2013-07-15 14 views
8

Ich versuche, mit einem Klick auf die Schaltfläche zu behandeln. Der "Button" ist ein benutzerdefiniertes div, das auch Kinder enthalten kann. Der Klick-Callback sollte ausgelöst werden, wenn der Benutzer im Element geklickt und freigegeben hat. Wenn der Benutzer in das Feld klickt und es anschließend nach außen zieht und freigibt, sollte der Handler nicht auslösen. Ich muss das auf dem Desktop und Mobile arbeiten lassen, daher verwende ich mousedown/mouseup und touchstart/touchend Ereignisse.How to touchend Ereignis außerhalb eines Elements

Ich brauche die Schaltfläche Klasse von „gedrückt“ auf „normal“ auch wenn der Benutzer Mitteilungen außerhalb ändern, also muss ich einen Listener hinzufügen, das die Freigabe Ereignis über das Dokument erfassen:

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

Es funktioniert gut mit Klicks, aber es funktioniert nicht wie erwartet mit Berührungsereignissen. Ich habe das in Chrome für Android getestet und wenn du über das Element drückst, ziehst du das "Inside!" Alarm wird angezeigt.

Das Problem ist in diesem Zustand:

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

Ich versuche, ob das touchend zu überprüfen Ereignis in der Taste oder einen der Kinder stattgefunden hat. Wenn die Schaltfläche keine untergeordneten Elemente enthält, wird der erste Teil der OR-Klausel in true aufgelöst, wenn Sie darauf klicken und dann nach außen freigeben. Wenn die Schaltfläche untergeordnete Elemente enthält und ich in die untergeordneten Objekte klicke, wird in jedem anderen Teil des Fensters der zweite Teil der Klausel aktiviert.

Was ist falsch an diesem Code?

Vielen Dank im Voraus.

UPDATE
Ich habe es auch in BlackBerry 10 mit den gleichen Ergebnissen getestet. Offensichtlich ist das Ziel für das Ereignis touchend der Knopf (oder die Kinder), selbst wenn ich draußen geklickt habe. Soll es so funktionieren?

UPDATE
Und hier ist warum. Dies ist what the W3C docs say about the event:

Das Ziel dieser Veranstaltung muß das gleiche Element sein, das das Berührungsstartereignis empfing, wenn dieser Berührungspunkt auf der Oberfläche platziert wurde, auch wenn der Berührungspunkt, da außerhalb des interaktiven Bereichs der bewegt hat Zielelement.

Es ergibt keinen Sinn für mich, aber das erklärt mein Problem. Ich nahm an, dass das Ereignis touchend über dem Element aufgerufen würde, wo der Finger freigegeben wurde. Wäre es möglich, die Fingerauslösung mit einem anderen Ansatz zu erkennen? die Koordinaten des touchevent zu bekommen, um dort das Element zu erhalten mit document.elementFromPoint

UPDATE
Ich habe versucht. Das Problem ist, dass dieses Ereignis keine Koordinaten enthält (die Listen changedTouches und touches sind leer). Ich habe das Ereignis im Debugger gründlich untersucht und es gibt keine Möglichkeit, andere Coords als die ursprünglichen Ereignisse wiederherzustellen. Dann dachte ich, ich könnte die letzte touchmove Veranstaltung zwischenspeichern und die Koordinaten von dort bekommen, aber wieder hat diese Veranstaltung keine eigenen Koordinaten! Warum auf der Erde existieren diese beiden Ereignisse, wenn sie nutzlos sind? Und ich habe diesen Ansatz in iOS, Android und BB10 mit identischen Ergebnissen getestet.

Ich habe aufgegeben. Ich rufe den Rückruf an, auch wenn der Benutzer nach draußen klickt.Ich kann nicht glauben, dass es 2013 Jungs sind und es gibt keine (einfache) Möglichkeit, dies zu tun? Ich könnte den Drag & drop api verwenden, aber laut this ist es auf dem Handy nicht unterstützt (Ich liebe mobile Web-Entwicklung).

+0

Obwohl es dir vielleicht nicht direkt helfen kann, habe ich ein VanillaJS-Skript zusammengestellt, das ziemlich genau dasselbe macht, und eine vereinfachte Version dieses Codes [hier] veröffentlicht (http://stackoverflow.com/questions/13030210/onclick-event-is-occuring-zwei-mal-in-iphone-hybrid-app/13030622 # 13030622) –

+0

@EliasVanOotegem danke, ich habe es gelesen, aber ich konnte es noch nicht testen. Ich lese jetzt die Quelle einiger der Bibliotheken, die Sie verlinkt haben. Vielleicht versuche ich das Rad hier neu zu erfinden. –

Antwort

8

Endlich (fast) hat es funktioniert in BB10 und Android. Ich habe eine Spur von Problemen in den Fragen Updates, so zusammengefasst: die touchend Ereignisziel ist das gleiche wie die touchstart, und es kommt ohne Koordinaten (die API-Designer aus irgendeinem Grund entschieden, sie in der originalEvent zu "verstecken" Eigentum :) . Also lade ich die Touch-End-Koordinaten von changedTouches[0].pageX und changedTouches[0].pageY ab und erhalte dann das Element, bei dem der Finger losgelassen wird, unter Verwendung von document.elementFromPoint. (Bevor Sie diese Methode mit den Ereigniskoordinaten füttern, müssen Sie den Scroll-Offset zu x und y hinzufügen). Wenn das Element an diesem Punkt die Schaltfläche ist (oder ein Kind davon ist), handle ich mit dem Klick.

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

Jetzt habe ich ein neues Problem: in iOS werden die Click-Ereignisse irgendwie dupliziert. Aber das verdient eine andere Frage (wenn nicht schon vorhanden).

+1

Das Problem mit doppelten Klicks wurde durch Debugging-Warnungen verursacht. Ich ersetzte sie durch 'console.log' und funktioniert jetzt wie erwartet. –

+0

Das ist schrecklich! Ich fühle deinen Schmerz. –

+0

Die Koordinaten sollten in einem Objekt in einer Liste namens targetTouches sein, das eine Eigenschaft des Ereignisobjekts ist, obwohl ich Blackberry gehört habe, kann Albtraum auf Spezifikationsunterstützung so sein? Dort. Dies ist notwendig, da Sie möglicherweise mehrere Berührungen am selben Elementursprung behandeln möchten, die Ereignisse in sehr enger Nähe zueinander erzeugen können. Es ist komisch, aber die API macht viel mehr Sinn, wenn man bedenkt, dass es viel mehr als Finger als Mausersatz-Probleme behandelt und ein gutes Design ist, IMO. Die Go-to-Library, um das Low-Level-Zeug zu abstrahieren, sollte bald auftauchen. –

1

Sie brauchen einen anderen und saubereren Ansatz.

Implementieren Sie onmousemove und ontouchmove für Desktop- bzw. mobile Geräte.

btn_obj.onmousemove = CancelOnClick (this); 

Implementieren Sie die CancelOnClick -Funktion, um das Flag von gedrückt auf normal zu setzen. Es speichert Sie von all Ihren Elementen und deren Kind-basierten Bedingungen.

Hoffe, dass hilft.

+0

'CancelOnClick (this);' wird dem 'onmousemove'-Ereignis den Rückgabewert von' CancelOnClick' zuweisen, was eine schreckliche Idee ist. –

+1

Dies betrifft nicht das Problem, die Klickfreigabe über das Element zu erkennen. Das Problem ist, ich muss den Rückruf aufrufen, wenn der Klick beendet ist, nicht wenn es startet. –