jQuery-Klickereignisse, die mehrmals ausgelöst werden

283

Ich versuche, ein Video-Pokerspiel in Javascript zu schreiben, um die Grundlagen zu erläutern, und bin auf ein Problem gestoßen, bei dem die Handler für jQuery-Klickereignisse mehrmals ausgelöst werden.

Sie sind an Schaltflächen zum Platzieren einer Wette angebracht und eignen sich gut zum Platzieren einer Wette aus erster Hand während eines Spiels (nur einmal abfeuern). Beim Wetten für die zweite Hand wird das Klickereignis jedoch jedes Mal zweimal ausgelöst, wenn eine Wette oder eine Platzierungswette gedrückt wird (also wird bei jedem Drücken der doppelte richtige Betrag gesetzt). Insgesamt folgt es diesem Muster für die Häufigkeit, mit der das Klickereignis ausgelöst wird, wenn einmal eine Wetttaste gedrückt wird - wobei der i-te Term der Sequenz für das Wetten der i-ten Hand vom Beginn des Spiels an gilt: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, was für alles, was es wert ist, n (n + 1) / 2 + 1 zu sein scheint - und ich war nicht klug genug, um das herauszufinden, ich habe OEIS verwendet . :) :)

Hier ist die Funktion mit den Click-Event-Handlern, die aktiv sind. hoffentlich ist es leicht zu verstehen (lass es mich wissen, wenn nicht, ich möchte auch darin besser werden):

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

Bitte lassen Sie mich wissen, wenn Sie Ideen oder Vorschläge haben oder wenn die Lösung für mein Problem einer Lösung für ein anderes Problem hier ähnlich ist (ich habe mir viele Threads mit ähnlichen Titeln angesehen und hatte kein Glück, eine Lösung zu finden, die funktionieren könnte für mich).

Gregory Fowler
quelle
var amount = parseInt(this.id.replace(/[^\d]/g,''),10);Und wenn Sie die gleiche Eigenschaft eines Elements verwenden werden mehr als einmal Cache diese Eigenschaft, nicht halten es bis suchen. Die Suche ist teuer.
David sagt, Monica
Vielen Dank für die Antwort und den Tipp zu den Caching-Eigenschaften. Ich setze player.money und player.bet auf lokale Variablen money und setze innerhalb dieser Funktion und manipuliere diese stattdessen und werde den Rest meines Codes ändern, um dies auch zu tun. :) Wenn Sie Zeit haben, können Sie auch erklären, was Sie vorgeschlagen haben Initialisierung des Betrags tut; es sieht aus wie ein regulärer Ausdruck, aber ich kann es nicht leicht verstehen.
Gregory Fowler
@GregoryFowler - hat nichts mit Ihrer Frage zu tun, aber ... eine Javascript-Switch-Anweisung könnte einen Blick wert sein.
Clayton
2
Mann, Ihre Funktion platziert bei jedem Aufruf einen Klick-Handler. Wenn Sie es in jeder Runde aufrufen, haben Sie in Runde zwei zwei Handler und so weiter. Jeder Handler erledigt seine Arbeit und bei Runde 100 erhalten Sie 100 Warnungen.
Marco Faustinelli
Dies liegt daran, dass Sie irgendwo in Ihrem Code den Ereignishandler neu binden, ohne ihn zuvor zu lösen. In dieser Frage finden Sie ein ähnliches Szenario und eine gute Erklärung.
Jpaugh

Antworten:

526

Um sicherzustellen, dass nur einmal geklickt wird, verwenden Sie Folgendes:

$(".bet").unbind().click(function() {
    //Stuff
});
rauben
quelle
31
OK, wo warst du gestern, Rob? ;) Genau das habe ich gesucht, weiß nicht warum ich es vorher nicht gefunden habe. Aber es war immer noch ein Segen in der Verkleidung, weil ich viele andere Dinge gelernt habe.
Gregory Fowler
1
;) Es tut uns leid. Auch hier habe ich ein paar Tage lang meine Räder herumgedreht. Ich suche immer noch nach einem logischen Grund, warum es überhaupt passiert.
Rob
6
Mann nach so vielen Dingen hat es das getan. Außer in meinem Fall habe ich das Äquivalent von: $ (". Bet"). Off (). On ()
MadTurki
7
Wie jroi_web weiter unten sagt, ist diese Methode veraltet. Eine neuere Lösung finden Sie in der Antwort von @ trolle.
Pascal
Ein Klick-Handler ist kein einmaliger Wegwerfartikel. Besonders wenn es das sperrige Wenn-Wenn-Wenn ist, das in der Frage gezeigt wird. Sie sollten es anhängen und es seine Arbeit machen lassen, solange die Seite lebt.
Marco Faustinelli
378

.unbind()ist veraltet und Sie sollten .off()stattdessen die Methode verwenden. Rufen .off()Sie einfach direkt vor Ihrem Anruf an .on().

Dadurch werden alle Ereignishandler entfernt:

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

So entfernen Sie nur registrierte 'Click'-Ereignishandler:

$(element).off('click').on('click', function() {
    // function body
});
mtl
quelle
9
Dies sollte in der neueren Version von jQuery verwendet werden, da das Aufheben, Sterben oder Leben bereits veraltet ist
jroi_web
Klappt wunderbar! Meine Funktion wurde einmal beim Klicken ausgeführt, dann zweimal, viermal ... .off (), bevor .on () dieses Problem löste.
jumpOnCommand
146

.einer()

Eine bessere Option wäre .one():

Der Handler wird höchstens einmal pro Element und Ereignistyp ausgeführt.

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

Bei mehreren Klassen und jeder Klasse muss einmal geklickt werden,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});
Shaunak D.
quelle
8
beste Antwort, du hast mich gerettet von einem dummen Hack, das ist viel besser , weil es , wenn Sie mehrere onclickEreignisse auf das gleiche Element , sondern an verschiedenen Orten wird dies den Rest nicht beeinflussen, während unbind()und off()zerstören nur andere onclicks danke wieder
Fanckush
3
Nachdem ich alle Antworten ausprobiert habe, ist dies die einzige Arbeit für mich.
Neversion
10
Eine Sache zu beachten, wenn Sie möchten, dass die Funktion nur einmal pro Klick ausgelöst wird, aber bei nachfolgenden Klicks weiterhin ausgelöst wird, wird diese Funktion buchstäblich nur einmal für die Lebensdauer der Seite ausgelöst. Daher muss die Antwort von mtl mit der Methode off (). On () verwendet werden.
Derek Hewitt
1
@munchschair, dafür kann es mehrere Gründe geben. Mehrere Elemente mit derselben Klasse ineinander. Oder ein Klick-Handler wird in einer Iteration mehrmals registriert.
Shaunak D
3
Bei mir nicht zu funktionieren - immer noch mehrere Klicks - macht keinen Sinn, da nur 1 Schaltflächenelement mit der ID vorhanden ist und nur 1 Ereignis abgefangen wird (das Klickereignis). Ich dachte, dies sei ein jQuery-Fehler, aber es ist wahrscheinlich ein Bootstrap-Fehler im modalen Dialog.
MC9000
78

Wenn Sie feststellen, dass .off () .unbind () oder .stopPropagation () Ihr spezifisches Problem immer noch nicht beheben, versuchen Sie es mit .stopImmediatePropagation (). Funktioniert hervorragend in Situationen, in denen Ihr Ereignis nur ohne und ohne Sprudeln behandelt werden soll Auswirkungen auf andere Ereignisse, die bereits behandelt werden. Etwas wie:

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

macht den Trick!

Alfonse Pinto
quelle
Dies hat mir ebenso geholfen, das zweimalige Abfeuern des Ereignisses zu stoppen, hat mich aber auch sehr durcheinander gebracht. Wenn Sie event.stopImmediatePropagation () in einem Ereignishandler verwenden, der an ein jQuery UI-Dialogfeld angehängt ist, kann der Dialog aus irgendeinem Grund nicht mehr auf die neueste Instanz globaler Variablen verweisen und verwendet stattdessen die Version globaler Variablen vor dem Rendern des jQuery UI-Dialogfelds. Das heißt, wenn beispielsweise Ihre globale Variable zum Zeitpunkt des Renderns des jQuery UI-Dialogs deklariert, aber nicht initialisiert wurde, wird sie als nicht initialisierter Ereignisbehandlungsroutine für den jQuery UI-Dialog angezeigt
UkraineTrain
1
Wenn Sie einer globalen Variablen vor dem Rendern des jQuery UI-Dialogfelds den Wert 16 zugewiesen haben, der später beispielsweise auf 55 geändert wurde, wird 16 für diese globale Variable im Ereignishandler des jQuery UI-Dialogfelds angezeigt, auch wenn dies zu diesem Zeitpunkt der Fall ist nicht mehr 16. Es sieht also nach einem jQuery-UI-Fehler aus, den die Leute kennen sollten.
UkraineTrain
Für ganz bestimmte Situationen, die sogar schwer zu beschreiben sind. Es funktioniert genau so, wie Sie gesagt haben: "Funktioniert, ohne andere Ereignisse zu beeinflussen". Vielen Dank! :)
Toms Bugna
Funktioniert ganz gut für mich. Vielen Dank!
Somnath Pawar
18

Wenn Sie diese Funktion bei jedem "Klick" aufrufen, wird bei jedem Aufruf ein weiteres Handlerpaar hinzugefügt .

Das Hinzufügen von Handlern mit jQuery entspricht nicht dem Festlegen des Werts des Attributs "onclick". Man kann so viele Handler hinzufügen, wie man möchte.

Spitze
quelle
4
Ihre Antwort hat mich in die richtige Richtung gelenkt und ich habe viel gelernt, aber leider nicht genug, um mein Problem mit jQuery zu lösen. :) Ich habe versucht, on / off, live (und delegate) / die und one und dann addEventListener / removeEventListener zu verwenden, aber das Beste, was ich tun konnte, war, das exponentielle Wachstum von Handlern zu verlangsamen. Ich habe mein Problem vorerst nur mit onclick gelöst. Aber ich habe aus Ihrer Antwort immer noch viel über das Ereignismodell von Javascript gelernt, wie z. B. Erfassen / Sprudeln. Ich weiß das zu schätzen. Danke dir. :)
Gregory Fowler
Vielen Dank, ich kann nicht glauben, dass ich das nicht wusste!
Lenny
12

Ein Ereignis wird mehrmals ausgelöst, wenn es mehrmals registriert wird (auch wenn es sich um denselben Handler handelt).

zB $("ctrl").on('click', somefunction)wenn dieses Stück Code jedes Mal , wenn die Seite ausgeführt wird , wird teilweise aktualisiert, wird das Ereignis jedes Mal zu registriert. Selbst wenn die Strg-Taste nur einmal angeklickt wird, kann sie "irgendeine Funktion" mehrmals ausführen. Wie oft sie ausgeführt wird, hängt davon ab, wie oft sie registriert wurde.

Dies gilt für alle in Javascript registrierten Ereignisse.

Lösung:

Stellen Sie sicher, dass Sie nur einmal "an" rufen.

und aus irgendeinem Grund, wenn Sie die Architektur nicht steuern können, gehen Sie folgendermaßen vor:

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

Kalpesh Popat
quelle
Was willst du wirklich sagen?
Somnath Kharat
Das erklärt nicht, was in diesem Fall vor sich geht. Die Schaltfläche hat eine eindeutige ID und nur ein Ereignis (sie wird NICHT mehrmals aufgerufen, da nur einmal darauf geklickt werden kann). Dies ist ein Fehler, der nur in jQuery-Dialogfeldern auftritt (und sehr einfach zu reproduzieren ist).
MC9000
2
Wir wissen nicht, ob die Funktion "pushBetButtons" nur einmal oder mehrmals aufgerufen wird. Wenn sie mehr als einmal aufgerufen wird, wird das Ereignis mehrmals registriert und daher auch mehrmals ausgelöst. Auch wenn die Schaltfläche nur einmal angeklickt wird .
Kalpesh Popat
4

Ich hatte ein Problem wegen 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) { ... } );

Sie bemerken, dass ich dieselbe Klasse 'myclass' zweimal in HTML habe, also ruft es click für jede Instanz von div auf.

Bobz
quelle
3

Die bessere Option wäre 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>
arc_shiva
quelle
3

Das ganze Zeug über .on () und .one () ist großartig, und jquery ist großartig.

Aber manchmal möchten Sie, dass es etwas offensichtlicher ist, dass der Benutzer nicht klicken darf, und in diesen Fällen können Sie Folgendes tun:

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

und Ihr Knopf würde aussehen wie:

<button onclick='funName()'>Click here</button>
rikkitikkitumbo
quelle
1
Das würde den Fall lösen, dass ein Benutzer tatsächlich mehrmals klickt, aber ... ich erhalte mehrere Klickereignisse mit demselben Zeitstempel. Es gibt keine Möglichkeit, dreimal in derselben Millisekunde zu klicken, selbst wenn ich extrem schnell darin war (und irgendwie nicht wusste, wie oft ich die Taste drücke).
Jpaugh
2

Dies geschieht, weil das jeweilige Ereignis mehrmals an dasselbe Element gebunden ist.

Die Lösung, die für mich funktioniert hat, ist:

Töte alle angehängten Ereignisse mit der .die()Methode.

Und dann hängen Sie Ihren Methoden-Listener an.

Somit,

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

sollte sein:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}
Schüler
quelle
2

stopPropagation()Um zu vermeiden, dass Klicks das Ereignis zu oft auslösen, müssen wir dies tun.

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

Es verhindert, dass das Ereignis in den DOM-Baum sprudelt, und verhindert, dass übergeordnete Handler über das Ereignis benachrichtigt werden. Diese Methode akzeptiert keine Argumente.

Wir können verwenden, event.isPropagationStopped()um festzustellen, ob diese Methode jemals aufgerufen wurde (für dieses Ereignisobjekt).

Diese Methode funktioniert auch für benutzerdefinierte Ereignisse, die mit trigger () ausgelöst werden. Beachten Sie, dass dies nicht verhindert, dass andere Handler auf demselben Element ausgeführt werden.

Daniel Raja Singh
quelle
2

In meinem Fall habe ich "Delegate" verwendet, sodass keine dieser Lösungen funktioniert hat. Ich glaube, es war die Schaltfläche, die mehrmals über Ajax-Aufrufe angezeigt wurde und das Problem mit mehreren Klicks verursachte. Die Lösungen verwendeten eine Zeitüberschreitung, sodass nur der letzte Klick erkannt wird:

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)
})
Trevor Lettman
quelle
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Rajan Rajan M.
quelle
1

.one wird während der gesamten Lebensdauer der Seite nur einmal ausgelöst

Wenn Sie also eine Validierung durchführen möchten, ist dies nicht die richtige Lösung, da Sie nie wieder zurückkehren, wenn Sie die Seite nach der Validierung nicht verlassen. Besser zu benutzen

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

Wenn ich mich mit diesem Problem befasse, verwende ich immer:

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

Auf diese Weise binde ich mich im selben Strich und wieder.

Andy
quelle
0

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

Um sicherzustellen, dass das Ereignis immer nur einmal ausgelöst wird, können Sie Jquery .one () verwenden. JQuery One stellt sicher, dass Ihr Event-Handler nur einmal aufgerufen wird. Darüber hinaus können Sie Ihren Ereignishandler mit einem abonnieren, um weitere Klicks zuzulassen, wenn Sie die Verarbeitung des aktuellen Klickvorgangs abgeschlossen haben.

<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();
Razan Paul
quelle
0

Versuchen Sie es so:

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

In meinem Fall wurde das Ereignis onclick mehrmals ausgelöst, da ich einen generischen Ereignishandler vergleichsweise als erstellt hatte

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

gewechselt zu

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

Außerdem müssen separate Handler für statische und dynamische Klicks für statische Tabulatoren erstellt werden

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

In meinem Fall hatte ich dieselbe *.jsDatei zweimal in einem <script>Tag auf die Seite geladen , sodass beide Dateien Ereignishandler an das Element anhängten. Ich habe die doppelte Deklaration entfernt und das Problem behoben.

Inostia
quelle
0

Eine andere Lösung, die ich gefunden habe, war folgende: Wenn Sie mehrere Klassen haben und sich mit Optionsfeldern befassen, während Sie auf das Etikett klicken.

$('.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();
    }
});
Andrew Vink
quelle
0

Ich hatte dieses Problem mit einem dynamisch generierten Link:

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

Ich habe festgestellt, dass das Problem durch Ersetzen documentdurch 'body'behoben wurde:

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

nateM
quelle
0

Unbind () funktioniert, aber das könnte in Zukunft andere Probleme verursachen. Der Handler wird mehrmals ausgelöst, wenn er sich in einem anderen Handler befindet. Lassen Sie Ihren Handler also draußen. Wenn Sie die Werte des verschachtelten Handlers verwenden möchten, weisen Sie sie einer globalen Variablen zu, auf die Ihr Handler zugreifen kann.

Praveen Poonja
quelle
0

Falls dies gut funktioniert

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
Santosh Vishwakarma
quelle
-1

Der folgende Code hat in meiner Chat-Anwendung funktioniert, um mehrere Mausklick-auslösende Ereignisse mehr als einmal zu verarbeiten. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

user2792303
quelle