web-dev-qa-db-fra.com

JavaScript touchend vs clic dilemme

Je travaille sur une interface utilisateur javascript et j'utilise beaucoup d'événements tactiles tels que 'touchend' pour améliorer la réponse sur les appareils tactiles. Cependant, il y a quelques problèmes logiques qui me dérangent ...

J'ai vu beaucoup de développeurs se mêler "touchend" et "cliquer" dans le même événement. Dans de nombreux cas, cela ne fera pas mal, mais essentiellement, la fonction se déclenchera deux fois sur des appareils tactiles:

button.on('click touchend', function(event) {
  // this fires twice on touch devices
});

Il a été suggéré de détecter la fonctionnalité tactile et de définir l'événement de manière appropriée, par exemple:

var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click';
button.on(myEvent, function(event) {
  // this fires only once regardless of device
});

Le problème avec ce qui précède, c'est que cela se brisera sur les périphériques prenant en charge le toucher et la souris. Si l'utilisateur utilise actuellement la souris sur un périphérique à double entrée, le «clic» ne se déclenchera pas car seul le bouton «touchend» est attribué au bouton.

Une autre solution consiste à détecter le périphérique (par exemple, "iOS") et à affecter un événement en fonction de cet événement: Cliquez sur l'événement appelé deux fois sur le terminal dans l'iPad . seulement pour iOS (pas Android ou autres appareils), et semble plus comme un "bidouillage" pour résoudre quelque chose d'assez élémentaire.

Une autre solution consisterait à détecter les mouvements de la souris et à les associer à la fonctionnalité tactile pour déterminer si l’utilisateur est au toucher ou avec la souris. Le problème est bien sûr que l'utilisateur peut ne pas déplacer la souris lorsque vous voulez le détecter ...

La solution la plus fiable à laquelle je puisse penser consiste à utiliser une simple fonction debounce afin de s’assurer simplement que la fonction ne se déclenche qu’une fois dans un intervalle court (par exemple 100 ms):

button.on('click touchend', $.debounce(100, function(event) {
  // this fires only once on all devices
}));

Est-ce que je manque quelque chose ou est-ce que quelqu'un a de meilleures suggestions?

Edit: J'ai trouvé ce lien après mon article, qui suggère une solution similaire à celle ci-dessus: Comment lier les événements 'touchstart' et 'click' sans répondre aux deux?

40
suncat100

Après une journée de recherche, je me suis dit que la meilleure solution consistait simplement à rester cliquez sur et à utiliser https://github.com/ftlabs/fastclick pour supprimer le délai au toucher. Je ne suis pas sûr à 100% que cela soit aussi efficace que touchend, mais pas loin du moins.

J'ai trouvé un moyen de désactiver deux fois le déclenchement d'événements tactiles en utilisant stopPropagation et preventDefault, mais c'est douteux car cela pourrait interférer avec d'autres gestes tactiles en fonction de l'élément où il est appliqué:

button.on('touchend click', function(event) {
  event.stopPropagation();
  event.preventDefault();
  // this fires once on all devices
});

En fait, je cherchais une solution pour combiner touchstart sur certains éléments de l'interface utilisateur, mais je ne vois pas comment cela pourrait être combiné avec click autre que la solution ci-dessus.

29
suncat100

Cette question a reçu une réponse mais doit peut-être être mise à jour.

Selon un avis de Google , il n'y aura plus de retard de 300 à 350 ms si nous incluons la ligne ci-dessous dans l'élément <head>.

<meta name="viewport" content="width=device-width">

C'est tout! Et il n'y aura plus de différence entre les événements click et touch!

5
user3347402

Oui, la meilleure option est généralement de désactiver le zoom double-pression (et par conséquent le délai de clic). Et nous avons enfin un bon conseil pour faire cela qui va { bientôt fonctionner sur tous les navigateurs }.

Si, pour une raison quelconque, vous ne voulez pas faire cela. Vous pouvez également utiliser UIEvent.sourceCapabilities.firesTouchEvents pour ignorer explicitement la variable click redondante. Le polyfill for this fait quelque chose de similaire à votre code anti-rebond.

0
Rick Byers

Votre fonction debounce retardera le traitement de chaque clic pendant 100 ms:

button.on('click touchend', $.debounce(100, function(event) {
  // this is delayed a minimum of 100 ms
}));

Au lieu de cela, j'ai créé une fonction cancelDuplicates qui se déclenche immédiatement, mais tous les appels ultérieurs dans les 10 ms seront annulés:

function cancelDuplicates(fn, threshhold, scope) {
    if (typeof threshhold !== 'number') threshhold = 10;
    var last = 0;

    return function () {
        var now = +new Date;

        if (now >= last + threshhold) {
            last = now;
            fn.apply(scope || this, arguments);
        }
    };
}

Usage:

button.on('click touchend', cancelDuplicates(function(event) {
  // This fires right away, and calls within 10 ms after are cancelled.
}));
0
Web_Designer

Bonjour, vous pouvez implémenter la manière suivante.

function eventHandler(event, selector) {
    event.stopPropagation(); // Stop event bubbling.
    event.preventDefault(); // Prevent default behaviour
    if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks.
}

// Implement
$('.class').on('touchend click', function(event) {
    eventHandler(event, $(this)); // Handle the event.
    // Do somethings...
});
0
David