Comment lier les événements' touchstart 'et' click ' mais ne pas répondre aux deux?

je travaille sur un site web mobile qui doit fonctionner sur une variété d'appareils. Celui qui me donne la migraine en ce moment, C'est BlackBerry.

nous devons prendre en charge les clics de clavier ainsi que les événements tactiles.

idéalement, Je n'utiliserais que:

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

mais le problème que nous rencontrons est que certains de ces appareils blackberry ont un délai très ennuyeux à partir du moment de la touche à elle déclenchant un clic.

le remède est plutôt d'utiliser touchstart:

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

mais comment lier les deux événements, mais en tirer un seul? J'ai toujours besoin de l'événement click pour les appareils à clavier, mais bien sûr, je ne veux pas que l'événement click se déclenche si j'utilise un appareil tactile.

une question bonus: y a-t-il de toute façon à faire cela et en plus accommoder les navigateurs qui n'ont même pas un événement touchstart? Dans cette recherche, il ressemble à BlackBerry OS5 ne supporte pas touchstart donc devra également compter sur click events pour ce navigateur.

ADDENDUM:

peut-être une question plus complète 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 fixations?

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

1) nous utilisons WURFL pour obtenir les informations de périphérique afin pourrait créer notre propre matrice de dispositifs. En fonction de l'appareil, nous utiliserons touchstart ou click.

2) Detect for touch support dans le navigateur via JS (j'ai besoin de faire plus de recherches à ce sujet, mais il semble que c'est faisable).

cependant, cela laisse encore un problème: qu'en est-il des dispositifs qui supportent les deux. Certains des téléphones que nous supportons (à savoir les Nokias et les Blackberry) ont à la fois des écrans tactiles et clavier. Cela me ramène à la question initiale ... y a-t-il un moyen de permettre les deux à la fois?

176
demandé sur DA. 2011-08-11 02:56:04

30 réponses

mise à jour : consultez le projet jQuery Pointer Events Polyfill qui vous permet de vous lier aux événements "pointer" au lieu de choisir entre la souris et le toucher.


lient aux deux, mais font un drapeau de sorte que la fonction ne démarre qu'une fois pour 100ms ou plus.

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

  return false
});
126
répondu Mottie 2015-05-01 20:20:16

C'est le correctif que je" crée " et il sort le GhostClick et implémente le FastClick. Essayez par vous-même et dites-nous 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

});
71
répondu Rafael Fragoso 2017-01-31 23:36:38

vous pourriez essayer quelque chose comme ceci:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
45
répondu swooter 2011-09-23 14:21:26

cela fonctionne généralement aussi bien:

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

});
41
répondu Jonathan 2013-06-08 16:53:25

le simple fait d'ajouter return false; à la fin de la fonction on("click touchstart") peut résoudre ce problème.

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

de la documentation de jQuery sur .on ()

Retour "à la 151930920" à partir d'un gestionnaire d'événements va automatiquement appeler event.stopPropagation() et event.preventDefault() . Une valeur false peut également être transmise pour le handler en raccourci pour function(){ return false; } .

14
répondu Roy 2017-11-12 20:57:17

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 élément <a> , donc j'ai dû supprimer la liaison click et redémarrer un événement click qui empêchait l'action par défaut pour l'élément <a> .

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

  //your code here
});
10
répondu Tim 2013-12-18 12:07:03

j'ai suivi la voie suivante.

Simple Comme Bonjour...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});
7
répondu Hasanavi 2015-05-21 14:30:37

généralement, vous ne voulez pas mélanger l'api par défaut touch et non-touch (click). Une fois que vous entrez dans le monde du toucher, il est plus facile de ne traiter que les fonctions liées au toucher. Ci-dessous, un pseudo code qui ferait ce que vous voulez.

si vous vous connectez dans l'événement touchmove et suivez les emplacements, vous pouvez ajouter plus d'éléments dans la fonction doTouchLogic pour détecter les gestes et autres.

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
     }
}
4
répondu Justin808 2017-02-08 15:17:54

je crois que la meilleure pratique consiste maintenant à utiliser:

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

touchend

l'événement touchend est déclenché lorsqu'un point de contact est enlevé de la surface de contact.

l'événement touchend déclenchera et non tous les événements de la souris.


mouseup

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

l'événement mouseup et non déclenchera des événements tactiles.

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)

à partir de 2017, les navigateurs commençant par Chrome et faisant des pas vers rendre l'événement de clic .on("click") plus compatible à la fois pour la souris et le toucher en éliminant le retard généré par les événements de robinet sur les demandes de clic.

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

Je n'ai pas encore fait de test de navigation croisée pour voir si c'est pratique.

4
répondu DreamTeK 2017-11-24 11:06:38

vérifier les boutons rapides et cliquez sur Google https://developers.google.com/mobile/articles/fast_buttons

3
répondu torbjon 2012-07-20 09:50:54

bien... Tout ça est super compliqué.

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

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

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});
3
répondu harelpls 2014-05-16 04:26:55

une autre implémentation pour une meilleure maintenance. Cependant, cette technique fera également événement.stopPropagation (). Le clic n'est saisi sur aucun autre élément qui a cliqué pour 100ms.

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
répondu mathieu hamel 2012-04-29 13:43:56

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

j'ai remplacé la fonction on de jQuery par une fonction modifiée qui, chaque fois que le navigateur prend en charge des événements tactiles, remplace tous mes événements cliquables 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);
        };
    }()),
});
"151930920 d'Utilisation" que serait exactement le même qu'avant, comme:

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

mais il serait utilisez touchstart lorsque disponible, cliquez lorsque Non. Aucun retard d'aucune sorte nécessaire: d

2
répondu Gerson Goulart 2013-04-25 17:50:57

je viens d'avoir l'idée de mémoriser si ontouchstart était déclenché. Dans ce cas, nous sommes sur un périphérique qui le supporte et nous voulons ignorer l'événement onclick . Puisque ontouchstart devrait toujours être déclenché avant "151970920 onclick , j'utilise ceci:

<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>
2
répondu jakob.j 2014-12-15 15:48:10

vous pourriez essayer comme ceci:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
2
répondu Sky Yip 2015-08-20 02:34:30

cela a fonctionné pour moi, mobile écoute les deux, donc éviter l'un, qui est l'événement touch. bureau d'écouter de 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
répondu Levarne Sobotker 2015-12-04 09:57:36

dans mon cas cela a fonctionné parfaitement:

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éclenché le clic et le toucher en même temps, si j'utilise le clic off, certaines fonctionnalités n'a pas fonctionné du tout sur les appareils mobiles. Le problème avec click est qu'il s'agit d'un événement global qui déclenche le reste de l'événement, y compris touchend.

1
répondu Pepita Ronderos 2017-10-02 14:27:10

je travaille aussi sur une application Web Android/iPad, et il semble que si seulement utiliser" touchmove "est suffisant pour" déplacer des composants " (Pas besoin de touchstart ). En désactivant touchstart, vous pouvez l'utiliser .cliquez sur(); jQuery. Il fonctionne en fait parce qu'il n'a pas été surchargé par touchstart.

enfin, vous pouvez binb .live("évènements touchstart", function(e) { e.stopPropagation(); }); poser les évènements touchstart événement pour arrêter la multiplication, de la salle de séjour et cliquez sur() pour obtenir déclenchée.

ça a marché pour moi.

0
répondu nembleton 2012-03-27 04:12:50

il y a beaucoup de choses à considérer quand on essaie de résoudre ce problème. La plupart des solutions soit briser le défilement ou ne pas gérer ghost click événements correctement.

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 clic fantôme sur une base par élément. Un clic retardé est déclenché par l'emplacement de l'écran, donc si vos événements de toucher modifient la page d'une certaine manière, le cliquez sur l'événement sera envoyé à la nouvelle version de la page.

0
répondu SystemParadox 2012-07-18 14:29:40

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

0
répondu Steven Lu 2012-11-16 23:50:00

profitant du fait qu'un clic va toujours suivre un événement tactile, voici ce que j'ai fait pour me débarrasser du" clic fantôme " sans avoir à utiliser des temps morts ou des 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
});

essentiellement chaque fois qu'un événement ontouchstart se déclenche sur l'élément, un drapeau un ensemble puis enlevé (et ignoré), lorsque le clic vient.

0
répondu BigMacAttack 2013-02-08 21:11:52

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, identifiable et assez souple 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
};
0
répondu mdbiscan 2013-02-23 14:46:05

si vous utilisez jQuery, ce qui suit a plutôt 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 reliais plusieurs événements dans une boucle et j'avais besoin de la fonction d'auto-appel pour créer une fermeture appropriée. En outre, Je ne voulais pas désactiver la reliure puisque je voulais qu'elle soit invocable sur next click/touchstart.

pourrait aider quelqu'un dans la même situation!

0
répondu Yusuf Bhabhrawala 2014-02-21 02:46:59

pour les fonctions simples, il suffit de reconnaître le toucher ou cliquez sur le bouton 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" );
});

renvoie" touch "si l'événement touchstart est défini et" no touch " si non. Comme je l'ai dit, il s'agit d'une approche simple pour les événements click/tap juste que.

0
répondu Rodrigo 2014-03-22 05:20:23

j'essaie ceci et jusqu'à présent cela fonctionne (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éengage les manipulateurs sur chaque touche, mais tout ce qui est considéré touche ne se produit pas très souvent (en temps informatique!)

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

0
répondu Dave Lowerre 2014-04-01 22:22:55

étant pour moi la meilleure réponse à celle donnée par Mottie, j'essaie juste de faire son code plus réutilisable, donc c'est 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
répondu Arco Voltaico 2014-08-05 07:08:05

essayez d'utiliser Virtual Mouse (vmouse) Bindings de jQuery Mobile . C'est un événement virtuel spécialement pour votre cas:

$thing.on('vclick', function(event){ ... });

http://api.jquerymobile.com/vclick /

liste de soutien du navigateur: http://jquerymobile.com/browser-support/1.4 /

0
répondu Sergey Denisov 2015-02-16 12:59:22

EDIT: Mon ancien réponse (basé sur les réponses dans ce fil) n'était pas la voie à suivre pour moi. Je voulais qu'un sous-menu s'étende sur le clic enter ou touch de la souris et s'effondre sur le départ de la souris ou un autre clic touch. Puisque les évènements de souris sont normalement lancés après les évènements touch, il était assez difficile d'écrire des écouteurs d'évènements qui supportent à la fois l'écran tactile et l'entrée de la souris.

plugin jQuery: Tactile Ou de la Souris

j'ai fini par écrire un plugin jQuery appelé " Touch Or Mouse " (897 bytes miniified) qui peut détecter si un événement a été invoqué par un écran tactile ou une souris (sans test de support tactile!). Cela permet à la fois le soutien d'écran tactile et de souris en même temps et complètement séparer leurs événements.

de cette façon, L'OP peut utiliser touchstart ou touchend pour réponse aux clics tactiles et click pour les clics invoqué seulement par une souris.

Démonstration

le premier doit faire ie. le corps de l'élément de suivre les événements tactiles:

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

établit par défaut notre lien avec les éléments et en appelant $body.touchOrMouse('get', e) nous pouvons savoir si l'événement a été invoqué par un écran tactile ou par 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 la plugin au travail à http://jsfiddle.net/lmeurs/uo4069nh .

explication

  1. ce plugin doit être appelé sur ie. l'élément de corps pour suivre les événements touchstart et touchend , de cette façon l'événement touchend ne doit pas être tiré sur l'élément déclencheur (c.-à-d. un lien ou un bouton). Entre ces deux évènements, Ce plugin considère tout évènement de la souris comme étant invoqué par touch.
  2. les évènements de la souris sont lancés seulement après touchend , quand un évènement de la souris est lancé dans le ghostEventDelay (option , 1000ms par défaut) après touchend , ce plugin considère que l'évènement de la souris est invoqué par touch.
  3. en cliquant sur un élément à l'aide d'un écran tactile, l'élément obtient l'état :active . L'événement mouseleave n'est déclenché qu'après que l'élément a perdu cet état par ie. cliquer sur un autre élément. Comme cela pourrait être des secondes (ou minutes!) après le lancement de l'événement mouseenter , ce plugin conserve la trace du dernier événement mouseenter d'un élément: si le dernier événement mouseenter a été invoqué par touch, l'événement mouseleave suivant est également considéré comme invoqué par touch.
0
répondu lmeurs 2015-03-24 09:10:59

Voici une façon 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 sauvegardez essentiellement le premier type d'événement qui est déclenché à la propriété 'trigger' dans l'objet de données de jQuery qui est attaché au document racine, et n'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 handler 'click' ne sera pas exécuté parce que 'click' ne correspond pas au type d'événement initial enregistré. "déclencheur" ("évènements touchstart").

l'hypothèse, et je crois que c'est une hypothèse 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 parce que le type d'événement "trigger" n'est enregistré qu'une fois par chargement de page et "click"ne correspondrait jamais à "touchstart".

voici un codepen vous pouvez jouer avec (Essayez de taper sur le bouton sur un appareil tactile -- il ne devrait pas y avoir de délai de clic): http://codepen.io/thdoan/pen/xVVrOZ

j'ai aussi implémenté ceci comme un simple plugin jQuery qui supporte aussi 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/

0
répondu thdoan 2016-03-18 05:35:39

La meilleure méthode que j'ai trouvé est d'écrire le toucher de l'événement et l'événement d'appeler la normale, cliquez sur l'événement en programmant. De cette façon, vous avez tous vos événements normaux de clic et ensuite vous avez besoin d'ajouter juste un gestionnaire d'événement pour tous les événements de contact. Pour chaque noeud que vous voulez rendre touchable, ajoutez simplement la classe "touchable" à celle-ci pour invoquer le gestionnaire de touches. Avec Jquery, cela fonctionne ainsi avec une certaine logique pour s'assurer que c'est un vrai é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
});
0
répondu mahatrsna 2016-12-06 04:38:51