2012-05-08 3 views
7

Sto sviluppando un'applicazione PhoneGap per Android utilizzando Jquery e Jquery Mobile.jQuery chiama evento click dopo evento taphold

Ho un elenco di elementi che necessitano di due eventi legati a ciascun elemento della lista. Ho bisogno di un evento "taphold" e di un "click". Il problema che sto avendo è quando faccio un "taphold", l'evento corretto "taphold" è sparato. Tuttavia, non appena rilascio, viene attivato anche l'evento click. Come posso evitare che l'evento click venga attivato dopo un taphold?

Codice:

function LoadMyItems(items) { 

for(var idx in items) 
{ 
    var itemLine = '<div class="my_item" id="my_item_'+items[idx].user_item_id+'">' + 
      '<img class="item_icon_32" src=./images/graphicFiles/Icon48/'+items[idx].item.graphic.graphicFiles.Icon48.filename+' />' + 
      items[idx].item.name+  
      '</div>'; 
    $('#my_list').append('<li>'+itemLine+'</li>'); 
     $('#my_item_'+items[idx].user_item_id).bind('taphold', {userItem:items[idx]},ShowMyItemInfo); 
     $('#my_item_'+items[idx].user_item_id).bind('click tap', {userItem:items[idx]},FitMyUpgradeItem); 
     console.log('UserItem '+items[idx].user_item_id+' loaded and events bound'); 
    } 
    $('#my_items_loader').hide(); 
    myScroll.refresh(); 
} 

Dopo i consigli di seguito, ecco cosa ho finito con. Funziona all'interno dell'oggetto iScroll.

function LoadMyItems(items) { 

for(var idx in items) 
{ 
    var itemLine = '<div class="my_item" id="my_item_'+items[idx].user_item_id+'">' + 
        '<img class="item_icon_32" src=./images/graphicFiles/Icon48/'+items[idx].item.graphic.graphicFiles.Icon48.filename+' />' + 
        items[idx].item.name+  
        '</div>'; 
    $('#my_list').append('<li>'+itemLine+'</li>'); 

    (function(index) { 
     var tapTime = 0; 
     var xPos = 0; 
     var yPos = 0; 
     $('#my_item_'+items[index].user_item_id).bind('vmousedown vmouseup', function (event) { 
      if (event.type == 'vmousedown') { 

       tapTime = new Date().getTime(); 
       xPos = event.pageX; 
       yPos = event.pageY; 

       var timer = setTimeout(function() { 
        var duration = (new Date().getTime() - tapTime); 
        var xDiff = Math.abs(mouseXPos - xPos); 
        var yDiff = Math.abs(mouseYPos - yPos); 
        if(duration >= 700 && (yDiff <= 40 || mouseXPos == 0)) 
         ShowItemInfo(items[index].item); 
       },750); 
      } else { 
       //event.type == 'vmouseup' 
       var duration = (new Date().getTime() - tapTime); 
       var xDiff = Math.abs(event.pageX - xPos); 
       var yDiff = Math.abs(event.pageY - yPos); 
       tapTime = new Date().getTime(); 
       if (duration < 699 && yDiff <= 40) { 
        //this is a tap 
        FitMyUpgradeItem(items[index]); 
       } 
      } 
     }); 

     $('#my_item_'+items[index].user_item_id).bind('touchmove',function(event) { 
      event.preventDefault(); 
     }); 
    })(idx); 

    console.log('UserItem '+items[idx].user_item_id+' loaded and events bound'); 
} 
$('#my_items_loader').hide(); 
myScroll.refresh(); 
} 
+0

collaudato soluzione rapida: http://stackoverflow.com/a/11941654/1386781 –

risposta

12

Invece di utilizzare tap e taphold (che ho provato ad usare, ma sono imbattuto negli stessi problemi, sembra essere un problema inerente con l'evento taphold) è possibile utilizzare vmousedown e impostare un flag, quindi associare a vmouseup per determinare se si trattava di un tap o un taphold:

var tapTime = 0; 
$('#my_item_'+items[idx].user_item_id).bind('vmousedown vmouseup', function (event) { 
    if (event.type == 'vmousedown') { 
     tapTime = new Date().getTime(); 
    } else { 
     //event.type == 'vmouseup' 
     //here you can check how long the `tap` was to determine what do do 

     var duration = (new Date().getTime() - tapTime); 
     if (duration > 3000) { 
      //this is a tap-hold 
      ShowMyItemInfo(items[idx]); 
     } else { 
      //this is a tap 
      FitMyUpgradeItem(items[idx]); 
     } 
    } 
}); 

Affinché funzioni correttamente, è necessario aggiungere un IIFE attorno al codice di loop o modificare ShowMyItemInfo(items[idx]); per lavorare senza fare riferimento alla variabile che modifica ogni iterazione del ciclo. Un facile creare un IIFE è semplicemente utilizzare $.each(). Altrimenti il ​​tuo ciclo sarebbe simile a questo:

for(var idx in items) 
{ 
    (function (idx) { 
     ... 
    })(idx); 
} 

IIFE = Espressione-Funzione-Richiamata. Ci consente di scattare una "istantanea" dello stato corrente delle variabili che passiamo nell'IFE. Così come passiamo idx (tecnicamente la seconda istanza è la variabile che viene passata in, e la prima istanza è la variabile disponibile all'interno della IIFE, che potrebbe essere cambiato qualcosa come ids_new per semplicità), il valore passato viene salvato per quando si attiva il gestore di eventi tap.

Aggiornamento

È anche possibile impostare un timeout per determinare taphold invece di usare l'evento vmouseup:

//setup a timer and a flag variable 
var tapTimer, 
    isTapHold = false; 
$('#my_item_'+items[idx].user_item_id).bind('vmousedown vmouseup', function (event) { 
    if (event.type == 'vmousedown') { 

     //set the timer to run the `taphold` function in three seconds 
     // 
     tapTimer = setTimeout(function() { 
      isTapHold = true; 
      ShowMyItemInfo(items[idx]); 
     }, 3000); 
    } else { 
     //event.type == 'vmouseup' 
     //clear the timeout if it hasn't yet occured 
     clearTimeout(tapTimer);  

     //if the flag is set to false then this is a `tap` event 
     if (!isTapHold) { 
      //this is a tap, not a tap-hold 
      FitMyUpgradeItem(items[idx]); 
     } 

     //reset flag 
     isTapHold = false; 
    } 
}); 

In questo modo l'evento verrà generato dopo che l'utente tiene premuto il dito per tre secondi. Quindi il gestore di eventi tap si attiva solo se non si verificano quei tre secondi.

+0

Ok, funziona alla grande. Grazie mille. Il mio unico problema ora è come renderlo un vero tap and hold piuttosto che un tap, hold, release. Mi piacerebbe che funzionasse proprio come fa l'evento taphold. Quindi dopo ~ 750ms la funzione viene attivata per completare il taphold – jasonlg3d

+0

@ jasonlg3d Ho aggiunto un esempio di come farlo usando un timeout in modo che non tocchi l'utente a sollevare il dito. Vedi ** aggiornamento ** alla mia risposta. – Jasper

+0

Stavo lavorando su questo mentre eri. Ho pubblicato la mia soluzione sopra. Ho dovuto fare qualcosa in più per assicurarmi che l'iScroll continuasse a scorrere e non si fosse fermato da un evento taphold. – jasonlg3d

0

Usa tap \ vclick e taphold invece - L'evento Tap viene sparato due volte dopo un taphold.

$('#my_item_'+items[idx].user_item_id).bind('vclick', ... 
$('#my_item_'+items[idx].user_item_id).bind('taphold', ... 

In questo esempio click non è in realtà chiamato dopo la taphold .. Verificare qui: http://jsfiddle.net/YL8hj/43/

Edit:

jQuery Mobile lega automaticamente evento di tocco ad alcuni elementi. Quando combina iScroll con jQuery Mobile potrebbe essere una buona idea associare una funzione separata allo all'evento 'touchmove' e impedire che l'evento di bubbling up (event.preventDefault()). In questo modo jQuery Mobile non sarà in grado di gestire l'evento tattile mentre gli utenti interagiscono con con l'elemento iScroll.

anche http://appcropolis.com/blog/jquery-wrapper-for-iscroll/ credito https://stackoverflow.com/a/9408567/643500

+0

Ci ho provato, sembra che stia facendo la stessa cosa. :/Questi elementi sono all'interno di un oggetto iScroll. Mi chiedo se questo sta causando problemi? – jasonlg3d

+0

Ok, ho provato il collegamento jsfiddle sul dispositivo e non funziona correttamente. Tutti gli eventi vengono attivati ​​nella visualizzazione Web Android. Quindi è un problema di dispositivo. Avrò bisogno di un altro modo per impostare questi eventi ... – jasonlg3d

+0

prova ancora il jsfiddle per favore. Ho lasciato il rubinetto, clic e vclick solo per mostrare la differenza. –

5

Basta impostare questo nella parte superiore del documento o dovunque prima di definire la vostra ancora:

$.event.special.tap.emitTapOnTaphold = false; 

Quindi è possibile utilizzare in questo modo:

$('#button').on('tap',function(){ 
    console.log('tap!'); 
}).on('taphold',function(){ 
    console.log('taphold!'); 
}); 
+0

Questa è la migliore risposta per gli eventi generali ... – Group

+0

grazie, risposta migliore –

1

Personalmente, ho pensato che le risposte qui ha complicato enormemente il problema. Se si desidera solo un modo semplice per essere in grado di continuare a utilizzare l'evento taphold e ignorare l'evento click che viene generato quando si rilascia un taphold, ecco come ho risolto questo stesso problema nel mio progetto:

// We will use this flag to ignore click events we don't want 
var skipNextClick = false; 

//Set up your event handler. You could do these using two handlers or one. I chose one. 

$('div.element').on('click taphold', function (e) { 
    //set up a quick bool flag that is true if click 
    var isClick = (e.type == 'click'); 

    if (isClick && !skipNextClick) { 
     //run your code for normal click events here... 

    } 
    else if (isClick && skipNextClick) { 
     //this is where skipped click events will end up... 

     //we need to reset our skipNextClick flag here, 
     //this way, our next click will not be ignored 
     skipNextClick = false; 
    } 
    else { 
     //taphold event 

     //to ignore the click event that fires when you release your taphold, 
     //we set the skipNextClick flag to true here. 
     skipNextClick = true; 

     //run your code for taphold events here... 

    } 
}); 
+0

L'evento taphold si attiva ancora se esci dall'elemento prima di essere rilasciato. Se si esegue questa operazione, l'evento di clic successivo verrà saltato perché l'evento click non viene attivato correttamente quando si sposta fuori dall'elemento. – Jules

+0

Posso sicuramente vedere come sarebbe successo! Ci sono alcuni modi in cui penso di cogliere questo. Forse cerca l'evento touchstart, annota l'elemento in cui è iniziato, quindi cerca l'evento touchend corrispondente e verifica se è esterno all'elemento originale o genitore. –

+0

... o jQuery mobile dovrebbe implementare correttamente tap/taphold in modo che non si attivi se ci si muove all'esterno dell'elemento prima di rilasciarlo :) – Jules