So überprüfen Sie, ob das Klickereignis bereits gebunden ist - JQuery

130

Ich binde ein Klickereignis mit einer Schaltfläche:

$('#myButton').bind('click',  onButtonClicked);

In einem Szenario wird dies mehrmals aufgerufen. Wenn ich also einen mache, triggersehe ich mehrere Ajax-Aufrufe, die ich verhindern möchte.

Wie mache ich das bindnur, wenn es vorher nicht gebunden ist.

Staubig
quelle
1
Mann, ich denke + Konrad Garus hat die sicherste Antwort ( stackoverflow.com/a/6930078/842768 ). Erwägen Sie , Ihre akzeptierte Antwort zu ändern. Prost! UPDATE: Überprüfen Sie auch den Kommentar von + Herbert Peters! Das ist der beste Ansatz.
Giovannipds
Aktuelle Standards finden Sie in der Antwort von @A Morel unten ( stackoverflow.com/a/50097988/1163705 ). Weniger Codierung und alle Rätselraten, Algorithmen und / oder Suchen nach vorhandenen Elementen aus der Gleichung.
Xandor

Antworten:

117

Update 24. August '12 : In jQuery 1.8 ist es nicht mehr möglich, mit auf die Ereignisse des Elements zuzugreifen .data('events'). ( Einzelheiten finden Sie in diesem Fehler .) Es ist möglich, mit jQuery._data(elem, 'events')einer internen Datenstruktur auf dieselben Daten zuzugreifen , die nicht dokumentiert ist und daher nicht zu 100% garantiert stabil bleibt. Dies sollte jedoch kein Problem sein, und die entsprechende Zeile des obigen Plugin-Codes kann wie folgt geändert werden:

var data = jQuery._data(this[0], 'events')[type];

jQuery-Ereignisse werden in einem Datenobjekt namens aufgerufen events, sodass Sie folgendermaßen suchen können:

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

Es wäre natürlich am besten, wenn Sie Ihre Anwendung so strukturieren könnten, dass dieser Code nur einmal aufgerufen wird.


Dies könnte in ein Plugin eingekapselt werden:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

Sie könnten dann anrufen:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
einsamer Tag
quelle
10
+1 für die Bearbeitung. Lesen Sie diesen Beitrag heute und ich benutze 1.8. Vielen Dank.
Gigi2m02
2
Danke fürs Bearbeiten. Ist das nur für Knöpfe oder könnte ich es auch für verwenden <a>? Denn wenn ich es versuche, steht auf jQuery._data()folgendem FehlerTypeError: a is undefined
Houman
Ich würde die Zeile var data = jQuery._data(this[0], 'events')[type];in einen Try-Catch einschließen und im Catch false zurückgeben. Wenn keine Ereignisse an diese [0] gebunden sind, führt ein Aufruf von [Typ] zu einer Variation von "Eigenschaftsklick auf undefinierte oder Nullreferenz kann nicht abgerufen werden", und dies sagt Ihnen natürlich auch, was Sie wissen müssen.
Robb Vandaveer
Ich bin nicht sicher, ob sich das _data-Objekt in jQuery 2.0.3 geändert hat, aber ich konnte $ .inArray dafür nicht verwenden. Die Funktion, die Sie vergleichen möchten, befindet sich in einer Eigenschaft jedes Datenelements namens "Handler". Ich habe es geändert, um eine einfache for-Anweisung zu verwenden, und auf Zeichenfolgenäquivalenz überprüft, was ich inArray angenommen habe. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer
Warum verschieben Sie Ihr Update / Ihre Bearbeitung nicht nach oben? ... Es ist die richtige Antwort.
Don Cheadle
179

Noch eine Möglichkeit: Markieren Sie solche Schaltflächen mit einer CSS-Klasse und einem Filter:

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

In neueren jQuery-Versionen ersetzen Sie binddurch on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Konrad Garus
quelle
4
Ausgezeichnet! Danke Konrad, das trifft den Sweet Spot. Ich liebe solche eleganten und einfachen Ansätze. Ich bin mir ziemlich sicher, dass es auch leistungsfähiger ist, da jedes einzelne Element (an das bereits Handler für Klickereignisse angehängt sind) keine vollständige Suche in einem großen Ereignisbereich durchführen muss, in dem jedes Element verglichen wird. In meiner Implementierung habe ich die hinzugefügte Hilfsklasse "klickgebunden" genannt, was meiner Meinung nach eine wartbarere Option ist: $ (". Listenelement: nicht (.klickgebunden)"). AddClass ("klickgebunden") ;;
Nicholas Petersen
1
In der akzeptierten Antwort heißt es: "Es wäre natürlich am besten, wenn Sie Ihre Anwendung so strukturieren könnten, dass dieser Code nur einmal aufgerufen wird." Dies erreicht das.
Jeff Dege
+1 In meiner Situation haben Aus / Ein und Binden / Aufheben der Bindung mehrere Anrufe verhindert. Dies war die Lösung, die funktioniert hat.
Jay
2
Dies ist die bessere Lösung für die Leistung, da der Code prüfen kann, ob die CSS-Klasse vorhanden ist und ob bereits eine Belegbindung vorhanden ist. Andere Lösungen führen einen Unbind-Then-Rebind-Prozess mit (zumindest von dem, was ich sagen kann) mehr Overhead aus.
Phil Nicholas
1
2 Probleme. (Wenn Sie es in einem Plug-In verwenden, das an mehrere Elemente gebunden werden kann.) Funktioniert nicht, wenn ein Größenänderungsereignis an das Fensterobjekt gebunden wird. Die Verwendung von Daten ('gebunden', 1) anstelle von addClass ('gebunden') ist ein weiterer Ansatz, der funktioniert. Wenn Sie das Ereignis zerstören, kann dies geschehen, während andere Instanzen davon abhängen. Es ist daher ratsam zu überprüfen, ob andere Instanzen noch verwendet werden.
Herbert Peters
59

Bei Verwendung von jQuery 1.7+:

Sie können offvorher anrufen on:

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

Wenn nicht:

Sie können das erste Ereignis einfach aufheben:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Naftali alias Neal
quelle
1
Dies ist nicht gerade eine schöne Lösung, aber sie würde verbessert, indem nur der eine Handler gelöst wird : .unbind('click', onButtonClicked). Siehe das Handbuch
einsamer
16
Sie können Ihre Handler beim Hinzufügen tatsächlich mit einem Namespace versehen, dann wird das Aufheben der Bindung ziemlich einfach. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday War Ihr Beispiel mit 'Unbind' dazu gedacht, etwas anderes zu zeigen? Ist .unbind nicht effektiv dasselbe wie .off, also müsstest du schreiben$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim
1
@ Jim Du wirst sehen, dass die Frage drei Jahre nach meinem Kommentar bearbeitet wurde! (Und auch in den Minuten unmittelbar nach meinem Kommentar. Der Inhalt, den ich kommentierte, existiert nicht mehr, weshalb es wahrscheinlich nicht viel Sinn macht.)
einsamer
@lonesomeday hmmm Ich bin mir nicht sicher, warum es bearbeitet wurde. Denkst du, ich sollte ein Rollback durchführen?
Naftali aka Neal
8

Ich sehe am besten live () oder delegate (), um das Ereignis in einem übergeordneten Element und nicht in jedem untergeordneten Element zu erfassen.

Befindet sich Ihre Schaltfläche in einem # Elternelement, können Sie Folgendes ersetzen:

$('#myButton').bind('click', onButtonClicked);

durch

$('#parent').delegate('#myButton', 'click', onButtonClicked);

auch wenn #myButton noch nicht existiert, wenn dieser Code ausgeführt wird.

Pablonete
quelle
2
Veraltete Methoden
Dobs
8

Ich habe ein sehr kleines Plugin namens "einmal" geschrieben, das das macht. Aus und Ein im Element ausführen.

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

Und einfach:

$(element).once('click', function(){
});
Rodolfo Jorge Nemer Nogueira
quelle
9
.one () löscht den Handler nach der ersten Ausführung.
Rodolfo Jorge Nemer Nogueira
3
hier ist "einmal" zum einmaligen Anhängen eines Handlers. "one" in jquery dient zum einmaligen Ausführen und nicht zum einmaligen Anhängen.
Shishir Arora
1
Ich weiß, dass ich nachträglich in die Quere komme, offentferne aber nicht alle Handler für den angegebenen Typ? Das heißt, wenn es einen separaten Klick-Handler gäbe, der nicht mit demselben Element zusammenhängt, würde dieser dann nicht auch entfernt werden?
Xandor
Verwenden Sie einfach einen Namespace für das Ereignis, damit nicht alle Handler wie 'keypup.test123'
SemanticZen
6

Warum nicht nutzen?

unbind() Vor bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
quelle
1
$('#myButton').off().on('click', onButtonClicked);funktioniert auch bei mir.
Veerakran Sereerungruangkul
Funktioniert für mich ... gr8 Lösung
imdadhusen
Wunderschönen. Funktioniert perfekt für mich für einen Knopf, der bereits gebunden war.
Seanbreeden
3

Hier ist meine Version:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

Verwendung:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)
Yair Galler
quelle
2

Basierend auf @ konrad-garus Antwort, aber mit data, da ich glaube, classsollte hauptsächlich für das Styling verwendet werden.

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}
Ariel
quelle
2

Um das Überprüfen / Binden / Aufheben der Bindung zu vermeiden, können Sie Ihren Ansatz ändern! Warum benutzt du nicht Jquery .on ()?

Da Jquery 1.7, .live (), .delegate () veraltet ist, können Sie jetzt .on () to verwenden

Fügen Sie jetzt und in Zukunft einen Ereignishandler für alle Elemente hinzu, die mit dem aktuellen Selektor übereinstimmen

Dies bedeutet, dass Sie ein Ereignis an ein übergeordnetes Element anhängen können, das noch vorhanden ist, und untergeordnete Elemente anhängen können, unabhängig davon, ob sie vorhanden sind oder nicht!

Wenn Sie .on () wie folgt verwenden:

$('#Parent').on('click', '#myButton'  onButtonClicked);

Wenn Sie ein Ereignis abfangen, klicken Sie auf das übergeordnete Element und suchen Sie das untergeordnete Element '#myButton', falls vorhanden ...

Wenn Sie also ein untergeordnetes Element entfernen oder hinzufügen, müssen Sie sich keine Gedanken darüber machen, ob Sie die Ereignisbindung hinzufügen oder entfernen möchten.

A. Morel
quelle
Dies ist hier absolut die beste Antwort für aktuelle Standards. Ich denke nicht, dass diese Funktion, den Handler auf die Eltern einzustellen, um auf das Kind zu achten, gut beworben wurde, aber eine ziemlich elegante Lösung ist. Ich hatte mehrmals ein Ereignisfeuer und jedes Mal nahm die Gesamtanzahl der Feuer zu. Diese einzelne Zeile hat den gesamten Trick ausgeführt und ohne Verwendung, von .offder ich glaube, dass sie nicht verwandte Handler in diesem Prozess entfernen würde.
Xandor
1

Versuchen:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}
Mukhtiar Zamin
quelle
0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}
Bishoy Hanna
quelle
0

Seit Juni 2019 habe ich die Funktion aktualisiert (und sie funktioniert für das, was ich brauche).

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};
Carlos Casalicchio
quelle
-2

JQuery hat folgende Lösung :

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

Äquivalent:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});
Veniamin
quelle
1
Ihre Antwort bezieht sich nicht auf eine Frage. Bei der Frage geht es darum, die Funktion nur einmal an das Ereignis des Elements zu binden.
Michał Zalewski