Jak powiązać zdarzenia „touchstart” i „kliknięcie”, ale nie reagować na oba?

198

Pracuję na mobilnej stronie internetowej, która musi działać na różnych urządzeniach. Ten, który sprawia mi teraz ból głowy, to BlackBerry.

Musimy obsługiwać zarówno kliknięcia klawiatury, jak i zdarzenia dotykowe.

Idealnie byłoby po prostu użyć:

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

ale mamy problem z tym, że niektóre z tych urządzeń BlackBerry mają bardzo irytujące opóźnienie od momentu dotknięcia do uruchomienia kliknięcia.

Środkiem zaradczym jest zamiast tego użycie touchstart:

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

Ale jak mam powiązać oba zdarzenia, ale tylko jedno? Nadal potrzebuję zdarzenia kliknięcia na urządzeniach z klawiaturą, ale oczywiście nie chcę uruchamiać zdarzenia kliknięcia, jeśli korzystam z urządzenia dotykowego.

Dodatkowe pytanie: czy można to zrobić i dodatkowo pomieścić przeglądarki, które nawet nie mają zdarzenia TouchStart? Badając to, wygląda na to, że BlackBerry OS5 nie obsługuje TouchStart, więc będzie musiał polegać na zdarzeniach kliknięcia w tej przeglądarce.

UZUPEŁNIENIE:

Być może bardziej kompleksowym pytaniem jest:

Czy w jQuery jest możliwe / zalecane, aby obsługiwać zarówno interakcje dotykowe, jak i interakcje myszy za pomocą tych samych powiązań?

Idealnie odpowiedź brzmi: tak. Jeśli nie, mam kilka opcji:

1) Korzystamy z WURFL, aby uzyskać informacje o urządzeniu, aby stworzyć własną matrycę urządzeń. W zależności od urządzenia użyjemy funkcji touchstart LUB kliknięcia.

2) Wykryj obsługę dotykową w przeglądarce przez JS (muszę zrobić więcej badań na ten temat, ale wydaje się, że jest to wykonalne).

Pozostaje jednak jeden problem: co z urządzeniami, które obsługują ZARÓWNO. Niektóre telefony, które obsługujemy (a mianowicie Nokias i BlackBerries) mają zarówno ekrany dotykowe, jak i klawiatury. W ten sposób zataczam pełne koło z powrotem do pierwotnego pytania ... czy jest sposób, aby pozwolić na oba na raz?

DA.
źródło
2
Lepiej jest związać się z touchstartem i dotykiem oraz napisać własną logikę kliknięć obok logiki dotyku. Wbudowane oddzwanianie po kliknięciu jako brak wiedzy na temat dotknięć.
Justin808,
Nie jestem pewien, czy podążam, Justin. Czy nadal nie miałbym powiązanego z nim zdarzenia startu i kliknięcia?
DA.
@DA - nie, w ogóle nie wiązałbyś się z wywołaniem zwrotnym .click (). Spróbuję napisać odpowiedź w jakimś kodzie sudo. Nie mam pod ręką urządzenia dotykowego, żeby napisać prawdziwy kod :)
Justin808,
Ach, ale aby to wyjaśnić, nadal potrzebuję zdarzeń kliknięcia, ponieważ osoby będą miały dostęp do tej witryny za pomocą urządzeń bezdotykowych.
DA.
2
Korzystanie .bind('touchstart mouseup')rozwiąże to (na podstawie jednego z poniższych komentarzy)
oriadam

Odpowiedzi:

135

Aktualizacja : Sprawdź projekt jQuery Pointer Events Polyfill , który umożliwia powiązanie ze zdarzeniami „wskaźnikowymi” zamiast wybierania między myszą a dotykiem.


Powiązaj oba, ale stwórz flagę, aby funkcja uruchamiała się tylko raz na około 100 ms.

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

  return false
});
Mottie
źródło
7
hmm ... wydaje się hacking, ale to może zadziałać. Problem polega na tym, że na niektórych z tych urządzeń zauważalne jest opóźnienie ... może prawie sekunda ... co byłoby denerwujące dla innych na szybszym urządzeniu.
DA.
15
Zamiast używać clickzmień go na mousedown... może duże opóźnienie jest różnicą między tym, mousedowna mouseuptym clickokreśla się a.
Mottie,
7
To jest rozwiązanie, z którym poszedłem. Dzięki! To, co robię, polega na oznaczeniu przedmiotu touchendprzez zastosowanie klasy touched. Uruchamia się przed wywołaniem zdarzenia kliknięcia. Następnie dodaję zdarzenie kliknięcia, które najpierw sprawdza istnienie tej klasy. Jeśli tak, zakładamy, że uruchomiono zdarzenia dotykowe i nie klikamy nic poza kliknięciem nazwy klasy, aby „zresetować” ją do następnej interakcji. Jeśli klasy nie ma, zakładamy, że użyli klawiatury, aby kliknąć element i uruchomić funkcję od tego miejsca.
DA.
7
Należy pamiętać, że większość przeglądarek telefonicznych ma opóźnienie 300 ms na zdarzenie kliknięcia, dlatego należy zwiększyć opóźnienie do co najmniej 300, zobacz ten artykuł na temat problemu opóźnienia 300 ms . Ma to na celu przede wszystkim włączenie funkcji „podwójnego kliknięcia, aby powiększyć”, która była bardzo ważną funkcją we wczesnych dniach iPhone'a, kiedy większość stron internetowych była zaprojektowana do wyświetlania na dużym ekranie pulpitu.
respekt
12
To pytanie zostało w tym momencie wyświetlone prawie 100 000 razy. Wydaje się, że jest to niezwykle powszechny przypadek użycia / problem. Jednak ta odpowiedź i inne pytania dotyczące tego i podobnych pytań wydają się hackerskie. Rozwiązanie google, choć dokładniej przemyślane niż większość, wydaje się po prostu bardziej solidnym hackem. W przypadku czegoś tak prostego jak moduł obsługi kliknięć wydaje się, że rozwiązanie powinno być proste. Czy nikt nie znalazł rozwiązania niehakującego dla tego problemu?
jinglesthula
73

To jest poprawka, którą „tworzę”, która usuwa GhostClick i implementuje FastClick. Spróbuj sam i daj nam znać, czy zadziałało.

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic here

});
Rafael Fragoso
źródło
2
helgatheviking: jsbin.com/ijizat/25 W JavaScript jest funkcja o nazwie TouchClick, która zawiera powyższe.
Matt Parkins
5
WYSOKO wolę tę metodę spośród wszystkich odpowiedzi na to pytanie, ponieważ a) nie testuje ona możliwości urządzenia ib) nie używa setTimeout. Z mojego doświadczenia wynika, że ​​rozwiązania takich problemów, które wykorzystują setTimeout, mogą działać w większości przypadków, ale czas zdarzeń jest dość dowolny i zależy od urządzenia.
itsmequinn,
7
To nie zadziała, ponieważ przekazując „zdarzenie” w funkcji anonimowej, staje się on lokalnym obiektem w obrębie tej funkcji, więc event.handledbędzie równa za undefinedkażdym razem, gdy zdarzenie zostanie wyzwolone. Oto rozwiązanie: ustaw flagę „obsługiwane” w samym elemencie docelowym za pomocą jQuery.data().
thdoan
3
Masz event.stopPropagation();i według event.preventDefault();mojego zrozumienia (i testowania) wydarzenie można odpalić tylko raz. więc po co to sprawdzać if(event.handled !== true)? Dla mnie nie jest to konieczne, czy coś mi umknęło?
nbar
2
Nie event.handlednależy sprawdzać wartości true? O ile mogę powiedzieć, to nigdy false. Zaczyna się jako undefined, a następnie chcesz powiedzieć, czy zostało ustawione true.
ACJ
49

Możesz spróbować czegoś takiego:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
spławik
źródło
5
Myślę, że problem polega na tym, że testuje możliwości urządzenia, a nie to, co robi użytkownik, prawda? Wyzwanie polega na tym, że musimy obsługiwać urządzenia dotykowe, które również mają klawiatury (aby mogły korzystać z obu)
DA.
8
To nie jest dobry pomysł - zepsuje wydarzenie dla użytkownika z ekranem dotykowym i myszą podczas korzystania z myszy (laptopy z ekranami dotykowymi stają się dość powszechne).
Kristian J.
1
Doskonały artykuł na ten temat: hacks.mozilla.org/2013/04/…
Luke Melia
Windows 8 zawsze będzie łączyć zdarzenie „touchstart”, jeśli jest ono używane, ponieważ „ontouchstart” zwróci „true” dla tego systemu operacyjnego. Prawdopodobnie nie jest to najlepsza odpowiedź, chyba że Twój kod będzie działał tylko na prawdziwych urządzeniach z ekranem dotykowym, ale jeśli tak, to wcale nie potrzebujesz czeku.
Neil Monroe
Na laptopach z ekranem dotykowym użytkownik nie będzie mógł klikać niczego przy użyciu tego kodu w chrome (przynajmniej chrome 46). Wydaje się, że IE 11 działa.
David Sherret
42

Zwykle działa to również:

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

});
Jonathan
źródło
8
@Jonathan się myli, jeśli używasz nowszych wersji jQuery. Usługa Live została wycofana w wersji 1.7.
Mike Barwick
Próbowałem też (i czytałem to gdzie indziej - nie mój pomysł) za pomocą e.stopPropagation (); e.preventDefault (); i działa (dla mnie) prawie, ale później odkryłem, że w rzeczywistości działa zgodnie z przewidywaniami 20 razy, ale 1 raz nie, więc nie jest kuloodporny. Zajrzyj tutaj: stackoverflow.com/questions/25671053/… Doceń swoje komentarze na ten temat!
Garavani
@MikeBarwick Czy możesz wyjaśnić swój komentarz lub podać link, który go wyjaśnia? Z góry dziękuję.
Billal Begueradj
22

Samo dodanie return false;na końcu on("click touchstart")funkcji zdarzenia może rozwiązać ten problem.

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

Z dokumentacji jQuery na .on ()

Po powrocie falsez procedury obsługi zdarzeń nastąpi automatyczne wywołanie event.stopPropagation()i event.preventDefault(). falseWartość może być również przekazywane do obsługi jako skrót function(){ return false; }.

Roy
źródło
2
działa świetnie, odpala tylko raz na urządzeniach z obu - wydaje mi się to najczystszym najmniej hacky rozwiązanie dla mnie
Adam Cooper
Działało najlepiej dla mnie i właściwie miało sens, dlaczego.
Amir5000,
Jakieś wady tego? Dlaczego tak proste rozwiązanie nie jest powszechnie znane?
ESR
10

Musiałem zrobić coś podobnego. Oto uproszczona wersja tego, co dla mnie zadziałało. Jeśli zostanie wykryte zdarzenie dotykowe, usuń powiązanie kliknięcia.

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

  //your code here
});

W moim przypadku zdarzenie click było powiązane z <a>elementem, więc musiałem usunąć powiązanie click i ponownie powiązać zdarzenie click, które uniemożliwiło domyślną akcję dla tego <a>elementu.

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

  //your code here
});
Tim
źródło
Chciałbym, żeby to działało tak, jak jest, ale tak nie jest. $ (this) nie wskazuje na to, co według ciebie działa.
Dave Lowerre
8

Udało mi się w następujący sposób.

Bułka z masłem...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});
Hasanavi
źródło
4
czy to nie uruchomi się dwukrotnie na urządzeniach, które rejestrują zarówno kliknięcie, jak i start-touch?
DA.
8
Przepraszam, dałem ci znać. Tak, masz rację, dotknięcie wygeneruje zdarzenie touchstart, a także zdarzenie click. Nazywa się to kliknięciem ducha. Google wdrożyło do tego rozwiązanie. Może nie jest prosty, ale działa idealnie. Tutaj jest link. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi
2
może być trywialne, ale brakuje eparametru wfunction()
ProblemsOfSumit
@Hasanavi to była świetna poprawka, ale znalazłem lepsze wyniki, używając .on
Neil
1
Dzięki @Neil. Tak, użycie .on jest lepszym pomysłem, ponieważ można go usunąć w przyszłej wersji. Zmieniłem mój przykład z bind na on.
Hasanavi
5

Uważam, że najlepszą praktyką jest teraz stosowanie:

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

touchend

Zdarzenie dotknięcia jest uruchamiane, gdy punkt styku zostanie usunięty z powierzchni styku.

Zdarzenie dotknięcia nie wywoła żadnych zdarzeń myszy.


mouseup

Zdarzenie mouseup jest wysyłane do elementu, gdy wskaźnik myszy znajduje się nad elementem, a przycisk myszy jest zwolniony. Każdy element HTML może otrzymać to zdarzenie.

Zdarzenie myszy nie wywoła żadnych zdarzeń dotyku.

PRZYKŁAD

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


EDYCJA (2017)

Od 2017 r. Przeglądarki zaczynające się od Chrome i podejmujące kroki w kierunku zdarzenia kliknięcia .on("click") bardziej zgodnym zarówno dla myszy, jak i dotyku, eliminując opóźnienie generowane przez zdarzenia stuknięcia w żądaniach kliknięcia.

Prowadzi to do wniosku, że powrót do korzystania ze zdarzenia „click” byłoby najprostszym rozwiązaniem w przyszłości.

Nie przeprowadziłem jeszcze żadnych testów w różnych przeglądarkach, aby sprawdzić, czy jest to praktyczne.

DreamTeK
źródło
Wydawało mi się to obiecujące, więc pomieszałem z nim kilka, ale ostatecznie okazało mouseupsię, że mam nieprzewidywalne wyniki, szczególnie gdy używam gładzika zamiast tradycyjnej myszy.
skybondsor
4

Zasadniczo nie chcesz mieszać domyślnego interfejsu dotykowego i bezdotykowego (kliknięcia). Po przejściu do świata dotyku łatwiej jest poradzić sobie tylko z funkcjami dotykowymi. Poniżej znajduje się pseudo kod, który zrobiłby to, co chcesz.

Jeśli połączysz się w zdarzeniu touchmove i śledzisz lokalizacje, możesz dodać więcej elementów w funkcji doTouchLogic, aby wykryć gesty i tak dalej.

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
     }
}
Justin808
źródło
Myślę, że to sedno pytania: czy w ogóle sens ma próba obsługi obu modeli za pomocą jednej bazy kodu? Zaktualizuję moje pytanie o kilka innych scenariuszy.
DA.
1
„Zasadniczo nie chcesz mieszać domyślnego dotyku i bezdotyku” po przejściu przez to, zgadzam się. Problem polega na tym, że wciąż produkują urządzenia dotykowe z klawiaturami, co jest dla nas programistów problemem.
DA.
Zgadzam się, że o wiele łatwiej byłoby na początku podjąć ostateczną decyzję dotykową lub bezdotykową. Cóż to byłby za łatwy świat! Ale co to jest z tymi przeklętymi urządzeniami rozsądnie klikającymi i dotykającymi? Czy z tego powodu warto mieć te wszystkie bóle głowy? Czy to przyszłość, o którą zadaję sobie pytanie?
Garavani
Nie podoba mi się pomysł posiadania wielu definicji funkcji. Moja firma pracuje obecnie nad projektem, w którym iPad nie jest specjalnie traktowany jako urządzenie mobilne, a Ty nadal potrzebujesz wydarzenia dotykowego. Ale touchend nie działa na normalnych wersjach komputerowych. Tak więc rozwiązanie @mottie jest absolutnie w porządku dla tego scenariusza.
Pete,
3

Cóż ... Wszystko to jest bardzo skomplikowane.

Jeśli masz modernizatora, nie musisz się zastanawiać.

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

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});
harelpls
źródło
2
Czy możesz wyjaśnić, co to robi? Uważam, że sprawdza, czy urządzenie obsługuje dotyk, a jeśli nie, używa kliknięcia. Problemem jest (a przynajmniej tak było , kiedy pisałem pytanie), że niektóre urządzenia obsługują oba. I na tych urządzeniach nadal muszę obsługiwać oba zdarzenia, ponieważ wyzwalacz może pochodzić z dotyku lub kliknięcia.
DA.
2
Polecam trochę więcej wyjaśnień.
Aaron Hall
2

Kolejne wdrożenie dla lepszej konserwacji. Jednak ta technika wykona również event.stopPropagation (). Kliknięcie nie jest rejestrowane w żadnym innym elemencie, który klikał przez 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()) {
      ...
   }
}
Mathieu Hamel
źródło
2

Tylko w celach dokumentacyjnych, oto co zrobiłem dla najszybszego / najbardziej responsywnego kliknięcia na komputerze / dotknij rozwiązania mobilnego, o którym mogłem pomyśleć:

Zastąpiłem onfunkcję jQuery zmodyfikowaną, która za każdym razem, gdy przeglądarka obsługuje zdarzenia dotykowe, zamieniła wszystkie moje zdarzenia kliknięcia na start dotykowy.

$.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);
        };
    }()),
});

Użycie, które byłoby dokładnie takie samo jak poprzednio, takie jak:

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

Ale użyłby touchstart, gdy jest dostępny, kliknij, gdy nie. Żadne opóźnienia nie są potrzebne: D

Gerson Goulart
źródło
1
Haczykiem z tym byłyby urządzenia obsługujące ZARÓWNO DOTYKOWE klawiatury, w których musisz upewnić się, że akceptujesz obie interakcje.
DA.
Dobra obserwacja @DA. Dodałem zaznaczenie, aby zastąpić zdarzenie kliknięcia tylko wtedy, gdy nie używasz żadnego zdarzenia dotyku w połączeniu z nim. Stil nie będzie rozwiązaniem dla każdego projektu, ale jestem pewien, że byłby odpowiedni dla wielu.
Gerson Goulart,
2

Właśnie wpadłem na pomysł, aby zapamiętać, czy ontouchstartkiedykolwiek został uruchomiony. W tym przypadku jesteśmy na urządzeniu, które je obsługuje i chcemy zignorować onclickzdarzenie. Ponieważ ontouchstartzawsze powinno być uruchamiane wcześniej onclick , używam tego:

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

jakob.j
źródło
1
Ponieważ użytkownicy mogą korzystać z dotyku i myszy podczas tej samej wizyty na stronie, problem z tym fragmentem polega na tym, że rejestruje flagę touchAvailable raz zamiast na zdarzenie. Wtyczka jQuery z stackoverflow.com/a/26145343/328272 działa tak samo jak kod, ale może stwierdzić zdarzenie myszy, czy zostało wywołane przez dotyk lub mysz na podstawie zdarzenia. Nie tylko tj. po kliknięciu, ale także po zwolnieniu myszy, które mogą być uruchamiane w sekundach (lub minutach) po centrum myszy (idealne dla wysuwanych menu, które powinny się rozwijać po najechaniu myszką gestem myszy lub kliknięciem podczas korzystania z dotyku).
lmeurs
2

Możesz spróbować w ten sposób:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
Sky Yip
źródło
co z urządzeniami obsługującymi dotyk i mysz
Walle Cyril
2

W moim przypadku działało to idealnie:

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

Główny problem polegał na tym, że zamiast klikania myszką próbowałem za pomocą kliknięcia, na urządzeniach dotykowych wywoływałem kliknięcie i dotknięcie w tym samym czasie, jeśli korzystam z wyłączenia, niektóre funkcje w ogóle nie działały na urządzeniach mobilnych. Problem z kliknięciem polega na tym, że jest to globalne wydarzenie, które uruchamia resztę wydarzenia, w tym retusz.

Pepita Ronderos
źródło
1

To działało dla mnie, mobile słucha obu, więc zapobiegaj temu, który jest zdarzeniem dotykowym. słuchaj tylko na pulpicie myszy.

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

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

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });
Levarne Sobotker
źródło
1

Będąc dla mnie najlepszą odpowiedzią udzieloną przez Mottie, po prostu staram się, aby jego kod był bardziej przydatny, więc oto mój wkład:

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;
});
Arco Voltaico
źródło
0

Pracuję również nad aplikacją internetową na Androida / iPada i wydaje się, że samo użycie „touchmove” wystarczy, aby „przenieść komponenty” (nie trzeba dotykać). Wyłączając touchstart, możesz użyć .click (); z jQuery. W rzeczywistości działa, ponieważ nie został przeciążony przez touchstart.

Na koniec możesz binb .live („touchstart”, funkcja (e) {e.stopPropagation ();}); aby poprosić zdarzenie touchstart, aby przestało się propagować, w salonie kliknij (), aby się uruchomić.

To zadziałało dla mnie.

nembleton
źródło
Jak byś wyłączył touchstart?
punkrockbuddyholly,
Wierzę, że „mógłbyś” zrobić coś takiego: jQuery („# my_element”). On ('touchstart', function (e) {e.preventDefault ()});
nembleton
0

Próbując rozwiązać ten problem, należy wziąć pod uwagę wiele kwestii. Większość rozwiązań albo przerywa przewijanie, albo nie obsługuje poprawnie zdarzeń kliknięcia ducha.

Pełne rozwiązanie można znaleźć na stronie https://developers.google.com/mobile/articles/fast_buttons

Uwaga: Nie można obsługiwać zdarzeń kliknięć duchów dla poszczególnych elementów. Opóźnione kliknięcie jest uruchamiane według lokalizacji na ekranie, więc jeśli zdarzenia dotykowe zmodyfikują stronę w jakiś sposób, zdarzenie kliknięcia zostanie wysłane do nowej wersji strony.

SystemParadox
źródło
0

Skuteczne może być przypisanie do zdarzeń 'touchstart mousedown'lub 'touchend mouseup'uniknięcie niepożądanych skutków ubocznych używania click.

Steven Lu
źródło
0

Korzystając z faktu, że kliknięcie zawsze następuje po zdarzeniu dotykowym, oto co zrobiłem, aby pozbyć się „kliknięcia ducha” bez konieczności używania limitów czasu lub flag globalnych.

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

Zasadniczo za każdym razem, gdy ontouchstartzdarzenie uruchamia się na elemencie, oflaguj zestaw, a następnie usuń (i zignoruj), gdy nadejdzie kliknięcie.

BigMacAttack
źródło
0

Dlaczego nie skorzystać z interfejsu API jQuery Event?

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

Z powodzeniem wykorzystałem to proste wydarzenie. Jest czysty, możliwy do nazewnictwa i wystarczająco elastyczny, aby go ulepszyć.

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
};
mdbiscan
źródło
1
Zaleca się stosowanie funkcji wykrywania funkcji zamiast wąchania agenta użytkownika.
jinglesthula
Haczyk to urządzenia obsługujące zarówno dotyk, jak i kliknięcie.
DA.
0

Jeśli używasz jQuery, działało to dla mnie całkiem dobrze:

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

Inne rozwiązania są również dobre, ale wiązałem wiele zdarzeń w pętli i potrzebowałem funkcji wywoływania, aby utworzyć odpowiednie zamknięcie. Ponadto nie chciałem wyłączać wiązania, ponieważ chciałem, aby można je było wywoływać przy następnym kliknięciu / uruchomieniu.

Może pomóc komuś w podobnej sytuacji!

Yusuf Bhabhrawala
źródło
0

W przypadku prostych funkcji wystarczy rozpoznać dotyk lub kliknąć Używam następującego kodu:

var element = $("#element");

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

To zwróci „dotyk”, jeśli zdefiniowano zdarzenie touchstart, i „brak dotyku”, jeśli nie. Tak jak powiedziałem, jest to proste podejście do zdarzeń typu „kliknij / dotknij”.

Rodrigo
źródło
0

Próbuję tego i jak dotąd działa (ale jestem tylko na Androidzie / Phonegap, więc zastrzeżenie 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...
  }

Nie podoba mi się fakt, że ponownie wiążę procedury obsługi za każdym dotknięciem, ale wszystkie rzeczy uważane za dotknięcia nie zdarzają się zbyt często (w czasie komputera!)

Mam TON programów obsługi, takich jak ten dla „#keypad”, więc posiadanie prostej funkcji, która pozwala mi poradzić sobie z problemem bez zbyt dużej ilości kodu, jest powodem, dla którego poszedłem w ten sposób.

Dave Lowerre
źródło
0

EDYTOWAĆ: Moja poprzednia odpowiedź (oparta na odpowiedziach w tym wątku) nie była dla mnie właściwą drogą. Chciałem, aby podmenu rozwijało się po kliknięciu myszą lub kliknięciu i zwinięciu po opuszczeniu myszy lub kliknięciu innym przyciskiem. Ponieważ zdarzenia myszy zwykle są uruchamiane po zdarzeniach dotykowych, pisanie detektorów zdarzeń obsługujących jednocześnie ekran dotykowy i mysz było trochę trudne.

Wtyczka jQuery: Touch Or Mouse

W końcu napisałem wtyczkę jQuery o nazwie „ Touch Or Mouse ” (897 bajtów zminimalizowane), która może wykryć, czy zdarzenie zostało wywołane przez ekran dotykowy lub mysz (bez testowania obsługi dotykowej!). To umożliwia obsługę zarówno ekran dotykowy i myszy w tym samym czasie i całkowicie oddziela ich zdarzenia.

W ten sposób OP może używać touchstartlub touchenddo szybkiego reagowania na kliknięcia dotykowe oraz clickna kliknięcia wywoływane tylko myszą.

Demonstracja

Najpierw trzeba zrobić tj. element ciała śledzi zdarzenia dotykowe:

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

Zdarzenia myszy są domyślnie powiązane z elementami, a dzwoniąc $body.touchOrMouse('get', e)możemy dowiedzieć się, czy zdarzenie zostało wywołane za pomocą ekranu dotykowego czy myszy.

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

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

Zobacz wtyczkę w pracy pod adresem http://jsfiddle.net/lmeurs/uo4069nh .

Wyjaśnienie

  1. Wtyczka ta musi zostać wywołana tj. element body do śledzenia touchstarti touchendzdarzenia, w ten sposób touchendzdarzenie nie musi być uruchamiane na elemencie wyzwalającym (tj. łączu lub przycisku). Pomiędzy tymi dwoma zdarzeniami dotykowymi wtyczka uważa, że ​​każde zdarzenie myszy jest wywoływane dotykiem.
  2. Zdarzenia myszy są uruchamiane dopiero po tym touchend, gdy zdarzenie myszy jest uruchamiane w ciągu ghostEventDelay(opcja, domyślnie 1000 ms) później touchend, ta wtyczka uznaje zdarzenie myszy za wywołane dotykiem.
  3. Po kliknięciu elementu za pomocą ekranu dotykowego element osiąga :activestan. mouseleaveZdarzenie jest zwolniony dopiero po element traci ten stan przez IE. klikając inny element. Ponieważ może mouseenterto potrwać kilka sekund (lub minut!) Po uruchomieniu zdarzenia, wtyczka ta śledzi ostatnie mouseenterzdarzenie elementu : jeśli ostatnie mouseenterzdarzenie zostało wywołane dotykiem, następujące mouseleavezdarzenie jest również wywoływane przez dotyk.
lmeurs
źródło
0

Oto prosty sposób, aby to zrobić:

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

Zasadniczo zapisujesz pierwszy typ zdarzenia, który jest wyzwalany we właściwości „trigger” w obiekcie danych jQuery, który jest dołączony do dokumentu głównego, i wykonujesz go tylko wtedy, gdy typ zdarzenia jest równy wartości w „trigger”. Na urządzeniach dotykowych łańcuchem zdarzeń będzie prawdopodobnie „start dotykowy”, a następnie „kliknięcie”; jednak procedura „kliknięcia” nie zostanie wykonana, ponieważ „kliknięcie” nie odpowiada początkowemu typowi zdarzenia zapisanego w „wyzwalaczu” („touchstart”).

Zakładam, i uważam, że jest to bezpieczne, że smartfon nie zmieni się spontanicznie z urządzenia dotykowego na urządzenie myszy, w przeciwnym razie stuknięcie nigdy się nie zarejestruje, ponieważ typ zdarzenia „wyzwalającego” jest zapisywany tylko raz na ładowanie strony i „kliknięcie” nigdy nie pasują do „startu dotykowego”.

Oto kodepen, z którym możesz się bawić (spróbuj nacisnąć przycisk na urządzeniu dotykowym - nie powinno być opóźnienia kliknięcia): http://codepen.io/thdoan/pen/xVVrOZ

Zaimplementowałem to również jako prostą wtyczkę jQuery, która obsługuje również filtrowanie potomków jQuery, przekazując ciąg selektora:

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

thdoan
źródło
Dlaczego użyłeś właściwości danych w dokumencie zamiast samej zmiennej?
Raphael Schweikert
@RaphaelSchweikert Prawdopodobnie po prostu z przyzwyczajenia. Nie ma dobrego lub złego sposobu, ale lubię używać $.data()do przechowywania danych, do których można uzyskać dostęp za pomocą wielu funkcji, ale nie należą one do zakresu globalnego. Plusem jest to, że ponieważ przechowuję go w elemencie, naturalnie zapewnia mi to większy kontekst.
thdoan
0

Najlepszą metodą, jaką znalazłem, jest zapisanie zdarzenia dotyku i zaprogramowanie go jako zdarzenia normalnego kliknięcia. W ten sposób masz wszystkie normalne zdarzenia kliknięcia, a następnie musisz dodać tylko jedną procedurę obsługi zdarzeń dla wszystkich zdarzeń dotykowych. Dla każdego węzła, który chcesz, aby można go było dotykać, po prostu dodaj do niego klasę „dotykalną”, aby wywołać procedurę obsługi dotykowej. Z Jquery działa tak z pewną logiką, aby upewnić się, że jest to prawdziwe zdarzenie dotykowe, a nie fałszywie pozytywne.

$("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
});
Mahatrsna
źródło