Come associare gli eventi 'touchstart' e 'click' ma non rispondere ad entrambi?


198

Sto lavorando su un sito Web mobile che deve funzionare su una varietà di dispositivi. Quello che mi sta facendo venire il mal di testa al momento è BlackBerry.

Dobbiamo supportare sia i clic della tastiera sia gli eventi touch.

Idealmente userei solo:

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

ma il problema che stiamo riscontrando è che alcuni di questi dispositivi Blackberry hanno un ritardo molto fastidioso dal momento del tocco ad innescare un clic.

Il rimedio è invece usare il touchstart:

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

Ma come faccio a legare entrambi gli eventi, ma solo sparandone uno? Ho ancora bisogno dell'evento click per i dispositivi a tastiera, ma ovviamente non voglio che l'evento click venga attivato se sto usando un dispositivo touch.

Una domanda bonus: esiste comunque un modo per farlo e ospitare anche i browser che non hanno nemmeno un evento touchstart? Nella ricerca di questo, sembra che BlackBerry OS5 non supporti il ​​touchstart, quindi dovrà anche fare affidamento sugli eventi di clic per quel browser.

APPENDICE:

Forse una domanda più completa è:

Con jQuery, è possibile / consigliato gestire entrambe le interazioni tattili e le interazioni del mouse con gli stessi binding?

Idealmente, la risposta è sì. In caso contrario, ho alcune opzioni:

1) Utilizziamo WURFL per ottenere informazioni sul dispositivo in modo da poter creare la nostra matrice di dispositivi. A seconda del dispositivo, utilizzeremo il touchstart O il clic.

2) Rileva il supporto touch nel browser tramite JS (ho bisogno di fare qualche ricerca in più su questo, ma sembra che sia fattibile).

Tuttavia, ciò lascia ancora un problema: che dire dei dispositivi che supportano ENTRAMBI. Alcuni dei telefoni supportati (ovvero Nokias e Blackberry) sono dotati sia di touchscreen che di tastiere. Quindi quel tipo di mi riporta al punto di partenza alla domanda originale ... c'è un modo per consentire entrambi contemporaneamente in qualche modo?


2
È meglio associarsi a touchstart e touchend e scrivere la propria logica di clic a fianco della logica di tocco. Il callback del clic incorporato come nessuna conoscenza di tocchi.
Justin808,

Non sono sicuro di seguirlo, Justin. Non avrei ancora associato sia un evento touchstart che un clic?
DA.

@DA - no, non ti legheresti affatto al callback .click (). Proverò a scrivere una risposta in qualche codice sudo. Non ho un dispositivo touch a portata di mano per scrivere codice reale :)
Justin808

Ah, ma per chiarire, ho ancora bisogno di eventi click, poiché ci saranno persone che accederanno a questo sito con dispositivi non touch.
DA.

2
L'utilizzo .bind('touchstart mouseup')lo risolverà (basato su uno dei commenti seguenti)
oriadam,

Risposte:


135

Aggiornamento : controlla il progetto Polyfill di eventi puntatore jQuery che ti consente di associare eventi "puntatore" invece di scegliere tra mouse e tocco.


Associa entrambi, ma crea una bandiera in modo che la funzione si attivi solo una volta ogni 100 ms circa.

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

  return false
});

7
hmm ... sembra strano, ma potrebbe funzionare. Il problema è che su alcuni di questi dispositivi, è un notevole ritardo ... forse quasi un secondo ... che sarebbe fastidioso per gli altri su un dispositivo più veloce.
DA.

15
Invece di usarlo clickcambiarlo in mousedown... forse il lungo ritardo è la differenza tra mousedowne mouseupche è come clickviene determinato un.
Mottie,

7
Questa è la soluzione con cui sono andato. Grazie! Quello che faccio è contrassegnare un oggetto touchendapplicando una classe di touched. Questo si attiva prima che venga chiamato l'evento click. Aggiungo quindi un evento click che controlla prima l'esistenza di quella classe. Se è presente, supponiamo che gli eventi touch vengano attivati ​​e non facciamo nulla con il clic se non rimuovere il nome della classe per "ripristinarlo" per l'interazione successiva. Se la classe non è presente, supponiamo che abbiano usato una tastiera per fare clic sull'elemento e attivare la funzione da lì.
DA.

7
Dovresti essere consapevole che la maggior parte dei browser del telefono ha un ritardo di 300 ms sull'evento clic, quindi dovresti aumentare il ritardo di almeno 300, vedi questo articolo sul problema del ritardo di 300 ms . Questo essenzialmente per abilitare la funzionalità di "doppio clic per ingrandire" che era una caratteristica molto importante nei primi giorni di iPhone, quando la maggior parte dei siti Web erano progettati per essere visualizzati su un grande schermo desktop.
timore

12
A questo punto questa domanda è stata vista quasi 100.000 volte. Questo sembra un caso / problema d'uso straordinariamente comune. Tuttavia, questa risposta e altre su questa e altre domande simili sembrano tutte confuse. La soluzione di google, sebbene pensata più a fondo rispetto alla maggior parte, sembra semplicemente un trucco più robusto. Per qualcosa di semplice come un gestore di clic, sembra che la soluzione dovrebbe essere semplice. Nessuno ha trovato una soluzione non hack per questo problema?
jinglesthula,

73

Questa è la correzione che "creo" ed elimina GhostClick e implementa FastClick. Prova da solo e facci sapere se ha funzionato per te.

$(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 Nel JavaScript c'è una funzione chiamata TouchClick che incorpora quanto sopra.
Matt Parkins,

5
Preferisco ALTAMENTE questo metodo di tutte le risposte a questa domanda perché è a) non verifica le funzionalità del dispositivo eb) non utilizza setTimeout. Nella mia esperienza, le soluzioni a problemi come questi che utilizzano setTimeout possono funzionare nella maggior parte dei casi, ma la tempistica degli eventi è abbastanza arbitraria e specifica per il dispositivo.
itsmequinn,

7
Questo non funzionerà perché passando "evento" nella funzione anonima diventa un oggetto locale all'interno di quella funzione, quindi event.handledsarà uguale undefinedogni volta che l'evento viene attivato. Ecco una soluzione: imposta il flag 'gestito' nell'elemento target stesso usando jQuery.data().
thdoan

3
Hai event.stopPropagation();e event.preventDefault();dalla mia comprensione (e test) l'evento può essere lanciato solo una volta con questo. quindi perché anche controllare if(event.handled !== true)? Per me non è necessario o mi manca qualcosa?
nbar,

2
Non dovrebbe event.handledessere controllato per il valore true? Per quanto ne so non lo è mai false. Inizia come undefined, e poi vuoi dire se è stato impostato su true.
ACJ,

49

Potresti provare qualcosa del genere:

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

5
Penso che il problema sia il test delle capacità del dispositivo piuttosto che quello che l'utente sta facendo, giusto? La sfida è che dobbiamo supportare i dispositivi touch che hanno anche tastiere (quindi potrebbero usare entrambi)
DA.

8
Questa non è una buona idea - interromperà l'evento per un utente con un touchscreen e un mouse quando lo utilizza (i laptop con touchscreen stanno diventando abbastanza comuni).
Kristian J.,

1
Ottimo articolo relativo a questo: hacks.mozilla.org/2013/04/…
Luke Melia,

Windows 8 collegherà sempre l'evento 'touchstart' se viene utilizzato perché 'ontouchstart' restituirà 'true' per questo sistema operativo. Quindi, probabilmente non è la risposta migliore a meno che il tuo codice non venga eseguito solo su veri dispositivi touchscreen, ma in tal caso, non è necessario il controllo.
Neil Monroe,

Sui laptop con touchscreen, l'utente non potrà fare clic su nulla utilizzando questo codice in Chrome (almeno Chrome 46). IE 11 sembra funzionare però.
David Sherret,

42

Di solito funziona anche questo:

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

});

8
@Jonathan sbagliato, se stai utilizzando le versioni jQuery più recenti. Live è stato deprecato nella versione 1.7.
Mike Barwick,

Ho anche provato (e l'ho letto altrove - non la mia idea) con e.stopPropagation (); e.preventDefault (); e funziona (per me) quasi, ma in seguito ho scoperto che in effetti funziona come previsto 20 volte ma 1 volta no, quindi non è a prova di proiettile. Guarda qui: stackoverflow.com/questions/25671053/… Apprezza i tuoi commenti su questo problema!
Garavani,

@MikeBarwick Potresti spiegare il tuo commento o fornire un link che lo spieghi? Grazie in anticipo.
Billal Begueradj,

22

Basta aggiungere return false;alla fine della on("click touchstart")funzione evento per risolvere questo problema.

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

Dalla documentazione di jQuery su .on ()

Il ritorno falseda un gestore di eventi chiamerà automaticamente event.stopPropagation()e event.preventDefault(). Un falsevalore può anche essere passato per il gestore come scorciatoia per function(){ return false; }.


2
funziona alla grande, si accende solo una volta su dispositivi con entrambi - questa mi sembra la soluzione più pulita e meno caotica
Adam Cooper

Ha funzionato meglio per me e in realtà aveva senso perché.
Amir5000,

Qualche svantaggio di questo? Perché una soluzione così semplice non è ampiamente conosciuta?
ESR,

10

Ho dovuto fare qualcosa di simile. Ecco una versione semplificata di ciò che ha funzionato per me. Se viene rilevato un evento tocco, rimuovere l'associazione clic.

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

  //your code here
});

Nel mio caso l'evento click è stato associato a un <a>elemento, quindi ho dovuto rimuovere l'associazione click e ricollegare un evento click che ha impedito l'azione predefinita per l' <a>elemento.

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

  //your code here
});

Vorrei che funzionasse così com'è, ma non lo fa. $ (questo) non punta a quello che pensi che faccia.
Dave Lowerre,

8

Ci sono riuscito nel modo seguente.

Vai tranquillo...

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

4
non sparerà due volte sui dispositivi che registrano sia un clic che un touchstart?
DA.

8
Scusa, ti ho fatto sapere. Sì, hai ragione, un tocco genererà anche l'evento touchstart e l'evento click. Questo si chiama ghost click. Google ha implementato una soluzione per questo. Potrebbe non essere semplice ma funziona perfettamente. Ecco il link. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi

2
potrebbe essere banale ma ti manca il eparametro infunction()
ProblemsOfSumit

@Hasanavi è stata un'ottima soluzione ma ho trovato risultati migliori usando .on
Neil

1
Grazie @Neil. Sì, l'utilizzo di .on è un'idea migliore in quanto potrebbe essere rimosso nella versione futura. Ho cambiato il mio esempio da bind a on.
Hasanavi,

5

Credo che la migliore pratica sia ora di usare:

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

touchend

L'evento touchend viene generato quando un touch point viene rimosso dalla superficie touch.

L'evento touchend non attiverà alcun evento del mouse.


MouseUp

L'evento mouseup viene inviato a un elemento quando il puntatore del mouse si trova sopra l'elemento e il pulsante del mouse viene rilasciato. Qualsiasi elemento HTML può ricevere questo evento.

L'evento del mouseup non attiverà alcun evento touch.

ESEMPIO

$('#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)

A partire dal 2017, i browser iniziano con Chrome e fanno passi verso la realizzazione dell'evento clic .on("click") più compatibile sia per il mouse che per il tocco, eliminando il ritardo generato dagli eventi di tocco sulle richieste di clic.

Questo porta alla conclusione che tornare all'utilizzo del solo evento click sarebbe la soluzione più semplice per andare avanti.

Non ho ancora fatto alcun test tra browser per vedere se questo è pratico.


Questo mi è sembrato promettente, quindi ho fatto un sacco di problemi, ma alla fine ho scoperto mouseupdi avere risultati imprevedibili, soprattutto quando si utilizza un trackpad invece di un mouse tradizionale.
skybondsor,

4

Generalmente non si desidera mescolare l'API touch e non touch (clic) predefinito. Una volta entrati nel mondo del tocco, è più facile occuparsi solo delle funzioni relative al tocco. Di seguito è riportato uno pseudo codice che farebbe quello che vuoi.

Se ti connetti nell'evento touchmove e segui le posizioni, puoi aggiungere altri elementi nella funzione doTouchLogic per rilevare gesti e quant'altro.

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

Suppongo che questo sia il nocciolo della domanda: ha senso cercare di supportare entrambi i modelli con una base di codice. Aggiornerò la mia domanda con alcuni altri scenari.
DA.

1
"In genere non si desidera mescolare il tocco predefinito e il non-tocco" dopo aver verificato questo, sono d'accordo. Il problema è che continuano a creare dispositivi touch con tastiere, il che è un mal di testa per noi sviluppatori.
DA.

Concordo sul fatto che sarebbe molto più facile fare all'inizio il tocco decisionale js finale o nessun tocco. Che mondo facile sarebbe! Ma cos'è questo dannato dispositivo che fa clic e tocca sensibile? Vale la pena avere tutti quei mal di testa a causa loro? È questo il futuro che mi chiedo?
Garavani,

Non mi piace l'idea di avere più definizioni di funzioni. La mia azienda sta attualmente lavorando a un progetto in cui un iPad non è gestito in modo specifico come dispositivo mobile e hai ancora bisogno dell'evento touchend. Ma touchend non funziona con le normali versioni desktop. Quindi la soluzione di @mottie va benissimo per quello scenario.
Pete,


3

Bene ... Tutti questi sono super complicati.

Se hai modernizr, è un gioco da ragazzi.

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

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

2
Puoi spiegare cosa fa? Credo che controlla se il dispositivo supporta il tocco e, in caso contrario, utilizza il clic. Il problema è (o almeno lo era , quando ho scritto la domanda) era che alcuni dispositivi supportano entrambi. E su quei dispositivi, ho ancora bisogno di gestire entrambi gli eventi, poiché il trigger potrebbe provenire dal tocco o dal clic.
DA.

2
Consiglio una spiegazione in più.
Aaron Hall

2

Un'altra implementazione per una migliore manutenzione. Tuttavia, questa tecnica eseguirà anche event.stopPropagation (). Il clic non viene catturato su nessun altro elemento che ha fatto clic per 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

Solo a scopo di documentazione, ecco cosa ho fatto per il clic più rapido / reattivo sul desktop / toccare su una soluzione mobile a cui potrei pensare:

Ho sostituito la onfunzione di jQuery con una modificata che, ogni volta che il browser supporta eventi touch, ha sostituito tutti i miei eventi click con 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'uso di quanto sarebbe lo stesso di prima, come:

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

Userebbe il touchstart quando disponibile, fare clic quando no. Non sono necessari ritardi di alcun tipo: D


1
La cattura di questo sarebbe dispositivi che supportano ENTRAMBE il tocco E le tastiere in cui è necessario assicurarsi di accogliere entrambe le interazioni.
DA.

Buona osservazione @DA. Ho aggiunto un segno di spunta per sostituire l'evento click solo se non si utilizza alcun evento touch in combinazione con esso. Stil non sarà una soluzione per ogni progetto, ma sono sicuro che sarebbe adatto a molti.
Gerson Goulart,

2

Ho appena ontouchstartavuto l'idea di memorizzare se fosse mai stato attivato. In questo caso ci troviamo su un dispositivo che lo supporta e vogliamo ignorare l' onclickevento. Dal momento che ontouchstartdovrebbe essere sempre attivato prima onclick , sto usando questo:

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


1
Poiché gli utenti potrebbero utilizzare il tocco e il mouse durante la stessa visita alla pagina, il problema con questo frammento è che registra il flag touchAvailable una volta anziché per evento. Il plug-in jQuery da stackoverflow.com/a/26145343/328272 fa lo stesso del tuo codice, ma può dire per un evento del mouse se è stato attivato dal tocco o dal mouse sulla base di un evento. Non solo ie. al clic, ma anche alla mouseleave che potrebbe essere attivata pochi secondi (o minuti) dopo il mouseenter (ideale per i menu a comparsa che dovrebbero espandersi al passaggio del mouse con i gesti del mouse o fare clic quando si utilizza il tocco).
Lmeurs,

2

Potresti provare così:

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

che dire dei dispositivi che supportano il tocco e il mouse
Walle Cyril il

2

Nel mio caso questo ha funzionato perfettamente:

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

Il problema principale era quando invece il mouse ho provato con il clic, sui dispositivi touch ha attivato contemporaneamente il clic e il touchend, se uso il clic disattivato, alcune funzionalità non hanno funzionato affatto sui dispositivi mobili. Il problema con il clic è che è un evento globale che attiva il resto dell'evento incluso il touchend.


1

Questo ha funzionato per me, il cellulare ascolta entrambi, quindi evita quello, che è l'evento touch. il desktop ascolta solo il mouse.

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

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

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

1

Essendo per me la migliore risposta a quella data da Mottie, sto solo cercando di rendere il suo codice più riutilizzabile, quindi questo è il mio contributo:

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

Sto anche lavorando su un'app Web per Android / iPad e sembra che usare solo "touchmove" sia sufficiente per "spostare i componenti" (non è necessario il touchstart). Disabilitando il touchstart, puoi usare .click (); da jQuery. In realtà funziona perché non è stato sovraccaricato dal touchstart.

Infine, puoi binb .live ("touchstart", function (e) {e.stopPropagation ();}); per chiedere all'evento touchstart di interrompere la propagazione, soggiorno fare clic su () per attivarsi.

Ha funzionato per me.


Come disabiliteresti touchstart?
punkrockbuddyholly,

Credo che tu "potresti" fare qualcosa del tipo: jQuery ("# ​​my_element"). On ('touchstart', function (e) {e.preventDefault ()});
nembleton,

0

Ci sono molte cose da considerare quando si cerca di risolvere questo problema. La maggior parte delle soluzioni interrompe lo scorrimento o non gestisce correttamente gli eventi di clic fantasma.

Per una soluzione completa vedi https://developers.google.com/mobile/articles/fast_buttons

NB: Non è possibile gestire eventi di clic fantasma in base agli elementi. Un clic ritardato viene generato dalla posizione dello schermo, quindi se i tuoi eventi touch modificano la pagina in qualche modo, l'evento click verrà inviato alla nuova versione della pagina.


0

Può essere efficace assegnare agli eventi 'touchstart mousedown'o 'touchend mouseup'evitare effetti collaterali indesiderati derivanti dall'uso click.


0

Approfittando del fatto che un clic seguirà sempre un evento touch, ecco cosa ho fatto per sbarazzarmi del "clic fantasma" senza dover utilizzare timeout o flag globali.

$('#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
});

Fondamentalmente ogni volta che un ontouchstartevento si attiva sull'elemento, contrassegna un set e successivamente viene rimosso (e ignorato), quando arriva il clic.


0

Perché non utilizzare l'API degli eventi jQuery?

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

Ho usato questo semplice evento con successo. È pulito, identificabile e abbastanza flessibile da migliorare.

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
Si consiglia di utilizzare il rilevamento delle funzionalità durante lo sniffing dell'agente utente.
jinglesthula,

Il fermo è dispositivi che supportano sia il tocco che il clic.
DA.

0

Se stai usando jQuery, per me ha funzionato abbastanza bene:

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

Anche altre soluzioni sono buone ma stavo legando più eventi in un ciclo e avevo bisogno della funzione di auto-chiamata per creare una chiusura appropriata. Inoltre, non volevo disabilitare l'associazione poiché volevo che fosse richiamabile al prossimo clic / touchstart.

Potrebbe aiutare qualcuno in una situazione simile!


0

Per funzionalità semplici, basta riconoscere il tocco o fare clic Uso il seguente codice:

var element = $("#element");

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

Questo restituirà "touch" se l'evento touchstart è definito e "no touch" in caso contrario. Come ho detto, questo è un approccio semplice per gli eventi click / tap proprio questo.


0

Sto provando questo e finora funziona (ma sono solo su Android / Phonegap quindi avvertimento)

  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...
  }

Non mi piace il fatto che sto rilegando i gestori ad ogni tocco, ma tutte le cose considerate i tocchi non accadono molto spesso (in tempo di computer!)

Ho un sacco di gestori come quello per '#keypad', quindi avere una semplice funzione che mi permette di affrontare il problema senza troppo codice è il motivo per cui sono andato in questo modo.



0

EDIT: La mia precedente risposta (basata sulle risposte in questo thread) non era la strada giusta per me. Volevo che un sottomenu si espandesse al passaggio del mouse o toccasse il clic e collassasse alla fine del mouse o un altro tocco del tocco. Dato che normalmente gli eventi del mouse vengono generati dopo gli eventi touch, è stato difficile scrivere listener di eventi che supportano sia l'input touchscreen che mouse contemporaneamente.

Plugin jQuery: tocca o mouse

Ho finito per scrivere un plug-in jQuery chiamato " Touch Or Mouse " (minimizzato 897 byte) in grado di rilevare se un evento è stato invocato da un touchscreen o da un mouse (senza testare il supporto touch!). Ciò consente il supporto sia del touchscreen che del mouse contemporaneamente e separa completamente i loro eventi.

In questo modo l'OP può utilizzare touchstarto touchendper rispondere rapidamente ai clic di tocco e clickai clic richiamati solo da un mouse.

Dimostrazione

Il primo deve fare cioè. l'elemento body tiene traccia degli eventi touch:

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

Gli eventi del mouse sono legati agli elementi nel modo predefinito e chiamando telefonicamente $body.touchOrMouse('get', e)possiamo scoprire se l'evento è stato invocato da un touchscreen o da un mouse.

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

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

Guarda il plugin al lavoro su http://jsfiddle.net/lmeurs/uo4069nh .

Spiegazione

  1. Questo plugin deve essere chiamato ie. l'elemento body da tracciare touchstarte gli touchendeventi, in questo modo l' touchendevento non deve essere attivato sull'elemento trigger (es. un link o un pulsante). Tra questi due eventi touch questo plug-in considera qualsiasi evento del mouse da invocare al tocco.
  2. Gli eventi del mouse vengono attivati ​​solo dopo touchend, quando viene ghostEventDelaygenerato un evento del mouse all'interno dell'opzione (opzione, 1000 ms per impostazione predefinita) touchend, questo plug-in considera l'evento del mouse invocato al tocco.
  3. Quando si fa clic su un elemento utilizzando un touchscreen, l'elemento ottiene lo :activestato. L' mouseleaveevento viene generato solo dopo che l'elemento perde questo stato di ie. facendo clic su un altro elemento. Dato che potrebbero essere secondi (o minuti!) Dopo che l' mouseenterevento è stato generato, questo plugin tiene traccia dell'ultimo mouseenterevento di un elemento : se l'ultimo mouseenterevento è stato invocato al tocco, anche il seguente mouseleaveevento viene considerato invocato al tocco.

0

Ecco un modo semplice per farlo:

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

Fondamentalmente si salva il primo tipo di evento che viene attivato nella proprietà 'trigger' nell'oggetto dati di jQuery che è collegato al documento principale ed eseguito solo quando il tipo di evento è uguale al valore in 'trigger'. Sui dispositivi touch, la catena di eventi sarebbe probabilmente "touchstart" seguita da "click"; tuttavia, il gestore "click" non verrà eseguito perché "click" non corrisponde al tipo di evento iniziale salvato in "trigger" ("touchstart").

Il presupposto, e credo che sia sicuro, è che il tuo smartphone non cambierà spontaneamente da un dispositivo touch a un dispositivo mouse, altrimenti il ​​tap non si registrerà mai perché il tipo di evento "trigger" viene salvato solo una volta per caricamento della pagina e "clic" non corrisponderebbero mai a "touchstart".

Ecco un codice con cui puoi giocare (prova a toccare il pulsante su un dispositivo touch - non ci dovrebbe essere alcun ritardo di clic): http://codepen.io/thdoan/pen/xVVrOZ

Ho anche implementato questo come un semplice plug-in jQuery che supporta anche il filtro dei discendenti di jQuery passando una stringa di selezione:

// 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/


Perché hai usato una proprietà data nel documento invece di usare solo una variabile?
Raphael Schweikert,

@RaphaelSchweikert Probabilmente solo per abitudine. Non esiste un modo giusto o sbagliato, ma mi piace usare $.data()per archiviare dati a cui è possibile accedere da più funzioni ma che non appartengono all'ambito globale. Un vantaggio è che, poiché lo sto memorizzando in un elemento, mi fornisce naturalmente più contesto.
dal

0

Il metodo migliore che ho trovato è quello di scrivere l'evento touch e fare in modo che l'evento chiami il normale evento click programmaticamente. In questo modo hai tutti i tuoi normali eventi click e quindi devi aggiungere un solo gestore eventi per tutti gli eventi touch. Per ogni nodo che vuoi rendere tangibile, aggiungi semplicemente la classe "toccabile" per invocare il gestore di tocco. Con Jquery funziona così con qualche logica per assicurarsi che sia un vero evento touch e non un falso positivo.

$("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
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.