Éliminez le délai de 300 ms sur les événements de clic dans Safari mobile


88

J'ai lu que Safari mobile a un délai de 300 ms sur les événements de clic entre le moment où le lien / bouton est cliqué et le moment où l'événement se déclenche. La raison du retard est d'attendre de voir si l'utilisateur a l'intention de double-cliquer, mais du point de vue UX, attendre 300 ms est souvent indésirable.

Une solution pour éliminer ce délai de 300 ms consiste à utiliser la gestion «tap» de jQuery Mobile. Malheureusement, je ne suis pas familier avec ce framework et je ne veux pas charger un gros framework si tout ce dont j'ai besoin est une ligne ou deux de code s'appliquant touchendde la bonne manière.

Comme de nombreux sites, mon site comporte de nombreux événements de clic comme celui-ci:

$("button.submitBtn").on('click', function (e) {   
  $.ajaxSubmit({... //ajax form submisssion
});

$("a.ajax").on('click', function (e) {   
  $.ajax({... //ajax page loading
});

$("button.modal").on('click', function (e) {   
      //show/hide modal dialog
});

et ce que j'aimerais faire, c'est supprimer le délai de 300 ms sur TOUS ces événements de clic en utilisant un seul extrait de code comme celui-ci:

$("a, button").on('tap', function (e) {
 $(this).trigger('click');
 e.preventDefault();
});

Est-ce une mauvaise / bonne idée?



@Pointy merci, cela pourrait bien fonctionner ...
Tim Peterson

"... évidemment ce n'est pas génial du point de vue UX." Je me méfierais de cette hypothèse.
Oliver Moran

1
@OliverMoran, merci pour la correction, je viens de modifier cette phrase, voir la question ci-dessus ..
Tim Peterson

1
pourrait être une solution: stackoverflow.com/a/12969739/1491212
Armel Larcier

Réponses:


72

Désormais, certains navigateurs mobiles éliminent un délai de clic de 300 ms si vous définissez la fenêtre. Vous n'avez plus besoin d'utiliser des solutions de contournement.

<meta name="viewport" content="width=device-width, user-scalable=no">

Ceci est actuellement pris en charge Chrome pour Android , Firefox pour Android et Safari pour iOS

Cependant, sur iOS Safari , le double-tap est un geste de défilement sur des pages non zoomables. Pour cette raison, ils ne peuvent pas supprimer le délai de 300 ms . S'ils ne peuvent pas supprimer le délai sur les pages non zoomables, il est peu probable qu'ils le suppriment sur les pages zoomables.

Les téléphones Windows conservent également le délai de 300 ms sur les pages non zoomables, mais ils n'ont pas de geste alternatif comme iOS, il leur est donc possible de supprimer ce délai comme Chrome l'a fait.Vous pouvez supprimer le délai sur Windows Phone en utilisant:

html {
-ms-touch-action: manipulation;
touch-action: manipulation;
}

Source: http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away

UPDATE 2015 Décembre

Jusqu'à présent, WebKit et Safari sur iOS avaient un délai de 350 ms avant que les simples tapotements activent des liens ou des boutons pour permettre aux gens de zoomer sur les pages avec un double tap. Chrome a déjà changé cela il y a quelques mois en utilisant un algorithme plus intelligent pour détecter cela et WebKit suivra une approche similaire . L'article donne de très bons aperçus sur la manière dont les navigateurs fonctionnent avec les gestes tactiles et sur la façon dont les navigateurs peuvent encore devenir beaucoup plus intelligents qu'ils ne le sont aujourd'hui.

MISE À JOUR 2016 Mars

Sur Safari pour iOS, le temps d'attente de 350 ms pour détecter un deuxième tap a été supprimé pour créer une réponse «rapide». Ceci est activé pour les pages qui déclarent une fenêtre avec width = device-width ou user-scalable = no. Les auteurs peuvent également opter pour un comportement de tapotement rapide sur des éléments spécifiques en utilisant le CSS touch-action: manipulationcomme documenté ici (faites défiler jusqu'à l'en-tête `` Styling Fast-Tap Behavior '') et ici .


En fait, il y a des événements tactiles pour WP: stackoverflow.com/questions/13396297/…
Cedric Reichenbach

1
Oui qui est contrôlé par -ms-touch-action comme je l'ai écrit dans mon post.
NoName Fourni le

1
Le problème existe toujours sur iOS lors de l'exécution de l'application à partir de la page d'accueil (autonome). Si quelqu'un a trouvé une solution de contournement décente pour cela, ce serait très bienvenu
Miguel Guardo

1
La définition des valeurs d'action tactile sur la manipulation est la plus proche de la performance souhaitée, mais comme @MiguelGuardo l'a indiqué précédemment, l'effet est perdu une fois que vous ajoutez la page Web à votre écran d'accueil dans iOS en tant qu'application autonome.
T. Steele

Peut-être que quelqu'un peut pirater UIWebView - stackoverflow.com/questions/55155560/…
Eazy

43

Ce plugin -FastClick développé par Financial Times le fait parfaitement pour vous!

Assurez-vous cependant d'ajouter event.stopPropagation();et / ou event.preventDefault();directement après la fonction de clic, sinon elle pourrait fonctionner deux fois comme elle l'a fait pour moi, c'est-à-dire:

$("#buttonId").on('click',function(event){
    event.stopPropagation(); event.preventDefault();
   //do your magic

});

3
Ce n'est pas parfait si vous utilisez jquery mobile (version 1.3.2 de toute façon), cela interrompt la fermeture du widget du panneau github.com/jquery/jquery-mobile/issues/6440
ryan0

cet event.stopPropagation () est exactement la raison pour laquelle je suis venu sur cette page en premier lieu. Fonctionne parfaitement, merci!
Lukas

@Jonathan fastclickfait sa magie uniquement sur les appareils tactiles qui ont un problème de retard (sinon, il est automatiquement contourné ). Cependant, le simple fait d'ajouter stopPropagation& preventDefaultaprès chaque événement de clic peut avoir un effet secondaire indésirable sur tous les gestionnaires d'événements de tous les navigateurs.
utilisateur du

17

Je sais que c'est vieux, mais ne pouvez-vous pas simplement tester pour voir si "tactile" est pris en charge dans le navigateur? Ensuite, créez une variable qui soit "touchend" ou "click" et utilisez cette variable comme événement lié à votre élément?

var clickOrTouch = (('ontouchend' in window)) ? 'touchend' : 'click';
$('#element').on(clickOrTouch, function() {
    // do something
});

Ainsi, cet exemple de code vérifie si l'événement "touchend" est pris en charge dans le navigateur et si ce n'est pas le cas, nous utilisons l'événement "click".

(Edit: changé "touchend" en "ontouchend")


Merci, j'aime la simplicité de cela. J'adorerais que les autres signalent ses éventuels pièges.
tim peterson

4
L'événement touchend ne peut pas remplacer l'événement de clic à cause de ce que @Andreas Köberle explique dans sa réponse. Comme par exemple si vous faites défiler et que votre doigt s'arrête sur un bouton, cela déclenchera le lien ...
adriendenat

C'est une solution légère et géniale sans avoir à inclure une grosse bibliothèque supplémentaire de choses juste pour se débarrasser du retard. J'aimerais pouvoir voter 5 fois sur celui-ci! (tant que vous n'êtes pas inquiet pour le scénario de défilement!)
Tonejac

2
Pour les ordinateurs / navigateurs avec prise en charge des événements tactiles et clics, c'est une mauvaise solution.
Alexander O'Mara

1
Certains appareils prennent en charge les événements de toucher et de clic, et vous voulez qu'ils se déclenchent tous les deux dans des situations différentes. Nous avons connu des situations où certaines fenêtres ou tablettes Android équipées d'une souris déclencheront évidemment un événement de clic lorsque vous cliquez sur une chose, mais elles déclencheront également des événements tactiles lorsque vous appuyez sur. Cela signifiait que vous deviez écouter les deux événements. Comme facteur de complication supplémentaire, certains appareils synthétiseraient un événement de clic lorsque vous appuyez sur, donc l'écoute des deux sur ces appareils pourrait entraîner le déclenchement de votre gestionnaire d'événements deux fois, à moins que vous ne soyez prudent.
1800 INFORMATION

12

Je suis tombé sur une alternative extrêmement populaire appelée Hammer.js (page Github) qui, à mon avis, est la meilleure approche.

Hammer.js est une bibliothèque tactile plus complète (avec de nombreuses commandes de balayage) que Fastclick.js (réponse la plus votée).

Attention cependant: le défilement rapide sur les appareils mobiles a tendance à vraiment verrouiller l'interface utilisateur lorsque vous utilisez Hammer.js ou Fastclick.js. C'est un problème majeur si votre site a un fil d'actualité ou une interface où les utilisateurs défileront beaucoup (cela ressemblerait à la plupart des applications Web). Pour cette raison, je n'utilise aucun de ces plugins pour le moment.


2
N'utilisez pas Hammerjs. Il présente de sérieux problèmes, par exemple: github.com/EightMedia/hammer.js/issues/388 github.com/EightMedia/hammer.js/issues/366 Au moins, ne l'utilisez pas avant d'être corrigé
Adonis K. Kakoulidis

2

D'une manière ou d'une autre, la désactivation du zoom semble désactiver ce petit délai. Cela a du sens, car un double-tap n'est plus nécessaire alors.

Comment puis-je "désactiver" le zoom sur une page Web mobile?

Mais soyez conscient de l'impact que cela aura sur la convivialité. Il peut être utile pour les pages Web conçues comme des applications, mais ne devrait pas être utilisé pour des pages «statiques» plus générales à mon humble avis. Je l'utilise pour un projet animalier qui nécessite une faible latence.


1

Malheureusement, il n'y a pas de moyen facile de le faire. Donc, simplement utiliser touchstartou touchendvous laissera avec d'autres problèmes comme quelqu'un commence à faire défiler lorsque vous cliquez sur un bouton par exemple. Nous utilisons zepto depuis un certain temps, et même avec ce très bon cadre, certains problèmes se sont posés au fil du temps. Beaucoup d'entre eux sont fermés , mais cela ne semble pas être un domaine de solution simple.

Nous avons cette solution pour gérer globalement les clics sur les liens:

   $(document.body).
    on('tap', 'a',function (e) {
      var href = this.getAttribute('href');
      if (e.defaultPrevented || !href) { return; }
      e.preventDefault();
      location.href= href;
    }).
    on('click', 'a', function (e) {
      e.preventDefault();
    });

- @ Andreas, merci donc vous recommanderiez de ne pas utiliser la tapsolution générale que j'ai postée et de factoriser les tapévénements élément par élément?
tim peterson le

Non, ce n'est pas le but. Le but n'est pas d'essayer de construire une tapsolution par vous-même. Je mettrai à jour ma réponse.
Andreas Köberle

- @ Andreas, merci pour votre réponse, de nombreux événements de clic sont AJAX donc l'événement par défaut est déjà empêché. Pourriez-vous mettre à jour votre réponse pour gérer cela également? Il semblerait que ma tapsolution générale soit la même que la vôtre dans ce cas?
tim peterson

1

J'ai cherché un moyen simple sans jquery et sans bibliothèque fastclick. Cela fonctionne pour moi:

var keyboard = document.getElementById("keyboard");
var buttons = keyboard.children;
var isTouch = ("ontouchstart" in window);
for (var i=0;i<buttons.length;i++) {
    if ( isTouch ) {
        buttons[i].addEventListener('touchstart', clickHandler, false);         
    } else {
        buttons[i].addEventListener('click', clickHandler, false);          
    }
}

0

Juste pour fournir quelques informations supplémentaires.

Sur iOS 10, les <button>s sur ma page ne pouvaient pas être déclenchés en continu. Il y avait toujours un décalage.

J'ai essayé fastclick / Hammer / tapjs / remplacer click par touchstart, tout a échoué.

MISE À JOUR: la raison semble être que le bouton est trop près du bord! déplacez-le près du centre et le décalage est parti!


0

Vous êtes censé déclarer explicitement le mode passif :

window.addEventListener('touchstart', (e) => {

    alert('fast touch');

}, { passive : true});

0

Dans jQuery, vous pouvez lier l'événement "touchend", le code de déclenchement de sorcière immédiatement après le tap (c'est comme un keydown dans le clavier). Testé sur les versions actuelles des tablettes Chrome et Firefox. N'oubliez pas «cliquez» également, pour vos ordinateurs portables et appareils de bureau à écran tactile.

jQuery('.yourElements').bind('click touchend',function(event){

    event.stopPropagation();
    event.preventDefault();

	// everything else
});

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.