2014-08-29 20 views
30

Sto lavorando su alcune interfacce utente javascript e utilizzo di molti eventi di tocco come "touchend" per una migliore risposta sui dispositivi touch. Tuttavia, ci sono alcuni problemi logici che mi infastidiscono ...JavaScript touchend versus click dilemma

Ho visto che molti sviluppatori mischiano "tocco" e "clic" nello stesso evento. In molti casi non farà male, ma essenzialmente la funzione avrebbe sparato due volte su dispositivi touch:

button.on('click touchend', function(event) { 
    // this fires twice on touch devices 
}); 

è stato suggerito che si potrebbe rilevare tocco capacità, e impostare l'evento in modo appropriato, ad esempio:

var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click'; 
button.on(myEvent, function(event) { 
    // this fires only once regardless of device 
}); 

Il problema di cui sopra, è che si romperà su dispositivi che supportano sia touch che mouse. Se l'utente sta attualmente utilizzando il mouse su un dispositivo a doppio ingresso, il "clic" non si attiva perché al pulsante viene assegnato solo "touchend".

Un'altra soluzione è rilevare il dispositivo (ad esempio "iOS") e assegnare un evento basato su quello: Click event called twice on touchend in iPad. Ovviamente, la soluzione nel link sopra è solo per iOS (non per Android o altri dispositivi), e sembra più un "hack" per risolvere qualcosa di abbastanza elementare.

Un'altra soluzione potrebbe essere quella di rilevare il movimento del mouse e combinarlo con la funzionalità touch per capire se l'utente è sul mouse o sul touch. Ovviamente il problema è che l'utente non stia spostando il mouse da quando lo si vuole rilevare ...

La soluzione più affidabile a cui posso pensare è quella di utilizzare una semplice funzione debounce per assicurarsi semplicemente che la funzione sia solo innesca una volta entro un breve intervallo (ad esempio 100 ms):

button.on('click touchend', $.debounce(100, function(event) { 
    // this fires only once on all devices 
})); 

mi sto perdendo qualcosa, o qualcuno ha qualche suggerimento migliore?

Edit: Ho trovato questo link dopo il mio post, che suggerisce una soluzione simile a quanto sopra: How to bind 'touchstart' and 'click' events but not respond to both?

+2

Questo non risolve nulla ... Modernizr rileva solo il "tocco". L'evento verrà comunque attivato due volte sui dispositivi touch. O se si assegna l'evento 'tocco' per dispositivi touch (tramite modernizr), smetterà di funzionare per gli utenti che utilizzano il mouse su dispositivi a doppio ingresso come molti dispositivi Windows8. – suncat100

risposta

24

Dopo una giornata di ricerche, ho pensato che la soluzione migliore è quella di attenersi solo per clic e utilizzare https://github.com/ftlabs/fastclick per rimuovere il ritardo tocco. Non sono sicuro al 100% che questo sia efficiente come touchend, ma non lontano da almeno.

ho fatto capire un modo per disattivare eventi di attivazione due volte sul touch utilizzando stopPropagation e preventDefault, ma questo è dubbia in quanto potrebbe interferire con altri movimenti tocco seconda dell'elemento cui è applicata:

button.on('touchend click', function(event) { 
    event.stopPropagation(); 
    event.preventDefault(); 
    // this fires once on all devices 
}); 

Stavo infatti cercando una soluzione per combinare touchstart su alcuni elementi dell'interfaccia utente, ma non riesco a vedere come sia possibile combinarlo con facendo clic su oltre alla soluzione precedente.

+0

esiste un modo nativo per rilevare se un browser supporta touchstart/end? – BenRacicot

-1

Sì, la disattivazione dello zoom con doppio tocco (e quindi il ritardo del clic) è in genere l'opzione migliore. E finalmente abbiamo un buon consiglio per fare ciò che sarà soon work on all browsers.

Se, per qualche motivo, non si desidera farlo.È inoltre possibile utilizzare UIEvent.sourceCapabilities.firesTouchEvents per ignorare esplicitamente il numero ridondante click. Il polyfill for this fa qualcosa di simile al tuo codice di debouncing.

3

Questa domanda viene data risposta, ma forse deve essere aggiornata.

Secondo a notice from Google, non ci sarà più alcun ritardo di 300-350 ms se includiamo la riga sottostante nell'elemento <head>.

<meta name="viewport" content="width=device-width"> 

Questo è tutto! E non ci saranno più differenze tra gli eventi click e touch!

+0

È interessante. Forse è il momento di sbarazzarsi di fastclick.js ... Lo metto alla prova un po 'nei browser mobili. – suncat100

+0

I meta tag devono essere posizionati nel tag 'head', non nel tag' header'. – James

+1

Questo non è stato vero per me. – skybondsor

0

Ciao è possibile implementare il seguente modo.

function eventHandler(event, selector) { 
    event.stopPropagation(); // Stop event bubbling. 
    event.preventDefault(); // Prevent default behaviour 
    if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks. 
} 

// Implement 
$('.class').on('touchend click', function(event) { 
    eventHandler(event, $(this)); // Handle the event. 
    // Do somethings... 
}); 
0

La vostra funzione debounce ritarderà movimentazione di ogni clic per 100 ms:

button.on('click touchend', $.debounce(100, function(event) { 
    // this is delayed a minimum of 100 ms 
})); 

Invece, ho creato una funzione cancelDuplicates che spara subito, ma tutte le chiamate successive entro 10 ms viene annullata:

function cancelDuplicates(fn, threshhold, scope) { 
    if (typeof threshhold !== 'number') threshhold = 10; 
    var last = 0; 

    return function() { 
     var now = +new Date; 

     if (now >= last + threshhold) { 
      last = now; 
      fn.apply(scope || this, arguments); 
     } 
    }; 
} 

Usage:

button.on('click touchend', cancelDuplicates(function(event) { 
    // This fires right away, and calls within 10 ms after are cancelled. 
}));