2014-08-29 14 views
30

Pracuję nad niektórymi interfejsami javascript i korzystam z wielu zdarzeń dotykowych, takich jak "touchend", aby poprawić reakcję na urządzenia dotykowe. Jednak są pewne problemy logiczne, które mnie dręczyć ...JavaScript touchend kontra dylemat kliknięcia

Widziałem, że wielu programistów miesza się z "touchend" i "kliknięciem" w tym samym wydarzeniu. W wielu przypadkach nie będzie bolało, ale w istocie funkcja będzie ogień dwa razy na urządzeniach dotykowych:

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

Sugerowano, że można wykryć dotykowy zdolności i ustaw zdarzenie odpowiednio na przykład:

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

Problem związany z powyższym problemem polega na tym, że ulegnie uszkodzeniu na urządzeniach obsługujących zarówno dotyk, jak i mysz. Jeśli użytkownik używa obecnie myszy na urządzeniu z dwoma wejściami, "kliknięcie" nie zostanie uruchomione, ponieważ do przycisku przypisany jest tylko przycisk "touchend".

Innym rozwiązaniem jest wykrycie urządzenia (np. "IOS") i przypisanie zdarzenia na podstawie: Click event called twice on touchend in iPad. Oczywiście rozwiązanie w powyższym linku dotyczy tylko iOS (nie Androida lub innych urządzeń) i wydaje się bardziej "hack", aby rozwiązać coś zupełnie elementarnego.

Innym rozwiązaniem byłoby wykrycie ruchu myszy i połączenie go z możliwościami dotykowymi, aby dowiedzieć się, czy użytkownik jest na myszy, czy dotknął. Problem oczywiście polegający na tym, że użytkownik może nie poruszać myszą, gdy chcesz go wykryć ...

Najbardziej niezawodnym rozwiązaniem, jakie mogę wymyślić, jest użycie prostej funkcji debounce, aby upewnić się, że tylko funkcja jest dostępna wyzwala raz w krótkim odstępie czasu (na przykład 100ms):

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

Czy czegoś brakuje, czy ktoś ma jakieś lepsze sugestie?

Edit: znalazłem ten link po moim poście, co sugeruje podobne rozwiązanie jak powyżej: How to bind 'touchstart' and 'click' events but not respond to both?

+2

To niczego nie rozwiązuje ... Modernizor wykrywa tylko "dotyk". Wydarzenie nadal będzie dwa razy uruchamiane na urządzeniach dotykowych. Lub jeśli przypiszesz wydarzenie "dotykowe" dla urządzeń dotykowych (poprzez modernizator), przestanie działać dla użytkowników korzystających z myszy na urządzeniach z dwoma wejściami, takich jak wiele urządzeń z systemem Windows8. – suncat100

Odpowiedz

24

Po dniu badania, pomyślałem najlepszym rozwiązaniem jest po prostu trzymać się kliknięcia i użyj https://github.com/ftlabs/fastclick, aby usunąć opóźnienie dotykania. Nie jestem w 100% pewien, czy jest tak wydajny, jak touchend, ale nie daleko od co najmniej.

zrobiłem wymyślić sposób, aby wyłączyć wyzwalanie zdarzeń dwukrotnie w kontakcie za pomocą stopPropagation i preventDefault, ale ta jest ryzykowna, gdyż może to kolidować z innymi gestów dotykowych w zależności od elementu, w którym jest stosowane:

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

W rzeczywistości szukałem rozwiązania, aby połączyć touchstart na niektórych elementach interfejsu użytkownika, ale nie widzę, jak można je połączyć z kliknięciem innym niż powyższe rozwiązanie.

+0

jest rodzimym sposobem wykrywania, czy przeglądarka obsługuje touchstart/koniec? – BenRacicot

3

To pytanie zostało odebrane, ale może wymaga aktualizacji.

Zgodnie z a notice from Google, nie będzie już żadnego opóźnienia 300-350ms, jeśli uwzględnimy poniższy wiersz w elemencie <head>.

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

To wszystko! I nie będzie już żadnej różnicy między wydarzeniem typu "kliknij i dotknij"!

+0

To interesujące. Być może czas pozbyć się fastclick.js ... Przetestuję to nieco w przeglądarkach mobilnych. – suncat100

+0

Meta tagi powinny być umieszczone w tagu 'head', a nie tagu" header ". – James

+1

To nie było dla mnie prawdziwe. – skybondsor

0

Witaj, możesz wdrożyć w następujący sposób.

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

Twoja funkcja debounce opóźni obsługę każde kliknięcie na 100 ms:

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

Zamiast tego stworzył cancelDuplicates funkcję, która odpala się od razu, ale wszelkie późniejsze rozmowy w 10 ms zostanie anulowane:

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

Zastosowanie:

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