Eventi clic jQuery attivati ​​più volte


284

Sto tentando di scrivere un gioco di video poker in Javascript come un modo per metterlo a tacere, e ho riscontrato un problema in cui i gestori di eventi jQuery Click si attivano più volte.

Sono collegati ai pulsanti per piazzare una scommessa, e funziona benissimo per piazzare una scommessa sulla prima mano durante una partita (sparando una sola volta); ma scommettendo per la seconda mano, genera l'evento click due volte ogni volta che viene premuto un pulsante di scommessa o piazza scommessa (quindi due volte l'importo corretto viene puntato per ogni pressione). Complessivamente, segue questo schema per il numero di volte in cui l'evento click viene lanciato quando si preme una volta il pulsante di scommessa - dove l' ennesimo termine della sequenza è per la scommessa dell'ennesima mano dall'inizio del gioco: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, che sembra essere n (n + 1) / 2 + 1 per quello che vale - e non ero abbastanza intelligente per capirlo, ho usato OEIS . :)

Ecco la funzione con i gestori di eventi click che agiscono; speriamo che sia facile da capire (fammi sapere se no, voglio migliorare anche in questo):

/** The following function keeps track of bet buttons that are pressed, until place button is pressed to place bet. **/
function pushingBetButtons() {
    $("#money").text("Money left: $" + player.money); // displays money player has left

    $(".bet").click(function() {
        var amount = 0; // holds the amount of money the player bet on this click
        if($(this).attr("id") == "bet1") { // the player just bet $1
            amount = 1;
        } else if($(this).attr("id") == "bet5") { // etc.
            amount = 5;
        } else if($(this).attr("id") == "bet25") {
            amount = 25;
        } else if($(this).attr("id") == "bet100") {
            amount = 100;
        } else if($(this).attr("id") == "bet500") {
            amount = 500;
        } else if($(this).attr("id") == "bet1000") {
            amount = 1000;
        }
        if(player.money >= amount) { // check whether the player has this much to bet
            player.bet += amount; // add what was just bet by clicking that button to the total bet on this hand
            player.money -= amount; // and, of course, subtract it from player's current pot
            $("#money").text("Money left: $" + player.money); // then redisplay what the player has left
        } else {
            alert("You don't have $" + amount + " to bet.");
        }
    });

    $("#place").click(function() {
        if(player.bet == 0) { // player didn't bet anything on this hand
            alert("Please place a bet first.");
        } else {
            $("#card_para").css("display", "block"); // now show the cards
            $(".card").bind("click", cardClicked); // and set up the event handler for the cards
            $("#bet_buttons_para").css("display", "none"); // hide the bet buttons and place bet button
            $("#redraw").css("display", "block"); // and reshow the button for redrawing the hand
            player.bet = 0; // reset the bet for betting on the next hand
            drawNewHand(); // draw the cards
        }
    });
}

Per favore fatemi sapere se avete idee o suggerimenti o se la soluzione al mio problema è simile a una soluzione a un altro problema qui (ho esaminato molti thread con lo stesso titolo e non ho avuto fortuna nel trovare una soluzione che potesse funzionare per me).


var amount = parseInt(this.id.replace(/[^\d]/g,''),10);E se hai intenzione di utilizzare la stessa proprietà di un elemento più di una volta memorizza nella cache quella proprietà, non continuare a cercarla. La ricerca è costosa.
David dice di ripristinare Monica il

Grazie per la risposta e il suggerimento sulle proprietà di memorizzazione nella cache. Ho impostato player.money e player.bet su variabili locali soldi e ho scommesso all'interno di quella funzione e le ho manipolate invece, e cambierò anche il resto del mio codice per farlo. :) Se hai tempo, potresti anche spiegare ciò che hai suggerito l'inizializzazione dell'importo è in corso; sembra un'espressione regolare, ma non riesco a capirlo facilmente.
Gregory Fowler,

@GregoryFowler - non correlato alla tua domanda, ma ... vale la pena esaminare un'istruzione switch javascript.
Clayton,

2
Amico, la tua funzione sta posizionando un gestore di clic ogni volta che viene invocato. Se lo invochi ad ogni round, al secondo round hai due gestori e così via. Ogni gestore fa il suo lavoro e al round 100 ricevi 100 avvisi.
Marco Faustinelli,

Questo accade perché da qualche parte nel tuo codice, stai vincolando il gestore eventi senza prima separarlo. Vedi questa domanda per uno scenario simile e una buona spiegazione.
jpaugh,

Risposte:


528

Per essere sicuro di fare clic solo sulle azioni una volta utilizzate questa:

$(".bet").unbind().click(function() {
    //Stuff
});

31
OK, dov'eri ieri, Rob? ;) Questo è esattamente quello che stavo cercando, non so perché non l'ho trovato prima. Ma era ancora una benedizione sotto mentite spoglie perché ho imparato molte altre cose.
Gregory Fowler,

1
;) spiacente. Ho girato le ruote per alcuni giorni anche su questo. Sto ancora cercando un motivo logico per cui accade anche in primo luogo.
Rob,

6
L'uomo dopo così tante cose ha fatto questo. Tranne nel mio caso ho usato l'equivalente di: $ (". Bet"). Off (). On ()
MadTurki

7
Come dice jroi_web di seguito, questo metodo è obsoleto. Vedi la risposta di @ trolle per una soluzione più recente.
Pascal,

Un gestore di clic non è una cosa da buttare una tantum. Soprattutto quando è il voluminoso if-if-if mostrato nella domanda. Dovresti allegarlo e lasciarlo fare tutto il tempo in cui la pagina rimane.
Marco Faustinelli,

380

.unbind()è obsoleto e dovresti invece utilizzare il .off()metodo Basta chiamare .off()subito prima di chiamare .on().

Ciò rimuoverà tutti i gestori di eventi:

$(element).off().on('click', function() {
    // function body
});

Per rimuovere solo i gestori di eventi 'clic' registrati:

$(element).off('click').on('click', function() {
    // function body
});

9
questo dovrebbe essere usato nella versione più recente di jQuery poiché unbind, die o live è già deprecato
jroi_web

Funziona come un fascino! La mia funzione verrebbe eseguita una volta al clic, quindi due volte, quattro volte ... .off () prima che .on () risolvesse questo problema.
jumpOnCommand

146

.uno()

Un'opzione migliore sarebbe .one():

Il gestore viene eseguito al massimo una volta per elemento per tipo di evento.

$(".bet").one('click',function() {
    //Your function
});

Nel caso di più classi e ogni classe deve essere cliccata una volta,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});

8
migliore risposta, mi hai salvato da un trucco stupido, questo è molto meglio perché se hai più onclickeventi nello stesso elemento ma in posizioni diverse questo non influenzerà il resto, mentre unbind()e off()distruggerà di nuovo altri onclickgrazie
Fanckush

3
dopo aver provato tutte le risposte, questa è l'unica opera per me.
neversion,

10
Una cosa da notare, se si desidera che la funzione venga attivata una sola volta PER CLIC, ma continui a essere attivata con i clic successivi, questa verrà letteralmente attivata una sola volta per tutta la vita della pagina. Quindi la risposta di mtl con il metodo off (). On () dovrà essere usata.
Derek Hewitt,

1
@munchschair, possono esserci più ragioni per questo. Più elementi con la stessa classe uno dentro l'altro. Oppure un gestore di clic viene registrato più volte in un'iterazione.
Shaunak D,

3
Non funziona per me - ancora attivazioni di clic multipli - non ha senso in quanto c'è solo 1 elemento pulsante con l'ID e solo 1 evento è bloccato (l'evento click). Ho pensato che si trattasse di un bug jQuery, ma probabilmente è un bug del dialogo modale Bootstrap.
MC9000,

78

Se scopri che .off () .unbind () o .stopPropagation () non risolve ancora il tuo problema specifico, prova a utilizzare .stopImmediatePropagation () Funziona alla grande in situazioni in cui vuoi che il tuo evento venga gestito senza bolle e senza effettuando qualsiasi altro evento già gestito. Qualcosa di simile a:

$(".bet").click(function(event) {
  event.stopImmediatePropagation();
  //Do Stuff
});

fa il trucco!


Questo mi ha aiutato oltre a fermare l'evento sparando due volte, ma mi ha anche incasinato alla grande. Si scopre che se si utilizza event.stopImmediatePropagation () in un gestore eventi collegato a un campo della finestra di dialogo dell'interfaccia utente jQuery, quindi, per qualche motivo, la finestra di dialogo non può più fare riferimento alla più recente istanza di variabili globali e utilizza invece la versione delle variabili globali prima del rendering della finestra di dialogo dell'interfaccia utente di jQuery. Ciò significa che se, ad esempio, la tua variabile globale è stata dichiarata, ma non inizializzata al momento del rendering della finestra di dialogo dell'interfaccia utente di jQuery, verrà visualizzata come gestore di eventi della finestra di dialogo dell'interfaccia utente di jQuery non inizializzata
Ucraina,

1
o se hai assegnato a una variabile globale un valore di 16 prima del rendering della finestra di dialogo dell'interfaccia utente jQuery, che in seguito è diventata 55, quindi 16, verrà mostrato per quella variabile globale nel gestore eventi della finestra di dialogo dell'interfaccia utente jQuery, anche se in quel momento è non più 16. Quindi, sembra un bug dell'interfaccia utente jQuery di cui le persone dovrebbero essere a conoscenza.
Ucraina,

Per situazioni molto specifiche che sono persino difficili da descrivere. Funziona esattamente come hai detto: "Funziona senza effettuare altri eventi". Grazie! :)
Toms Bugna,

Funziona perfettamente per me. Grazie!
Somnath Pawar,

18

Se stai chiamando quella funzione su ogni "clic", allora sta aggiungendo un'altra coppia di gestori ad ogni chiamata.

Aggiungere gestori con jQuery non è come impostare il valore dell'attributo "onclick". Si possono aggiungere tutti i gestori che si desiderano.


4
La tua risposta mi ha messo nella giusta direzione e ho imparato molto, ma non abbastanza per risolvere il mio problema usando jQuery purtroppo. :) Ho provato a usare on / off, live (e delegate) / die, e uno, quindi aggiungereEventListener / removeEventListener, ma il meglio che ho potuto fare è stato rallentare la crescita esponenziale dei gestori. Alla fine ho appena risolto il mio problema con onclick per il momento. Ma ho ancora imparato molto sul modello di eventi di Javascript, come catturare / gorgogliare, dalla tua risposta, quindi lo apprezzo. Grazie. :)
Gregory Fowler,

Grazie mille, non posso credere di non saperlo!
Lenny,

12

un Evento si attiva più volte quando viene registrato più volte (anche se allo stesso gestore).

ad es. $("ctrl").on('click', somefunction)se questo pezzo di codice viene eseguito ogni volta che la pagina viene parzialmente aggiornata, anche l'evento viene registrato ogni volta. Pertanto, anche se si fa clic sul ctrl solo una volta, è possibile eseguire più volte una "funzione"; quante volte verrà eseguita dipenderà da quante volte è stata registrata.

questo vale per qualsiasi evento registrato in javascript.

soluzione:

assicurati di chiamare "on" solo una volta.

e per qualche motivo se non puoi controllare l'architettura, allora fai questo:

$("ctrl").off('click'); $("ctrl").on('click', somefunction);


Cosa vuoi veramente dire?
Somnath Kharat

Questo non spiega cosa sta succedendo in questo caso. Il pulsante ha un ID univoco e solo 1 evento (NON viene chiamato più volte, poiché può essere fatto clic una sola volta). Questo è un bug che appare solo nelle finestre di dialogo jQuery (e molto facile da riprodurre).
MC9000,

2
non sappiamo se la funzione "pushBetButtons" viene chiamata solo una volta o più volte .. se viene chiamata più di una volta, allora l'evento viene registrato più volte e quindi genererà anche più volte..anche se si fa clic sul pulsante una sola volta .
Kalpesh Popat,

4

Ho avuto un problema a causa del markup.

HTML:

<div class="myclass">
 <div class="inner">

  <div class="myclass">
   <a href="#">Click Me</a>
  </div>

 </div>
</div>

jQuery

$('.myclass').on('click', 'a', function(event) { ... } );

Noterai che ho la stessa classe "myclass" due volte in html, quindi chiama click per ogni istanza di div.


3

L'opzione migliore sarebbe utilizzando off

<script>
function flash() {
  $("div").show().fadeOut("slow");
}
$("#bind").click(function() {
  $( "body" )
    .on("click", "#theone", flash)
    .find("#theone")
      .text("Can Click!");
});
$("#unbind").click(function() {
  $("body")
    .off("click", "#theone", flash)
    .find("#theone")
      .text("Does nothing...");
});
</script>

3

Tutte le cose su .on () e .one () sono fantastiche, e jquery è fantastica.

Ma a volte, vuoi che sia un po 'più ovvio che l'utente non è autorizzato a fare clic, e in quei casi potresti fare qualcosa del genere:

function funName(){
    $("#orderButton").prop("disabled", true);
    //  do a bunch of stuff
    // and now that you're all done
    setTimeout(function(){
        $("#orderButton").prop("disabled",false);
        $("#orderButton").blur();
    }, 3000);
}

e il tuo pulsante sarebbe simile a:

<button onclick='funName()'>Click here</button>

1
Ciò risolverebbe il caso in cui un utente fa effettivamente clic più volte, ma ... Ricevo più eventi di clic con lo stesso timestamp. Non è possibile fare clic 3 volte nello stesso millisecondo, anche se sono stato estremamente veloce (e in qualche modo ignaro di quante volte premo il pulsante).
jpaugh,

2

Succede a causa di un evento particolare legato più volte allo stesso elemento.

La soluzione che ha funzionato per me è:

Uccidi tutti gli eventi collegati usando il .die()metodo.

E quindi allega il tuo listener di metodi.

Così,

$('.arrow').click(function() {
// FUNCTION BODY HERE
}

dovrebbe essere:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}

2

Dobbiamo stopPropagation()evitare Al fine di evitare l'evento di innescare Clic troppe volte.

$(this).find('#cameraImageView').on('click', function(evt) {
   evt.stopPropagation();
   console.log("Camera click event.");
});

Impedisce all'evento di gorgogliare l'albero del DOM, impedendo a qualsiasi gestore padre di essere informato dell'evento. Questo metodo non accetta alcun argomento.

Possiamo usare event.isPropagationStopped() per determinare se questo metodo è mai stato chiamato (su quell'oggetto evento).

Questo metodo funziona anche per eventi personalizzati attivati ​​con trigger (). Notare che ciò non impedirà l'esecuzione di altri gestori sullo stesso elemento.


2

Nel mio caso stavo usando "delegato", quindi nessuna di queste soluzioni ha funzionato. Credo che sia stato il pulsante a comparire più volte tramite chiamate Ajax che ha causato il problema dei clic multipli. Le soluzioni utilizzavano un timeout, quindi viene riconosciuto solo l'ultimo clic:

var t;
$('body').delegate( '.mybutton', 'click', function(){
    // clear the timeout
    clearTimeout(t);
    // Delay the actionable script by 500ms
    t = setTimeout( function(){
        // do something here
    },500)
})


1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}

1

.one viene generato solo una volta per la durata della pagina

Quindi, nel caso in cui si desideri eseguire la convalida, questa non è la soluzione giusta, perché quando non si esce dalla pagina dopo la convalida, non si torna più indietro. Meglio usare

$(".bet").on('click',function() 
{ //validation 
   if (validated) { 
      $(".bet").off('click'); //prevent to fire again when we are not yet off the page
      //go somewhere
    }
});

1

Quando mi occupo di questo problema, utilizzo sempre:

$(".bet").unbind("click").bind("click", function (e) {
  // code goes here
}

In questo modo mi sciolgo e mi ritrovo nello stesso colpo.


0

https://jsfiddle.net/0vgchj9n/1/

Per assicurarti che l'evento si attivi sempre solo una volta, puoi usare Jquery .one (). JQuery one assicura che il gestore eventi abbia chiamato solo una volta. Inoltre, puoi iscriverti al gestore dell'evento con uno per consentire ulteriori clic al termine dell'elaborazione dell'operazione di clic corrente.

<div id="testDiv">
  <button class="testClass">Test Button</button>
</div>

...

var subscribeClickEvent = function() {$("#testDiv").one("click", ".testClass", clickHandler);};

function clickHandler() {
  //... perform the tasks  
  alert("you clicked the button");
  //... subscribe the click handler again when the processing of current click operation is complete  
  subscribeClickEvent();
}

subscribeClickEvent();

0

Prova in questo modo:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>

0

Nel mio caso, l'evento onclick è stato attivato più volte perché avevo creato un gestore di eventi generico relativamente

  `$('div').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
   });`

cambiato in

    `$('div#mainData').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`

e devono anche creare gestori separati per i clic statici e dinamici, per i clic sulle schede statiche

    `$('a[data-toggle="tab"]').on("click",function () {
        console.log("static bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`

0

Nel mio caso avevo caricato *.jsdue volte lo stesso file sulla pagina in un <script>tag, quindi entrambi i file collegavano i gestori di eventi all'elemento. Ho rimosso la dichiarazione duplicata e questo ha risolto il problema.


0

Un'altra soluzione che ho trovato è stata questa, se hai più classi e hai a che fare con i pulsanti di opzione mentre fai clic sull'etichetta.

$('.btn').on('click', function(e) {
    e.preventDefault();

    // Hack - Stop Double click on Radio Buttons
    if (e.target.tagName != 'INPUT') {
        // Not a input, check to see if we have a radio
        $(this).find('input').attr('checked', 'checked').change();
    }
});

0

Stavo avendo questo problema con un link generato dinamicamente:

$(document).on('click', '#mylink', function({...do stuff...});

Ho trovato la sostituzione documentcon 'body'risolto il problema per me:

$('body').on('click', '#mylink', function({...do stuff...});


0

Unbind () funziona, ma ciò potrebbe causare altri problemi in futuro. Il gestore si innesca più volte quando si trova all'interno di un altro gestore, quindi mantieni il gestore all'esterno e se desideri i valori del gestore che aveva nidificato, assegnali a una variabile globale che sarà accessibile al gestore.



-1

Il codice seguente ha funzionato per me nella mia applicazione di chat per gestire più clic innescando eventi più di una volta. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.