2013-07-15 16 views
8

Próbuję obsłużyć kliknięcie przycisku. "Przycisk" to niestandardowy element div, który może również zawierać elementy podrzędne. Kliknięcie zwrotne powinno wystrzelić, gdy użytkownik kliknie i zwolni element wewnątrz elementu. Jeśli użytkownik kliknie w środku, a następnie przeciągnie i zwalnia na zewnątrz, przewodnik nie powinien strzelać. Muszę uruchomić to zarówno na komputerze stacjonarnym, jak i mobilnym, dlatego używam zdarzeń mousedown/mouseup i touchstart/touchend.Jak wykryć zdarzenie touchend poza elementem

muszę zmienić klasę przycisk z „wciśnięty” do „normalnego”, nawet jeśli użytkownik zwalnia na zewnątrz, więc trzeba dodać detektor uchwycić uwalniający imprezę nad dokumentem:

var $myElement = $("#myelement"); //This is a div and can also contain children. 

    $myElement.on("mousedown touchstart", function(e){ 

     $(this).addClass("pressed");  

     $(document).on("mouseup touchend", function listener(e){ 
      $myElement.removeClass("pressed"); 

      $(document).off("mouseup touchend", listener); 

      var $target = $(e.target); 
      if ($target.is($myElement) || $target.parents().is($myElement)){   
       //FIXME 
       alert("Inside!"); 

       //do something here 
       return false; 
      } else { 
       //FIXME 
       alert("Outside!"); 

       //let event bubble 
      } 
     }); 

     return false; 
    }); 

It działa dobrze z kliknięciami, ale nie działa zgodnie z oczekiwaniami w przypadku zdarzeń dotykowych. Testowałem to w Chrome na Androida i naciskając na element, a następnie przeciągając, "Wewnątrz!" alert jest wyświetlany.

Problem jest w tym stanie:

if ($target.is($myElement) || $target.parents().is($myElement)){ 

Próbuję sprawdzić, czy zdarzenie wystąpiło w touchend przycisku lub którykolwiek z dziećmi. Jeśli przycisk nie ma dzieci, po kliknięciu, a następnie zwolnieniu na zewnątrz, pierwsza część klauzuli OR staje się prawdą. Jeśli przycisk ma dzieci i klikam w dzieci, a następnie zwalniam w dowolnej innej części ekranu, druga część klauzuli jest prawdziwa.

Co jest nie tak z tym kodem?

Dzięki z góry.

AKTUALIZACJA
Testowałem to również w BlackBerry 10 z takimi samymi wynikami. Najwyraźniej celem zdarzenia touchend jest przycisk (lub dzieci), nawet gdy kliknąłem na zewnątrz. Czy to ma działać?

UPDATE
A oto dlaczego. To what the W3C docs say about the event:

Celem tego wydarzenia musi być ten sam element, który otrzymał zdarzenie touchstart gdy punkt ten dotyk był umieszczony na powierzchni, nawet jeśli punkt dotykowy od tego czasu przeniósł się poza interaktywnej Powierzchnia element docelowy.

Nie ma dla mnie sensu, ale w każdym razie to wyjaśnia mój problem. Zakładałem, że zdarzenie o nazwie touchend zostanie wywołane przez element, w którym palec został zwolniony. Czy byłoby możliwe wykrycie uwolnienia palca na zewnątrz przy użyciu innego podejścia?

UPDATE
Próbowałem dostać współrzędne touchevent dostać się tam za pomocą elementu document.elementFromPoint. Problem polega na tym, że to zdarzenie nie zawiera współrzędnych (listy changedTouches isą puste). Dokładnie sprawdziłem zdarzenie w debugerze i nie ma sposobu na odzyskanie współrzędnych z niego innych niż oryginalne wydarzenie. Pomyślałem, że mogę zapisać ostatnie wydarzenie touchmove i pobrać współrzędne, ale znowu to zdarzenie nie ma własnych współrzędnych! Dlaczego, na Boga, te dwa wydarzenia istnieją, gdy są bezużyteczne? I testowałem to podejście w iOS, Android i BB10 z identycznymi wynikami.

Poddałem się. Zadzwonię do oddzwonienia, nawet jeśli użytkownik kliknął na zewnątrz.Nie mogę uwierzyć, że to faceci z 2013 roku i nie ma (prostego) sposobu na zrobienie tego? Mógłbym użyć funkcji przeciągania & drop api, ale zgodnie z this jest ona nieobsługiwana na urządzeniach mobilnych (Uwielbiam rozwój aplikacji mobilnych).

+0

Chociaż może to nie pomóc ci bezpośrednio, stworzyłem skrypt VanillaJS, który robi to samo i opublikował uproszczoną wersję tego kodu [tutaj] (http://stackoverflow.com/questions/13030210/onclick-event-is-occuring-two-times-in-iphone-hybrid-app/13030622 # 13030622) –

+0

@EliasVanOotegem dzięki, przeczytałem go, ale nie mogłem go jeszcze przetestować. Teraz czytam źródło niektórych bibliotek, które połączyłeś. Może próbuję wymyślić tutaj koło. –

Odpowiedz

8

Wreszcie (prawie) sprawdziło się w BB10 i Androidzie. Pozostawiłem ślad problemów w aktualizacjach pytań, więc podsumowując: cel zdarzenia touchend jest taki sam, jak touchstart, i przychodzi bez współrzędnych (projektant API z jakiegoś powodu zdecydował się "ukryć" je w originalEvent własność :) . Tak więc odczytywam współrzędne końca ekranu od changedTouches[0].pageX i changedTouches[0].pageY, a następnie uzyskuję element, w którym palec jest zwolniony przy użyciu document.elementFromPoint. (Przed podaniem tej metody ze współrzędnymi zdarzenia należy dodać przesunięcie przewijania do x i y). Następnie, jeśli elementem w tym miejscu jest przycisk (lub jest to dziecko), obsługuję kliknięcie.

var $myElement = $("#myelement"); //This is a div and can also contain children. 
    var startEvent = touchDevice() ? "touchstart" : "mousedown"; 
    var releaseEvent = touchDevice() ? "touchend" : "mouseup"; 

    $myElement.on(startEvent, function(e){ 

     $(this).addClass("pressed");  

     $(document).on(releaseEvent, function listener(e){ 
      $myElement.removeClass("pressed"); 

      $(document).off(releaseEvent, listener); 

      var $target = null; 
      if(e.type === "mouseup"){ 
       $target = $(e.target);   
      } else {    
       var x = e.originalEvent.changedTouches[0].pageX - window.pageXOffset; 
       var y = e.originalEvent.changedTouches[0].pageY - window.pageYOffset; 
       var target = document.elementFromPoint(x, y); 

       if(target){ 
        $target = $(target); 
       }   
      } 

      if ($target !== null && ($target.is($myElement) || $target.parents().is($myElement))){  
       //FIXME 
       alert("Inside!"); 

       //callback here 
       return false; 
      } else { 
       //FIXME 
       alert("Outside!"); 
      } 
     }); 

     return false; 
    }); 

Teraz mam nowy problem: w iOS jakoś zdarzenia kliknięcia są dublowane. Ale to zasługuje na inne pytanie (jeśli jeszcze nie istnieje).

+1

Problem powielonych kliknięć został spowodowany przez alerty debugowania. Zamieniłem je na 'console.log' i teraz działam zgodnie z oczekiwaniami. –

+0

To jest okropne! Czuję twój ból. –

+0

Współrzędne powinny znajdować się w obiekcie na liście o nazwie targetTouches, która jest właściwością obiektu zdarzenia, chociaż słyszałem, że blackberry może być koszmarem przy pomocy specyfikacji, więc? tam. Jest to konieczne, ponieważ możesz chcieć obsłużyć wielokrotne dotknięcia przy tym samym pochodzeniu elementu, które może generować zdarzenia w bardzo bliskim sąsiedztwie. To dziwne, ale API ma dużo więcej sensu, jeśli wziąć pod uwagę, że zaprojektowano go tak, aby obsługiwał znacznie więcej niż palec jako problem zastępczy myszy i jest dobrym projektem, IMO. Biblioteka podręczna do abstrakcji rzeczy niskiego poziomu powinna wkrótce pojawić się na rynku. –

1

Potrzebne jest inne, bardziej przyjazne podejście.

Zaimplementuj onmousemove i ontouchmove odpowiednio dla komputerów i urządzeń przenośnych.

btn_obj.onmousemove = CancelOnClick (this); 

Zaimplementuj funkcję CancelOnClick, aby ustawić flagę na normalną. To oszczędza cię od całego twojego elementu i jego warunków opartych na dzieciach.

Nadzieję, że pomaga.

+0

'CancelOnClick (this);' przydzieli zwracaną wartość 'CancelOnClick' do zdarzenia' onmousemove', to jest okropny pomysł –

+1

Nie rozwiązuje problemu wykrywania kliknięcia na elemencie. Problem polega na tym, że muszę wywołać wywołanie zwrotne, gdy kliknięcie się kończy, a nie kiedy się zaczyna. –