Comment lier les événements 'touchstart' et 'click' sans répondre aux deux?


198

Je travaille sur un site Web mobile qui doit fonctionner sur une variété d'appareils. Celui qui me fait mal à la tête en ce moment est BlackBerry.

Nous devons prendre en charge les clics sur le clavier ainsi que les événements tactiles.

Idéalement, j'utiliserais simplement:

$thing.click(function(){...})

mais le problème que nous rencontrons est que certains de ces appareils BlackBerry ont un délai très gênant entre le moment où le toucher et le déclenchement d'un clic.

Le remède consiste à utiliser plutôt le démarrage tactile:

$thing.bind('touchstart', function(event){...})

Mais comment puis-je lier les deux événements, mais n'en déclencher qu'un? J'ai toujours besoin de l'événement click pour les périphériques clavier, mais bien sûr, je ne veux pas que l'événement click se déclenche si j'utilise un périphérique tactile.

Une question bonus: existe-t-il de toute façon de le faire et d'accueillir en outre des navigateurs qui n'ont même pas d'événement tactile? En recherchant cela, il semble que BlackBerry OS5 ne prend pas en charge le démarrage tactile, il devra donc également s'appuyer sur les événements de clic pour ce navigateur.

ADDENDA:

Peut-être une question plus globale est:

Avec jQuery, est-il possible / recommandé de gérer à la fois les interactions tactiles et les interactions avec la souris avec les mêmes liaisons?

Idéalement, la réponse est oui. Sinon, j'ai quelques options:

1) Nous utilisons WURFL pour obtenir des informations sur les appareils afin de créer notre propre matrice d'appareils. Selon l'appareil, nous utiliserons le démarrage tactile OU cliquez sur.

2) Détecter la prise en charge tactile dans le navigateur via JS (j'ai besoin de faire plus de recherches à ce sujet, mais il semble que cela soit faisable).

Cependant, cela laisse toujours un problème: qu'en est-il des appareils qui prennent en charge les deux. Certains des téléphones que nous prenons en charge (à savoir les Nokias et les Blackberry) ont à la fois des écrans tactiles et des claviers. Donc, ce genre de chose me ramène à la question d'origine ... existe-t-il un moyen de permettre les deux à la fois d'une manière ou d'une autre?


2
Il vaut mieux se lier au démarrage et au toucher et écrire votre propre logique de clic en même temps que votre logique tactile. Le rappel de clic intégré comme aucune connaissance des touches.
Justin808

Je ne suis pas sûr de suivre, Justin. Ne serais-je pas toujours lié à un événement de démarrage et de clic?
DA.

@DA - non, vous ne vous lieriez pas du tout au rappel .click (). Je vais essayer d'écrire une réponse dans du code sudo. Je n'ai pas d'appareil tactile à portée de main pour écrire du vrai code :)
Justin808

Ah, mais pour clarifier, j'ai toujours besoin d'événements de clic, car il y aura des gens qui accèderont à ce site avec des appareils non tactiles.
DA.

2
Utiliser .bind('touchstart mouseup')le résoudra (basé sur l'un des commentaires ci-dessous)
oriadam

Réponses:


135

Mise à jour : Découvrez le projet jQuery Pointer Events Polyfill qui vous permet de vous lier à des événements "pointeur" au lieu de choisir entre la souris et le toucher.


Liez aux deux, mais créez un indicateur pour que la fonction ne se déclenche qu'une fois toutes les 100 ms environ.

var flag = false;
$thing.bind('touchstart click', function(){
  if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
    // do something
  }

  return false
});

7
hmm ... ça sent hacky, mais ça pourrait marcher. Le hic, c'est que sur certains de ces appareils, c'est un décalage notable ... peut-être presque une seconde ... ce qui serait gênant pour d'autres sur un appareil plus rapide.
DA.

15
Au lieu d'utiliser le clickchanger en mousedown... peut-être le long délai est la différence entre mousedownet mouseupqui est la façon dont a clickest déterminé.
Mottie

7
C'est la solution que j'ai choisie. Merci! Je signale un élément touchenden appliquant une classe de touched. Cela se déclenche avant l'appel à l'événement click. J'ajoute ensuite un événement click qui vérifie d'abord l'existence de cette classe. Si c'est le cas, nous supposons que les événements tactiles ont été déclenchés et nous ne faisons rien d'autre avec un clic que de supprimer le nom de la classe pour le «réinitialiser» pour la prochaine interaction. Si la classe n'est pas là, nous supposons qu'ils ont utilisé un clavier pour cliquer sur l'élément et déclencher la fonction à partir de là.
DA.

7
Vous devez savoir que la plupart des navigateurs téléphoniques ont un retard de 300 ms sur l'événement de clic, vous devez donc augmenter le délai à au moins 300, voir cet article sur le problème du retard de 300 ms . Il s'agit essentiellement d'activer la fonctionnalité de "double-clic pour zoomer" qui était une fonctionnalité très importante au début de l'iPhone, lorsque la plupart des sites Web étaient conçus pour être affichés sur un grand écran de bureau.
admiration

12
Cette question a été vue près de 100 000 fois à ce stade. Cela semble être un cas / problème d'utilisation extrêmement courant. Cependant, cette réponse et d'autres sur cette question et des questions similaires semblent toutes hackeuses. La solution Google, bien que plus approfondie que la plupart, ne semble être qu'un hack plus robuste. Pour quelque chose d'aussi simple qu'un gestionnaire de clics, il semble que la solution devrait être simple. Personne n'a trouvé de solution non piratée pour ce problème?
jinglesthula

73

C'est le correctif que je "crée" et il supprime le GhostClick et implémente le FastClick. Essayez par vous-même et faites-nous savoir si cela a fonctionné pour vous.

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic here

});

2
helgatheviking: jsbin.com/ijizat/25 Dans le JavaScript, il y a une fonction appelée TouchClick qui intègre ce qui précède.
Matt Parkins

5
Je préfère FORTEMENT cette méthode de toutes les réponses à cette question car c'est a) ne teste pas les capacités de l'appareil et b) n'utilise pas setTimeout. D'après mon expérience, les solutions à des problèmes comme ceux-ci qui utilisent setTimeout peuvent fonctionner dans la plupart des cas, mais la synchronisation des événements est assez arbitraire et spécifique au périphérique.
itsmequinn

7
Cela ne fonctionnera pas car en passant «événement» dans la fonction anonyme, il devient un objet local dans cette fonction, il event.handledsera donc égal à undefinedchaque fois que l'événement est déclenché. Voici une solution: définissez l'indicateur «géré» dans l'élément cible lui-même à l'aide de jQuery.data().
thdoan

3
Vous avez event.stopPropagation();et d' event.preventDefault();après ma compréhension (et mes tests), l'événement ne peut être déclenché qu'une seule fois avec cela. alors pourquoi même vérifier if(event.handled !== true)? Pour moi, ce n'est pas nécessaire ou je manque quelque chose?
nbar

2
Ne devrait pas event.handledêtre vérifié pour la valeur true? Pour autant que je sache, ce n'est jamais false. Il commence par undefined, puis vous voulez savoir s'il a été défini sur true.
ACJ

49

Vous pouvez essayer quelque chose comme ceci:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);

5
Je pense que le problème est qu'il teste les capacités de l'appareil plutôt que ce que l'utilisateur fait, non? Le défi est que nous devons prendre en charge les appareils tactiles qui ont également des claviers (afin qu'ils puissent utiliser les deux)
DA.

8
Ce n'est pas une bonne idée - interrompra l'événement pour un utilisateur avec un écran tactile et une souris lors de l'utilisation de la souris (les ordinateurs portables avec écrans tactiles deviennent assez courants).
Kristian J.24

1
Excellent article à ce sujet: hacks.mozilla.org/2013/04/…
Luke Melia

Windows 8 reliera toujours l'événement 'touchstart' s'il est utilisé car 'ontouchstart' renverra 'true' pour ce système d'exploitation. Donc, ce n'est probablement pas la meilleure réponse à moins que votre code ne s'exécute que sur de vrais appareils à écran tactile, mais si c'est le cas, vous n'avez pas besoin du tout de vérifier.
Neil Monroe

Sur les ordinateurs portables avec écran tactile, l'utilisateur ne pourra rien cliquer en utilisant ce code en chrome (au moins chrome 46). IE 11 semble cependant fonctionner.
David Sherret

42

Habituellement, cela fonctionne aussi:

$('#buttonId').on('touchstart click', function(e){
    e.stopPropagation(); e.preventDefault();
    //your code here

});

8
@Jonathan mal, si vous utilisez des versions plus récentes de jQuery. Live a été déconseillé dans la version 1.7.
Mike Barwick

J'ai également essayé (et je l'ai lu ailleurs - pas mon idée) avec e.stopPropagation (); e.preventDefault (); et ça marche (pour moi) presque mais j'ai découvert plus tard qu'en fait ça marche comme prévu 20 fois mais 1 fois pas, donc ce n'est pas à l'épreuve des balles. Regardez ici: stackoverflow.com/questions/25671053/… Appréciez vos commentaires à ce sujet!
Garavani

@MikeBarwick Pourriez-vous s'il vous plaît expliquer votre commentaire ou donner un lien qui l'explique? Merci d'avance.
Billal Begueradj

22

Un simple ajout return false;à la fin de la on("click touchstart")fonction d'événement peut résoudre ce problème.

$(this).on("click touchstart", function() {
  // Do things
  return false;
});

De la documentation jQuery sur .on ()

Le retour falsed'un gestionnaire d'événements appellera automatiquement event.stopPropagation()et event.preventDefault(). Une falsevaleur peut également être transmise pour le gestionnaire en tant que raccourci pour function(){ return false; }.


2
fonctionne très bien, ne se déclenche qu'une seule fois sur les appareils avec les deux - cela me semble être la solution la moins propre et la plus hacky
Adam Cooper

A fonctionné le mieux pour moi et a vraiment fait sens pourquoi.
Amir5000

Des inconvénients à cela? Pourquoi une solution aussi simple n'est-elle pas largement connue?
ESR

10

J'ai dû faire quelque chose de similaire. Voici une version simplifiée de ce qui a fonctionné pour moi. Si un événement tactile est détecté, supprimez la liaison de clic.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

Dans mon cas, l'événement click était lié à un <a>élément, j'ai donc dû supprimer la liaison click et relier un événement click qui a empêché l'action par défaut de l' <a>élément.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});

Je souhaite que cela fonctionne tel quel, mais ce n'est pas le cas. $ (this) ne montre pas ce que vous pensez qu'il fait.
Dave Lowerre

8

J'ai réussi par la voie suivante.

Peasy facile...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});

4
cela ne se déclenchera-t-il pas deux fois sur des appareils qui enregistrent à la fois un clic et un démarrage tactile?
DA.

8
Désolé, je vous ai fait savoir. Oui, vous avez raison, une touche génère également un événement de démarrage tactile et un événement de clic. Cela s'appelle un clic fantôme. Google a mis en place une solution pour cela. Pourrait pas simple, mais fonctionne parfaitement. Voici le lien. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi

2
peut être trivial mais vous manquez le eparamètre dansfunction()
ProblemsOfSumit

@Hasanavi c'était une excellente solution mais j'ai trouvé de meilleurs résultats en utilisant .on
Neil

1
Merci @Neil. Oui, l'utilisation de .on est une meilleure idée car elle pourrait être supprimée dans la future version. J'ai changé mon exemple de bind en on.
Hasanavi

5

Je crois que la meilleure pratique consiste maintenant à utiliser:

$('#object').on('touchend mouseup', function () { });

toucher

L'événement tactile est déclenché lorsqu'un point tactile est retiré de la surface tactile.

L'événement touchend ne déclenchera aucun événement de souris.


souris

L'événement mouseup est envoyé à un élément lorsque le pointeur de la souris se trouve sur l'élément et que le bouton de la souris est relâché. Tout élément HTML peut recevoir cet événement.

L'événement mouseup ne déclenchera aucun événement tactile.

EXEMPLE

$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>


EDIT (2017)

Depuis 2017, les navigateurs commençant par Chrome et effectuant des étapes vers la création de l'événement de clic .on("click") plus compatible pour la souris et le toucher en éliminant le retard généré par les événements tap sur les demandes de clic.

Cela conduit à la conclusion que le retour à l'utilisation de l'événement click serait la solution la plus simple pour aller de l'avant.

Je n'ai pas encore fait de tests croisés pour voir si cela est pratique.


Cela m'a semblé prometteur, donc je me suis amusé avec ça, mais finalement j'ai trouvé mouseupdes résultats imprévisibles, surtout lorsque j'utilise un trackpad au lieu d'une souris traditionnelle.
skybondsor

4

Généralement, vous ne voulez pas mélanger les API tactile et non tactile (clic) par défaut. Une fois que vous entrez dans le monde du toucher, il est plus facile de traiter uniquement les fonctions liées au toucher. Voici un pseudo-code qui ferait ce que vous voulez.

Si vous vous connectez à l'événement touchmove et suivez les emplacements, vous pouvez ajouter d'autres éléments dans la fonction doTouchLogic pour détecter les gestes et ainsi de suite.

var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;

$thing.bind('touchstart'), function() {
     var d = new Date();
     touchStartTime = d.getTime();
     touchStartLocation = mouse.location(x,y);
});

$thing.bind('touchend'), function() {
     var d = new Date();
     touchEndTime= d.getTime();
     touchEndLocation= mouse.location(x,y);
     doTouchLogic();
});

function doTouchLogic() {
     var distance = touchEndLocation - touchStartLocation;
     var duration = touchEndTime - touchStartTime;

     if (duration <= 100ms && distance <= 10px) {
          // Person tapped their finger (do click/tap stuff here)
     }
     if (duration > 100ms && distance <= 10px) {
          // Person pressed their finger (not a quick tap)
     }
     if (duration <= 100ms && distance > 10px) {
          // Person flicked their finger
     }
     if (duration > 100ms && distance > 10px) {
          // Person dragged their finger
     }
}

Je suppose que c'est le nœud de la question: est-il même logique d'essayer de prendre en charge les deux modèles avec une seule base de code. Je mettrai à jour ma question avec quelques scénarios supplémentaires.
DA.

1
"En général, vous ne voulez pas mélanger le toucher par défaut et le non-toucher" après avoir traversé cela, je suis d'accord. Le problème est qu'ils continuent de fabriquer des appareils tactiles avec des claviers, ce qui est un casse-tête pour nous, développeurs.
DA.

Je suis d'accord qu'il serait beaucoup plus facile de faire au début la décision finale de toucher ou de ne pas toucher. Quel monde facile ce serait! Mais qu'est-ce que c'est que ces fichus appareils qui cliquent et touchent? Vaut-il la peine d'avoir tous ces maux de tête à cause d'eux? Est-ce l'avenir que je me demande?
Garavani

Je n'aime pas l'idée d'avoir plusieurs définitions de fonctions. Mon entreprise travaille actuellement sur un projet où un iPad n'est pas spécialement géré comme un appareil mobile et vous avez toujours besoin de l'événement tactile. Mais touchend ne fonctionne pas sur les versions de bureau normales. La solution de @mottie est donc parfaitement adaptée à ce scénario.
Pete


3

Eh bien ... Tout cela est super compliqué.

Si vous avez modernizr, c'est une évidence.

ev = Modernizr.touch ? 'touchstart' : 'click';

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});

2
Pouvez-vous expliquer ce que cela fait? Je crois qu'il vérifie si l'appareil prend en charge le toucher et sinon, utilise le clic. Le hic (ou du moins l' était quand j'ai écrit la question) était que certains appareils prennent en charge les deux. Et sur ces appareils, je dois toujours gérer les deux événements, car le déclencheur peut provenir du toucher ou du clic.
DA.

2
Je recommande un peu plus d'explications.
Aaron Hall

2

Une autre implémentation pour une meilleure maintenance. Cependant, cette technique fera également event.stopPropagation (). Le clic n'est détecté sur aucun autre élément ayant cliqué pendant 100 ms.

var clickObject = {
    flag: false,
    isAlreadyClicked: function () {
        var wasClicked = clickObject.flag;
        clickObject.flag = true;
        setTimeout(function () { clickObject.flag = false; }, 100);
        return wasClicked;
    }
};

$("#myButton").bind("click touchstart", function (event) {
   if (!clickObject.isAlreadyClicked()) {
      ...
   }
}

2

Juste à des fins de documentation, voici ce que j'ai fait pour le clic le plus rapide / le plus réactif sur le bureau / appuyez sur la solution mobile à laquelle je pourrais penser:

J'ai remplacé la onfonction de jQuery par une fonction modifiée qui, chaque fois que le navigateur prend en charge les événements tactiles, a remplacé tous mes événements de clic par TouchStart.

$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
    on: (function(){
        var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
        return function( types, selector, data, fn, one ) {
            if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
            return this._on( types, selector, data, fn);
        };
    }()),
});

L'utilisation serait la même qu'avant, comme:

$('#my-button').on('click', function(){ /* ... */ });

Mais il utiliserait le démarrage tactile lorsqu'il est disponible, cliquez dans le cas contraire. Aucun retard d'aucune sorte nécessaire: D


1
Le problème serait que les appareils prennent en charge les deux claviers tactiles ET où vous devez vous assurer que vous acceptez les deux interactions.
DA.

Bonne observation @DA. J'ai ajouté une coche pour remplacer uniquement l'événement de clic si vous n'utilisez aucun événement tactile en combinaison avec lui. Stil ne sera pas une solution pour chaque projet, mais je suis sûr que cela conviendrait à beaucoup.
Gerson Goulart

2

Je suis juste venu avec l'idée de mémoriser si ontouchstartjamais cela se déclenchait. Dans ce cas, nous sommes sur un appareil qui le prend en charge et voulons ignorer l' onclickévénement. Puisque ontouchstartdevrait toujours être déclenché avant onclick , j'utilise ceci:

<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>


1
Étant donné que les utilisateurs peuvent utiliser le toucher et la souris pendant la même visite de page, le problème avec cet extrait de code est qu'il enregistre l'indicateur touchAvailable une fois au lieu de par événement. Le plugin jQuery de stackoverflow.com/a/26145343/328272 fait la même chose que votre code, mais peut indiquer pour un événement de souris s'il a été déclenché par le toucher ou la souris sur la base d'un événement. Non seulement ie. au clic, mais aussi sur la souris qui peut être déclenchée quelques secondes (ou minutes) après l'entrée de la souris (idéal pour les menus déroulants qui devraient s'étendre au survol de la souris avec des gestes de la souris ou cliquer lors de l'utilisation du toucher).
lmeurs

2

Vous pouvez essayer comme ceci:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);

Qu'en est-il des appareils prenant en charge le toucher et la souris
Walle Cyril

2

Dans mon cas, cela a parfaitement fonctionné:

jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
    jQuery(this).off('mouseup');
}
});

Le problème principal était quand à la place mouseup j'ai essayé avec le clic, sur les appareils tactiles, déclenchait le clic et le toucher en même temps, si j'utilise le clic, certaines fonctionnalités ne fonctionnaient pas du tout sur les appareils mobiles. Le problème avec le clic est qu'il s'agit d'un événement global qui déclenche le reste de l'événement, y compris le toucher.


1

Cela a fonctionné pour moi, le mobile écoute les deux, alors évitez celui qui est l'événement tactile. le bureau n'écoute que la souris.

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });

1

Étant pour moi la meilleure réponse que celle donnée par Mottie, j'essaie juste de rendre son code plus réutilisable, c'est donc ma contribution:

bindBtn ("#loginbutton",loginAction);

function bindBtn(element,action){

var flag = false;
$(element).bind('touchstart click', function(e) {
    e.preventDefault();
    if (!flag) {
        flag = true;
        setTimeout(function() {
            flag = false;
        }, 100);
        // do something
        action();
    }
    return false;
});

0

Je travaille également sur une application web Android / iPad, et il semble que si seulement "touchmove" suffit pour "déplacer des composants" (pas besoin de toucher au démarrage). En désactivant le démarrage tactile, vous pouvez utiliser .click (); de jQuery. Cela fonctionne réellement car il n'a pas été surchargé par le démarrage tactile.

Enfin, vous pouvez binb .live ("touchstart", function (e) {e.stopPropagation ();}); pour demander à l'événement Touchstart d'arrêter de se propager, le salon clique sur () pour se déclencher.

Ça a marché pour moi.


Comment désactiveriez-vous touchstart?
punkrockbuddyholly

Je crois que vous "pourriez" faire quelque chose comme: jQuery ("# ​​my_element"). On ('touchstart', function (e) {e.preventDefault ()});
nembleton

0

Il y a beaucoup de choses à considérer lorsque vous essayez de résoudre ce problème. La plupart des solutions interrompent le défilement ou ne gèrent pas correctement les événements de clics fantômes.

Pour une solution complète, voir https://developers.google.com/mobile/articles/fast_buttons

NB: Vous ne pouvez pas gérer les événements de clics fantômes par élément. Un clic retardé est déclenché par l'emplacement de l'écran, donc si vos événements tactiles modifient la page d'une certaine manière, l'événement click sera envoyé à la nouvelle version de la page.


0

Il peut être efficace de l'attribuer aux événements 'touchstart mousedown'ou 'touchend mouseup'd'éviter les effets secondaires indésirables de l'utilisation click.


0

Profitant du fait qu'un clic suivra toujours un événement tactile, voici ce que j'ai fait pour me débarrasser du «clic fantôme» sans avoir à utiliser de timeouts ou de drapeaux globaux.

$('#buttonId').on('touchstart click', function(event){
    if ($(this).data("already")) {
        $(this).data("already", false);
        return false;
    } else if (event.type == "touchstart") {
        $(this).data("already", true);
    }
    //your code here
});

Fondamentalement, chaque fois qu'un ontouchstartévénement se déclenche sur l'élément, un indicateur un ensemble, puis supprimé (et ignoré), lorsque le clic arrive.


0

Pourquoi ne pas utiliser l'API jQuery Event?

http://learn.jquery.com/events/event-extensions/

J'ai utilisé cet événement simple avec succès. Il est propre, suffisamment identifiable et suffisamment flexible pour être amélioré.

var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";

jQuery.event.special.touchclick = {
  bindType: eventType,
  delegateType: eventType
};

1
Il est recommandé d'utiliser la détection des fonctionnalités sur le reniflage d'agent utilisateur.
jinglesthula

Le hic est des appareils qui prennent en charge le toucher et le clic.
DA.

0

Si vous utilisez jQuery, les éléments suivants ont très bien fonctionné pour moi:

var callback; // Initialize this to the function which needs to be called

$(target).on("click touchstart", selector, (function (func){
    var timer = 0;
    return function(e){
        if ($.now() - timer < 500) return false;
        timer = $.now();
        func(e);
    }
})(callback));

D'autres solutions sont également bonnes, mais je liais plusieurs événements dans une boucle et j'avais besoin de la fonction d'appel automatique pour créer une fermeture appropriée. De plus, je ne voulais pas désactiver la liaison car je voulais qu'elle puisse être invoquée au prochain clic / toucher.

Pourrait aider quelqu'un dans une situation similaire!


0

Pour des fonctionnalités simples, reconnaissez simplement le toucher ou cliquez sur J'utilise le code suivant:

var element = $("#element");

element.click(function(e)
{
  if(e.target.ontouchstart !== undefined)
  {
    console.log( "touch" );
    return;
  }
  console.log( "no touch" );
});

Cela renverra "toucher" si l'événement de démarrage tactile est défini et "pas de contact" sinon. Comme je l'ai dit, c'est une approche simple pour les événements click / tap.


0

J'essaye ceci et jusqu'ici ça marche (mais je ne suis que sur Android / Phonegap donc caveat emptor)

  function filterEvent( ob, ev ) {
      if (ev.type == "touchstart") {
          ob.off('click').on('click', function(e){ e.preventDefault(); });
      }
  }
  $('#keypad').on('touchstart click', '.number, .dot', function(event) {
      filterEvent( $('#keypad'), event );
      console.log( event.type );  // debugging only
           ... finish handling touch events...
  }

Je n'aime pas le fait que je réaffecte les gestionnaires à chaque contact, mais toutes les choses considérées ne se produisent pas très souvent (en temps d'ordinateur!)

J'ai une tonne de gestionnaires comme celui de '#keypad', donc avoir une fonction simple qui me permet de résoudre le problème sans trop de code est la raison pour laquelle je suis allé de cette façon.



0

ÉDITER: Mon ancienne réponse (basée sur les réponses dans ce fil) n'était pas la voie à suivre pour moi. Je voulais un sous-menu à développer lors de la saisie de la souris ou du clic tactile et de réduire le congé de la souris ou un autre clic tactile. Étant donné que les événements de souris sont normalement déclenchés après des événements tactiles, il était assez difficile d'écrire des écouteurs d'événements qui prennent en charge à la fois l'écran tactile et la souris.

Plugin jQuery: Touch Or Mouse

J'ai fini par écrire un plugin jQuery appelé " Touch Or Mouse " (897 octets minifiés) qui peut détecter si un événement a été invoqué par un écran tactile ou une souris (sans tester la prise en charge tactile!). Cela permet la prise en charge de l'écran tactile et de la souris en même temps et sépare complètement leurs événements.

De cette façon, l'OP peut utiliser touchstartou touchendpour répondre rapidement aux clics tactiles et clickpour les clics invoqués uniquement par une souris.

Manifestation

Il faut d'abord faire ie. les événements tactiles de piste de l'élément de corps:

$(document.body).touchOrMouse('init');

Les événements de souris sont liés aux éléments par défaut et en appelant, $body.touchOrMouse('get', e)nous pouvons savoir si l'événement a été invoqué par un écran tactile ou une souris.

$('.link').click(function(e) {
  var touchOrMouse = $(document.body).touchOrMouse('get', e);

  if (touchOrMouse === 'touch') {
    // Handle touch click.
  }
  else if (touchOrMouse === 'mouse') {
    // Handle mouse click.
  }
}

Voir le plugin à l'œuvre sur http://jsfiddle.net/lmeurs/uo4069nh .

Explication

  1. Ce plugin doit être appelé ie. l'élément de corps à suivre touchstartet les touchendévénements, de cette façon, l' touchendévénement n'a pas à être déclenché sur l'élément déclencheur (c'est-à-dire un lien ou un bouton). Entre ces deux événements tactiles, ce plugin considère que tout événement de souris est appelé par le toucher.
  2. Les événements de souris sont déclenchés uniquement après touchend, lorsqu'un événement de souris est déclenché dans ghostEventDelay(option, 1000 ms par défaut) après touchend, ce plugin considère que l'événement de souris est invoqué par le toucher.
  3. Lorsque vous cliquez sur un élément à l'aide d'un écran tactile, l'élément gagne l' :activeétat. L' mouseleaveévénement n'est déclenché que lorsque l'élément perd cet état par ie. en cliquant sur un autre élément. Étant donné que cela peut se produire quelques secondes (ou minutes!) Après le mouseenterdéclenchement de l' événement, ce plugin conserve la trace du dernier mouseenterévénement d'un élément : si le dernier mouseenterévénement a été appelé au toucher, l' mouseleaveévénement suivant est également considéré comme étant appelé au toucher.

0

Voici un moyen simple de le faire:

// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
  if (!$(document).data('trigger')) $(document).data('trigger', e.type);
  if (e.type===$(document).data('trigger')) {
    // Do your stuff here
  }
});

Vous enregistrez essentiellement le premier type d'événement qui est déclenché dans la propriété 'trigger' dans l'objet de données de jQuery qui est attaché au document racine, et vous ne l'exécutez que lorsque le type d'événement est égal à la valeur dans 'trigger'. Sur les appareils tactiles, la chaîne d'événements serait probablement «touchstart» suivie de «click»; cependant, le gestionnaire «click» ne sera pas exécuté car «click» ne correspond pas au type d'événement initial enregistré dans «trigger» («touchstart»).

L'hypothèse, et je pense qu'elle est sûre, est que votre smartphone ne passera pas spontanément d'un appareil tactile à un appareil de souris ou bien le robinet ne s'enregistrera jamais car le type d'événement `` déclencheur '' n'est enregistré qu'une fois par le chargement de la page et le «clic» ne correspondraient jamais au «démarrage tactile».

Voici un codepen avec lequel vous pouvez jouer (essayez d'appuyer sur le bouton d'un appareil tactile - il ne devrait pas y avoir de retard de clic): http://codepen.io/thdoan/pen/xVVrOZ

J'ai également implémenté cela comme un simple plugin jQuery qui prend également en charge le filtrage des descendants de jQuery en passant une chaîne de sélection:

// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
  var selector, handler;
  switch (typeof arg1) {
    case 'function':
      selector = null;
      handler = arg1;
      break;
    case 'string':
      selector = arg1;
      if (typeof arg2==='function') handler = arg2;
      else return;
      break;
    default:
      return;
  }
  this.on('click touchstart', selector, function(e) {
    if (!$(document).data('trigger')) $(document).data('trigger', e.type);
    if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
  });
};

Codepen: http://codepen.io/thdoan/pen/GZrBdo/


Pourquoi avez-vous utilisé une propriété de données sur le document au lieu d'utiliser simplement une variable?
Raphael Schweikert

@RaphaelSchweikert Probablement juste par habitude. Il n'y a pas de bonne ou de mauvaise façon, mais j'aime utiliser $.data()pour stocker des données accessibles par plusieurs fonctions mais qui n'appartiennent pas à la portée globale. Un avantage est que puisque je le stocke dans un élément, il me fournit naturellement plus de contexte.
thdoan

0

La meilleure méthode que j'ai trouvée consiste à écrire l'événement tactile et à faire en sorte que cet événement appelle l'événement de clic normal par programmation. De cette façon, vous avez tous vos événements de clic normaux, puis vous devez ajouter un seul gestionnaire d'événements pour tous les événements tactiles. Pour chaque nœud que vous souhaitez rendre touchable, ajoutez simplement la classe "touchable" pour invoquer le gestionnaire tactile. Avec Jquery, cela fonctionne comme ça avec une certaine logique pour s'assurer que c'est un véritable événement tactile et non un faux positif.

$("body").on("touchstart", ".touchable", function() { //make touchable  items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
    $(".touchable").on("touchend", function(event) {
        var d2 = new Date();
        var n2 = d2.getTime();
        if (n2 - n1 <= 300) {
            $(event.target).trigger("click"); //dont do the action here just call real click handler
        }
    });
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});
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.