2013-07-15 9 views
8

Sto provando a gestire un clic del pulsante. Il "pulsante" è un div personalizzato che può contenere anche bambini. La richiamata clic deve essere attivata quando l'utente ha fatto clic e rilasciato all'interno dell'elemento. Se l'utente fa clic all'interno e trascina e rilascia all'esterno, il gestore non deve attivarsi. Ho bisogno di farlo funzionare su desktop e mobile, quindi sto usando gli eventi mousedown/mouseup e touchstart/touchend.Come rilevare un evento touchend al di fuori di un elemento

ho bisogno di cambiare la classe pulsante da "premuto" a "normale", anche se l'utente rilascia al di fuori, quindi ho bisogno di aggiungere un listener per catturare l'evento rilasciando il documento:

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

E ' funziona bene con i clic, ma non funziona come previsto con gli eventi touch. Ho provato questo in Chrome per Android e quando si preme sopra l'elemento, quindi trascinando fuori, il "Inside!" l'avviso è mostrato.

Il problema è in questa condizione:

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

sto cercando di verificare se l'evento si è verificato in touchend il pulsante o uno qualsiasi dei bambini. Se il pulsante non ha figli, quando fai clic su di esso e poi rilasciando all'esterno, la prima parte della clausola OR si risolve in true. Se il pulsante ha figli e faccio clic sui bambini, quindi rilasciamo in qualsiasi altra parte dello schermo, la seconda parte della clausola è vera.

Cosa c'è di sbagliato con questo codice?

Grazie in anticipo.

UPDATE
L'ho provato anche in BlackBerry 10 con gli stessi risultati. Apparentemente l'obiettivo per l'evento touchend è il pulsante (oi figli), anche quando ho fatto clic all'esterno. È così che dovrebbe funzionare?

UPDATE
Ed ecco perché. Questo è what the W3C docs say about the event:

L'obiettivo di questo evento deve essere lo stesso elemento che ha ricevuto l'evento TouchStart quando questo punto di contatto è disposto sulla superficie, anche se il punto di contatto è poi trasferito fuori area interattiva del elemento bersaglio.

Non ha senso per me, ma in ogni caso questo spiega il mio problema. Supponevo che l'evento touchend sarebbe stato chiamato sull'elemento in cui è stato rilasciato il dito. Sarebbe possibile rilevare il rilascio delle dita all'esterno utilizzando un approccio diverso?

UPDATE
Ho cercato di ottenere le coordinate della touchevent per ottenere l'elemento di là usando document.elementFromPoint. Il problema è che questo evento non contiene le coordinate (gli elenchi changedTouches e touches sono vuoti). Ho esaminato attentamente l'evento nel debugger e non c'è modo di recuperarne altri da quelli dell'evento originale. Poi ho pensato che potevo memorizzare l'ultimo evento touchmove e ottenere le corde da lì, ma ancora una volta questo evento non ha le proprie coordinate! Perché sulla Terra esistono questi due eventi quando sono inutili? E ho testato questo approccio su iOS, Android e BB10 con risultati identici.

Mi sono arreso. Chiamerò la richiamata anche se l'utente ha fatto clic all'esterno.Non posso credere che siano ragazzi del 2013 e non c'è un modo (semplice) per farlo? Potrei usare il drag & drop api ma in base a this non è supportato su dispositivi mobili (Gotta love mobile web development).

+0

Anche se potrebbe non essere di aiuto, ho creato uno script VanillaJS che fa praticamente la stessa cosa e ha pubblicato una versione semplificata di quel codice [qui] (http://stackoverflow.com/questions/13030210/onclick-event-is-occuring-due-times-in-iphone-hybrid-app/13030622 # 13030622) –

+0

@EliasVanOotegem grazie, ho letto su di esso, ma non ho potuto ancora provarlo. Sto leggendo la fonte di alcune delle librerie che hai collegato. Forse sto cercando di reinventare la ruota qui. –

risposta

8

Infine (quasi) ha funzionato in BB10 e Android. Ho lasciato una scia di problemi negli aggiornamenti delle domande, quindi riassumendo: l'obiettivo dell'evento touchend è lo stesso del touchstart e viene fornito senza coordinate (il progettista dell'API per qualche motivo ha deciso di "nasconderlo" nello originalEvent proprietà :) . Quindi sto recuperando le coordinate di fine tocco da changedTouches[0].pageX e changedTouches[0].pageY e quindi ottenendo l'elemento in cui il dito viene rilasciato utilizzando document.elementFromPoint. (Prima di alimentare questo metodo con le coordinate dell'evento, è necessario aggiungere l'offset di scorrimento a x e y). Quindi se l'elemento in quel punto è il pulsante (o ne è figlio), gestisco il clic.

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

Ora ho un nuovo problema: in iOS in qualche modo gli eventi di clic vengono duplicati. Ma questo merita una domanda diversa (se non già esistente).

+1

Il problema dei clic duplicati è stato causato dagli avvisi di debug. Li ho sostituiti con 'console.log' e ora funziona come previsto. –

+0

Questo è orribile! Condivido il tuo dolore. –

+0

Le coordinate dovrebbero essere in un oggetto in un elenco chiamato targetTouches che è una proprietà dell'oggetto evento, anche se ho sentito blackberry può essere un incubo sul supporto spec così? Là. Questo è necessario perché potresti voler gestire più tocchi sullo stesso elemento di origine che può generare eventi molto vicini l'uno all'altro. È strano, ma l'API ha molto più senso se si considera che è progettato per gestire molto più delle dita come problemi di sostituzione del mouse ed è un buon progetto, IMO. La libreria di go-to per astrarre le cose di basso livello dovrebbe emergere abbastanza presto. –

1

È necessario un approccio diverso e più ordinato.

Implementare onmousemove e ontouchmove rispettivamente per desktop e dispositivi mobili.

btn_obj.onmousemove = CancelOnClick (this); 

Implementare la funzione CancelOnClick per impostare il flag da premuto a normale. Ti salva da tutti gli elementi e dalle condizioni di base dei bambini.

Spero che questo aiuti.

+0

'CancelOnClick (this);' assegnerà il valore di ritorno di 'CancelOnClick' all'evento 'onmousemove', è una pessima idea –

+1

Questo non risolve il problema di rilevare il rilascio dei clic sull'elemento. il problema è che ho bisogno di chiamare il callback quando termina il clic, non quando inizia. –

Problemi correlati