web-dev-qa-db-fra.com

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

Je travaille sur un site Web mobile qui doit fonctionner sur divers appareils. Celui qui me donne mal à la tête en ce moment est BlackBerry.

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

Idéalement, je voudrais juste utiliser:

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

mais le problème que nous rencontrons est que certains de ces appareils BlackBerry ont un retard très gênant, du moment où ils touchent au toucher, ce qui déclenche un clic.

Le remède consiste plutôt à utiliser touchstart:

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

Mais comment faire pour lier les deux événements, mais en tirer un seul? 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 supplémentaire: est-il possible de le faire et d’adapter les navigateurs n’ayant même pas d’événement touchstart? En recherchant cela, il semble que BlackBerry OS5 ne prend pas en charge le touchstart, il faudra donc également compter sur les événements de clic pour ce navigateur.

ADDENDA:

Peut-être une question plus complète est:

Avec jQuery, est-il possible/recommandé de gérer les interactions tactiles et les interactions 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 périphériques et pouvons donc créer notre propre matrice de périphériques. En fonction du périphérique, nous utiliserons touchstart OR click.

2) Détectez la prise en charge tactile dans le navigateur via JS (je dois faire quelques recherches supplémentaires à ce sujet, mais il semble que cela soit faisable).

Cependant, il reste un problème: qu’en est-il des périphériques prenant en charge BOTH. Certains des téléphones que nous prenons en charge (notamment les Nokias et les Blackberry) possèdent les deux écrans tactiles et claviers. Cela me ramène donc à la question initiale: y a-t-il moyen de permettre les deux à la fois?

188
DA.

Update : Découvrez le projet jQuery Pointeur Polyfill qui vous permet de vous lier à des événements "pointeur" au lieu de choisir entre la souris. & toucher.


Liez-les à tous les deux, mais créez un drapeau pour que la fonction ne soit déclenchée 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
});
130
Mottie

C’est le correctif que je "crée". Il supprime GhostClick et implémente FastClick. Essayez vous-même et laissez-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

});
72
Rafael Fragoso

Vous pouvez essayer quelque chose comme ça:

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

Habituellement, cela fonctionne aussi bien:

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

});
42
Jonathan

Ajouter simplement return false; à la fin de la fonction événement on("click touchstart") peut résoudre ce problème.

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

De la documentation jQuery sur . On ()

Renvoyer false depuis un gestionnaire d'événements appellera automatiquement event.stopPropagation() et event.preventDefault(). Une valeur false peut également être transmise au gestionnaire en tant que raccourci pour function(){ return false; }.

18
Roy

Je devais 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 au 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>. Je devais donc supprimer la liaison au clic et relier un événement click empêchant l'action par défaut de 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
Tim

J'ai réussi de la manière suivante.

Peasy facile...

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

Généralement, vous ne voulez pas mélanger les api tactiles et non tactiles par défaut. Une fois que vous entrez dans le monde du toucher, il est plus facile de gérer uniquement les fonctions liées au toucher. Vous trouverez ci-dessous 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, etc.

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
Justin808

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 supprimé de la surface tactile.

L'événement touchend déclenchera pas tout événement de la souris.


mouseup

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

L'événement mouseup déclenchera pas tout é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)

À partir de 2017, les navigateurs commencent par Chrome et permettent de rendre l’événement de clic .on("click") plus compatible à la fois avec la souris et le toucher en éliminant le retard généré par les événements de tap sur les demandes de clic.

Cela nous amène à la conclusion que le retour à l'utilisation de l'événement click serait la solution la plus simple.

Je n'ai pas encore effectué de test sur plusieurs navigateurs pour voir si cela est pratique.

4
DreamTeK

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

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

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

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

vérifier les boutons rapides et les clics de chost depuis Google https://developers.google.com/mobile/articles/fast_buttons

3
torbjon

Une autre implémentation pour un meilleur entretien. Cependant, cette technique fera aussi event.stopPropagation (). Le clic n'est attrapé 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
mathieu hamel

J'ai juste eu l'idée de mémoriser si ontouchstart était déclenché. Dans ce cas, nous sommes sur un périphérique qui le prend en charge et souhaitons ignorer l'événement onclick. Puisque ontouchstart devrait toujours être déclenché avantonclick, j'utilise ceci:

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

Vous pourriez essayer comme ça:

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

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

J'ai remplacé la fonction on 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 touchstart lorsque disponible, cliquez sur sinon. Aucun retard d'aucune sorte nécessaire: D

2
Gerson Goulart

Cela a fonctionné pour moi, mobile écoute les deux, alors évitez celui qui est l'événement tactile. bureau uniquement écouter 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
Levarne Sobotker

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, au lieu de cela, j'ai essayé avec click, sur les appareils tactiles, en même temps, si je l'utilisais, certaines fonctionnalités ne fonctionnaient pas 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
Pepita Ronderos

Étant pour moi la meilleure réponse que celle donnée par Mottie, j'essaie simplement de rendre son code plus réutilisable, voici 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;
});
1
Arco Voltaico

J'essaie cela et jusqu'à présent, cela fonctionne (mais je ne suis que sur Android/Phonegap alors attention.)

  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 ressasse des liens sur tous les contacts, mais tout ce qui est considéré comme un toucher ne se produit pas très souvent (à l'heure des ordinateurs!)

J'ai une tonne de gestionnaires comme celui de '#keypad', alors avoir une fonction simple qui me permet de traiter le problème sans trop de code est la raison pour laquelle j'ai choisi cette méthode.

0
Dave Lowerre

Essayez d'utiliser Virtual Mouse (vmouse) Bindings à partir 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 support du navigateur: http://jquerymobile.com/browser-support/1.4/

0
Sergey Denisov

J'ai donné une réponse et je démontre avec un jsfiddle . Vous pouvez rechercher différents appareils et les signaler.

En gros, j'utilise une sorte de verrou d'événement avec certaines fonctions qui le servent:

/*
 * Event lock functions
 * ====================
 */
function getEventLock(evt, key){
   if(typeof(eventLock[key]) == 'undefined'){
      eventLock[key] = {};
      eventLock[key].primary = evt.type;
      return true;
   }
   if(evt.type == eventLock[key].primary)
      return true;
   else
      return false;
}

function primaryEventLock(evt, key){
   eventLock[key].primary = evt.type;
}

Ensuite, dans mes gestionnaires d'événements, je commence par une demande à ma serrure:

/*
 * Event handlers
 * ==============
 */
$("#add").on("touchstart mousedown", addStart);
$("#add").on("touchend mouseup", addEnd);
function addStart(evt){
   // race condition between 'mousedown' and 'touchstart'
   if(!getEventLock(evt, 'add'))
      return;

   // some logic
   now = new Date().getTime();
   press = -defaults.pressDelay;
   task();

   // enable event lock and(?) event repetition
   pids.add = setTimeout(closure, defaults.pressDelay);

   function closure(){
        // some logic(?): comment out to disable repetition
      task();

      // set primary input device
      primaryEventLock(evt, 'add');

      // enable event repetition
      pids.add = setTimeout(closure, defaults.pressDelay);
   }
}
function addEnd(evt){
      clearTimeout(pids.add);
}

Je dois souligner que le problème n'est pas de répondre simplement à un événement mais à PAS répondre sur les deux.

Enfin, à jsfiddle , il existe un lien vers une version mise à jour dans laquelle j'introduis un impact minimal sur le code existant en ajoutant simplement un simple appel à ma bibliothèque de verrouillage d'événements à la fois aux gestionnaires de début et de fin d'événement. avec 2 variables de portée eventLock et eventLockDelay.

0
centurian

La meilleure méthode que j'ai trouvée est d'écrire l'événement tactile et de le faire appeler l'événement de clic normal par programmation. De cette façon, vous avez tous vos événements de clic normaux et vous devez ensuite ajouter un seul gestionnaire d'événements pour tous les événements tactiles. Pour chaque nœud que vous souhaitez rendre touchable, ajoutez-y simplement la classe "touchable" pour appeler le gestionnaire tactile. Avec Jquery, cela fonctionne ainsi avec une certaine logique pour s’assurer qu’il s’agit d’un véritable événement tactile et non d’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
mahatrsna

EDIT: Ma réponse précédente (basée sur les réponses fournies dans ce fil) n'était pas la voie à suivre pour moi. Je voulais un sous-menu pour développer à la souris entrer ou toucher clic et réduire en laissant 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 prenant en charge simultanément les entrées à l'écran tactile et à la souris.

plugin jQuery: Touch Or Mouse

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

De cette manière, l'OP peut utiliser touchstart ou touchend pour répondre rapidement aux clics tactiles et click pour les clics invoqués uniquement par une souris.

Manifestation

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

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

Les événements de souris sont liés aux éléments de la manière par défaut et en appelant $body.touchOrMouse('get', e), nous pouvons déterminer si l'événement a été appelé 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 à http://jsfiddle.net/lmeurs/uo4069nh .

Explication

  1. Ce plugin doit être appelé c'est-à-dire. l'élément body à suivre touchstart et touchend, de cette manière, il n'est pas nécessaire que l'événement touchend soit déclenché sur l'élément déclencheur (c'est-à-dire un lien ou un bouton). Entre ces deux événements tactiles, ce plug-in considère que tout événement de souris doit être appelé par contact.
  2. Les événements de souris ne sont déclenchés qu'après touchend, lorsqu'un événement de souris est déclenché dans la ghostEventDelay (option, 1000 ms par défaut) après touchend, ce plug-in considère qu'il est invoqué par l'événement de souris. toucher.
  3. Lorsque vous cliquez sur un élément à l'aide d'un écran tactile, celui-ci passe à l'état :active. L'événement mouseleave est déclenché uniquement lorsque l'élément perd cet état, par exemple. en cliquant sur un autre élément. Comme il peut s’agir de secondes (ou de minutes!) Après le déclenchement de l’événement mouseenter, ce plug-in garde la trace du dernier événement mouseenter d’un élément: si le dernier événement mouseenter a été appelé par contact , l'événement mouseleave suivant est également considéré comme invoqué au toucher.
0
lmeurs

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
  }
});

En règle générale, vous enregistrez le premier type d'événement déclenché dans la propriété 'trigger' dans l'objet de données de jQuery associé 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" suivi de "clic"; toutefois, 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 que c’est une solution sûre, est que votre smartphone ne passera pas spontanément d’un appareil tactile à un appareil souris, sinon le tap ne s’enregistrera jamais, car le type d’événement "déclencheur" n’est enregistré qu’une seule fois. le chargement de page et le "clic" ne correspondraient jamais à "touchstart".

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

J'ai également implémenté cela comme un simple plugin jQuery qui supporte également 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);
  });
};

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

0
thdoan

trouvez la différence de déplacement du document (horizontal et vertical), touchez et touchez, si l’un d’entre eux est supérieur à 1 pixel, alors il faut déplacer plutôt que cliquer

var touchstartverscrollpos , touchstarthorscrollpos;


    $('body').on('touchstart','.thumbnail',function(e){

        touchstartverscrollpos = $(document).scrollTop();
        touchstarthorscrollpos = $(document).scrollLeft();


    });



    $('body').on('touchend','.thumbnail',function(e){


        var touchendverscrollpos = $(document).scrollTop();
        var touchendhorscrollpos = $(document).scrollLeft();

        var verdiff = touchendverscrollpos - touchstartverscrollpos;
        var hordiff = touchendhorscrollpos - touchstarthorscrollpos;


        if (Math.abs(verdiff) <1 && Math.abs(hordiff)<1){

// do you own function () here 



            e.stopImmediatePropagation();

            return false;
        }

    });
0
chings228

Cela n’a pas été mentionné ici, mais vous pouvez consulter ce lien: https://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the- same-element /

Pour récapituler pour la postérité, au lieu d'essayer d'affecter les deux gestionnaires, puis de trier le résultat, vous pouvez simplement vérifier si le périphérique est un écran tactile ou non et n'affecter que l'événement correspondant. Observer:

var clickEvent = (function() {
  if ('ontouchstart' in document.documentElement === true)
    return 'touchstart';
  else
    return 'click';
})();

// and assign thusly:

el.addEventListener( clickEvent, function( e ){ 
    // things and stuff
});

J'utilise ceci pour lier mes événements afin de pouvoir tester sur des écrans tactiles qui gèrent les événements touchstart et click qui se déclencheraient deux fois, ainsi que sur mon PC de développement qui n'entend que le click

L’auteur de ce lien mentionne cependant un problème: les ordinateurs portables à écran tactile conçus pour gérer les deux événements:

J'ai entendu parler d'un troisième appareil que je n'envisageais pas, l'ordinateur portable à écran tactile. C’est un appareil hybride qui prend en charge les événements tactiles et les événements clic. La liaison d'un événement signifie que seul cet événement sera pris en charge. Est-ce que cela signifie que quelqu'un avec un écran tactile et une souris devrait toucher explicitement parce que c'est le seul événement que je gère?

Les liaisons touchstart et click semblaient idéales pour gérer ces périphériques hybrides. Pour que l'événement ne se déclenche pas deux fois, j'ai ajouté e.stopPropagation() et e.preventDefault() aux fonctions de rappel. e.stopPropagation() empêche les événements de "se propager" chez leurs parents, mais empêche également le déclenchement d'un deuxième événement. J'ai inclus e.preventDefault() comme "juste au cas où", mais il semble que cela pourrait être omis.

0
Abraham Brookes

Je travaille également 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 utiliser .click (); de jQuery. Cela fonctionne réellement parce qu'il n'a pas été surchargé par touchstart.

Enfin, vous pouvez binb .live ("touchstart", fonction (e) {e.stopPropagation ();}); demander à l'événement touchstart de cesser de se propager, living room cliquer () pour se déclencher.

Cela a fonctionné pour moi.

0
nembleton

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 liés aux 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 clic fantôme par élément. Un clic retardé est déclenché en fonction de l'emplacement de l'écran. Par conséquent, si vos événements tactiles modifient la page d'une manière ou d'une autre, l'événement de clic sera envoyé à la nouvelle version de la page.

0
SystemParadox

Il peut s'avérer utile d'affecter les événements 'touchstart mousedown' ou 'touchend mouseup' pour éviter les effets secondaires indésirables de click.

0
Steven Lu

Pourquoi ne pas utiliser l'API d'événement jQuery?

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

J'ai utilisé cet événement simple avec succès. C'est propre, nom 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
};
0
mdbiscan

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 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 soit invoquable au prochain clic/touchstart.

Pourrait aider quelqu'un dans une situation similaire!

0
Yusuf Bhabhrawala

Pour les fonctionnalités simples, il suffit de reconnaître tactile 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" );
});

Ceci retournera "touch" si l'événement touchstart est défini et "no touch" sinon. Comme je l’ai dit, c’est une approche simple pour les événements click/tap.

0
Rodrigo

UDPATE:

Je travaille sur une implémentation pour utiliser à la fois les événements click et touchend pour la même fonction, et la fonction bloque efficacement les événements si le type change. Mon objectif était de disposer d’une interface d’application plus réactive. Je souhaitais réduire le temps nécessaire au début de l’événement pour la boucle de rétroaction de l’UI.

Pour que cette implémentation fonctionne, il est supposé que tous vos événements associés sont ajoutés sur 'click' et 'touchend'. Cela empêche qu'un élément soit privé de la propagation d'événements si les deux événements doivent être exécutés, mais sont de types différents.

Voici une implémentation basée sur une API légère que j'ai simplifiée à des fins de démonstration. Il montre comment utiliser la fonctionnalité sur un élément collapse.

var tv = {
    /**
     * @method eventValidator()
     * @desc responsible for validating event of the same type.
     * @param {Object} e - event object
     * @param {Object} element - element event cache
     * @param {Function} callback - callback to invoke for events of the same type Origin
     * @param {Object} [context] - context to pass to callback function
     * @param {Array} [args] - arguments array to pass in with context. Requires context to be passed
     * @return {Object} - new event cache
     */
    eventValidator: function(e, element, callback, context, args){
        if(element && element.type && element.type !== e.type){
            e.stopPropagation();
            e.preventDefault();
            return tv.createEventCacheObj({}, true);
        } else {
            element = tv.createEventCacheObj(e);
            (typeof context === "object" ? callback.apply(context, args) : callback());
            return element;
        }
    },

    /**
     * @method createEventCacheObj()
     * @param {Object} event - event object
     * @param {String} [event.type] - event type
     * @param {Number} [event.timeStamp] - time of event in MS since load
     * @param {Boolean} [reset=false] - flag to reset the object
     * @returns {{type: *, time: string}}
     */
    createEventCacheObj: function (event, reset){
        if(typeof reset !== 'boolean') reset = false;
        return {
            type: !reset ? event.type : null,
            time: !reset ? (event.timeStamp).toFixed(2): null
        };
    }
};

// Here is where the magic happens
var eventCache = [];
var pos = 0;

var $collapses = document.getElementsByClassName('tv-collapse__heading');
    Array.prototype.forEach.call($collapses, function(ele){
        ele.addEventListener('click', toggleCollapse);
        ele.addEventListener('touchend', toggleCollapse);

        // Cache mechanism
        ele.setAttribute('data-event-cache', String(pos++));
    });

/**
 * @func toggleCollapse()
 * @param {Object} e - event object
 * @desc responsible for toggling the state of a collapse element
 */
function toggleCollapse(e){
    eventCache[pos] = tv.eventValidator(e, eventCache[pos], function(){
       // Any event which isn't blocked will run the callback and its content
       // the context and arguments of the anonymous function match the event function context and arguments (assuming they are passed using the last two parameters of tv.eventValidator)

    }, this, arguments);
}

Réponse originale:

Voici une réponse qui est une modification de Rafael Fragoso answer - pure JS.

(function(){
  
  button = document.getElementById('sayHi');
  
  button.addEventListener('touchstart', ohHai);
  button.addEventListener('click', ohHai);

  function ohHai(event){
    event.stopPropagation();
    event.preventDefault();
    console.log('ohHai is:', event.type);
  };
})();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SO - Answer</title>
</head>
<body>
  <button id="sayHi">Anyone there?</button>
</body>
</html>

Exécutez le fragment de code sur ce qui suit et faites attention au résultat:

  • Téléphone
  • Tablette
  • Tablette (mode bureau - le cas échéant)
  • Bureau
  • Bureau (écran tactile - le cas échéant)

La clé est que nous empêchons les événements successifs de tirer. Les navigateurs mobiles font de leur mieux pour émuler un clic lorsqu'une touche se produit. J'aimerais pouvoir trouver le lien vers un article récemment décrit qui décrit tous les événements qui se produisent après le démarrage par clic. (Je cherchais le délai de 300 ms entre le double tapement et le clic de déclenchement).

Dispositifs tactiles et souris

J'ai effectué quelques tests avec une Surface Pro et un ordinateur de bureau Windows 10 doté d'un écran tactile. Ce que j’ai trouvé, c’est qu’ils ont tous les deux déclenché des événements comme vous le supposeriez, touchez au toucher et cliquez pour le trackpad, la souris et le styliste. La chose intéressante était qu'un événement tactile qui était proche, mais pas sur le bouton, déclencherait un événement clic sans événement tactile. Il semble que la fonctionnalité intégrée de Windows 10 recherche les nœuds les plus proches dans un rayon et si un nœud est trouvé, un événement basé sur la souris sera déclenché.

Plusieurs événements du même type

Si deux événements du même type se trouvent sur un élément, l'arrêt de la propagation de l'événement pourrait empêcher l'un des événements de se déclencher. Il existe différentes manières de gérer cela en utilisant une sorte de cache. Mes idées initiales étaient de modifier l'objet événement, mais nous obtenons une référence, je pense donc qu'une solution de cache devra suffire.

0
curtismorte

Profitant du fait qu'un clic toujours suivra un événement tactile, voici ce que j'ai fait pour supprimer le "clic fantôme" sans devoir utiliser de délai d'attente ni 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
});

En gros, chaque fois qu'un événement ontouchstart est déclenché sur l'élément, un indicateur est défini sur un ensemble, puis supprimé (et ignoré), lorsque le clic survient.

0
BigMacAttack