Direkt vs. delegiert - jQuery .on ()


159

Ich versuche, diesen besonderen Unterschied zwischen den direkten und delegierten Ereignishandlern mithilfe der Methode jQuery .on () zu verstehen . Insbesondere der letzte Satz in diesem Absatz:

Wenn a angegeben selectorist, wird der Ereignishandler als delegiert bezeichnet . Der Handler wird nicht aufgerufen, wenn das Ereignis direkt auf dem gebundenen Element auftritt, sondern nur für Nachkommen (innere Elemente), die dem Selektor entsprechen. jQuery sprudelt das Ereignis vom Ereignisziel bis zu dem Element, an das der Handler angehängt ist (dh vom innersten zum äußersten Element), und führt den Handler für alle Elemente entlang dieses Pfads aus, die dem Selektor entsprechen.

Was bedeutet "führt den Handler für Elemente aus"? Ich habe eine Testseite erstellt , um mit dem Konzept zu experimentieren. Beide folgenden Konstrukte führen jedoch zum gleichen Verhalten:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

oder,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

Vielleicht könnte sich jemand auf ein anderes Beispiel beziehen, um diesen Punkt zu verdeutlichen? Vielen Dank.


7
Für alle Interessierten: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo

1
@ KevinWheeler Ich habe Ihre Geige unten kommentiert, aber hier ist sie im Wesentlichen nicht richtig eingerichtet (Sie sind an das übergeordnete Element gebunden, und die Delegierung ist für die untergeordneten Elemente vorgesehen). Um Ihre Frage zu beantworten, bedeutet dies, dass der delegierte Handler mit neu hinzugefügten Elementen übereinstimmt, während der ohne Delegierung dies nicht tut. Die Delegierung hat den Vorteil, dass weniger Ereignisse in den Browser eingebunden sind, was zu einem geringeren Speicherverbrauch für die App führt. Der Nachteil ist jedoch, dass die Zeit für die Verarbeitung eines Klicks (minimal) verlängert wird. Wenn Sie ein Spiel machen, delegieren Sie nicht.
Vipero07

1
Die von Ihnen referenzierte "Testseite" funktioniert nicht.
Jaime Montoya

Antworten:


372

Fall 1 (direkt):

$("div#target span.green").on("click", function() {...});

== Hey! Ich möchte, dass jedes span.green in div # target zuhört: Wenn Sie darauf klicken, machen Sie X.

Fall 2 (delegiert):

$("div#target").on("click", "span.green", function() {...});

== Hey, div # Ziel! Wenn eines Ihrer untergeordneten Elemente, die "span.green" sind, angeklickt wird, machen Sie X mit ihnen.

Mit anderen Worten...

In Fall 1 wurde jeder dieser Bereiche einzeln Anweisungen gegeben. Wenn neue Bereiche erstellt werden, haben sie die Anweisung nicht gehört und reagieren nicht auf Klicks. Jede Spanne ist direkt für ihre eigenen Ereignisse verantwortlich.

In Fall 2 hat nur der Container die Anweisung erhalten; Es ist dafür verantwortlich, Klicks im Namen seiner untergeordneten Elemente zu bemerken . Die Arbeit, Ereignisse zu fangen, wurde delegiert . Dies bedeutet auch, dass die Anweisung für untergeordnete Elemente ausgeführt wird, die in Zukunft erstellt werden.


45
Das ist eine großartige Erklärung und hat Klarheit in ein Thema gebracht, das ich lange nicht verstanden habe. Vielen Dank!
dgo

3
Warum also on()zwei Argumente zulassen, wenn das so ziemlich das gleiche wäre wie die Verwendung click()?
Nipponese

5
.on () ist eine Allzweck-API, die jede Art von Ereignis verarbeiten kann, einschließlich mehrerer verschiedener Ereignisse (Sie können mehrere Ereignisnamen in diese erste Zeichenfolge einfügen.) .click () ist nur eine Abkürzung für dieses erste Formular.
N3dst4

1
@newbie, @ N3dst4: e.targetist das ursprüngliche Ziel des Klickereignisses (kann ein untergeordneter Knoten sein, wenn untergeordnete Knoten vorhanden sind span.green). Im Handler sollten Sie die thisReferenz verwenden. Sehen Sie diese Geige .
LeGEC

1
Ein weiterer Hinweis zum Thema Delegation - er ist sowohl sehr nützlich als auch sehr effizient. Mit der Delegierung verbrauchen Sie weniger Browser-Ressourcen als mit dem Anhängen eines Ereignisses an jedes übereinstimmende Element. Ich dachte, ich würde es erwähnen, nur für den Fall, dass die Leute mehr Gründe brauchen, um die Delegation zu nutzen.
Phatskat

6

Der erste Weg $("div#target span.green").on()bindet einen Klick-Handler direkt an die Spanne (n), die zum Zeitpunkt der Ausführung des Codes mit dem Selektor übereinstimmen. Dies bedeutet, dass andere Bereiche, die später hinzugefügt werden (oder deren Klasse entsprechend geändert wird), diese verpasst haben und keinen Klick-Handler haben. Wenn Sie später die "grüne" Klasse aus einem der Bereiche entfernen, wird der Klick-Handler weiterhin ausgeführt. JQuery verfolgt nicht, wie der Handler zugewiesen wurde, und prüft, ob der Selektor noch übereinstimmt.

Die zweite Möglichkeit besteht darin $("div#target").on(), einen Klick-Handler an die übereinstimmenden Divs zu binden (dies ist wiederum gegen diejenigen, die in diesem Moment übereinstimmen). Wenn jedoch irgendwo im Div ein Klick auftritt, wird die Handler-Funktion nur ausgeführt, wenn der Klick erfolgt trat nicht nur im div auf, sondern in einem .on()untergeordneten Element, das dem Selektor im zweiten Parameter "span.green" entspricht. Auf diese Weise spielt es keine Rolle, wann diese untergeordneten Bereiche erstellt wurden. Wenn Sie darauf klicken, wird der Handler weiterhin ausgeführt.

Bei einer Seite, die ihren Inhalt nicht dynamisch hinzufügt oder ändert, werden Sie keinen Unterschied zwischen den beiden Methoden feststellen. Wenn Sie zusätzliche untergeordnete Elemente dynamisch hinzufügen, müssen Sie sich nach der zweiten Syntax keine Gedanken mehr über das Zuweisen von Klick-Handlern machen, da Sie dies bereits einmal auf dem übergeordneten Element getan haben.


5

Die Erklärung von N3dst4 ist perfekt. Auf dieser Grundlage können wir davon ausgehen, dass sich alle untergeordneten Elemente im Körper befinden. Daher müssen wir nur Folgendes verwenden:

$('body').on('click', '.element', function(){
    alert('It works!')
});

Es funktioniert mit direkten oder delegierten Ereignissen.


2
jquery rät von der Verwendung von body ab, da es langsamer ist, da das Skript nach allen Kindern im Körper suchen müsste, was in den meisten Fällen sehr viel sein sollte. Es ist besser (schneller), den unmittelbaren übergeordneten Container des Elements zu verwenden.
Mikeoft

1
Jquery hat die Live-Methode entfernt, die genau so funktioniert, wie Sie es gezeigt haben. Dies ist leistungsschwach und sollte nicht verwendet werden.
Maciej Sikora

In modernen Browsern sollte sich die $('body').on()Delegierung von .elementgenauso verhalten wie die native Delegierung document.body.addEventHandler()mit einem if (Event.target.className.matches(/\belement\b/))im Rückruf. Aufgrund des $.proxyOverheads kann es bei der Abfrage etwas langsamer sein, aber zitieren Sie mich nicht dazu.
Cowbert

2

Tangential zum OP, aber das Konzept, das mir geholfen hat, die Verwirrung mit dieser Funktion zu lösen, ist, dass die gebundenen Elemente Eltern der ausgewählten Elemente sein müssen .

  • Gebunden bezieht sich auf das, was von der übrig bleibt .on.
  • Ausgewählt bezieht sich auf das 2. Argument von .on().

Die Delegierung funktioniert nicht wie .find () und wählt eine Teilmenge der gebundenen Elemente aus. Der Selektor gilt nur für strenge untergeordnete Elemente.

$("span.green").on("click", ...

ist ganz anders als

$("span").on("click", ".green", ...

Insbesondere um die Vorteile zu nutzen, die @ N3dst4 mit "Elementen, die in Zukunft erstellt werden" angibt, muss das gebundene Element ein permanentes übergeordnetes Element sein . Dann können die ausgewählten Kinder kommen und gehen.

BEARBEITEN

Checkliste, warum delegiert .onnicht funktioniert

Knifflige Gründe, warum $('.bound').on('event', '.selected', some_function)möglicherweise nicht funktionieren:

  1. Gebundenes Element ist nicht permanent . Es wurde nach dem Aufruf erstellt.on()
  2. Ausgewähltes Element ist kein richtiges Kind eines gebundenen Elements. Es ist das gleiche Element.
  3. Das ausgewählte Element verhinderte das Aufblasen eines Ereignisses auf das gebundene Element durch Aufrufen .stopPropagation().

(Auslassen weniger kniffliger Gründe wie eines falsch geschriebenen Selektors.)


1

Ich habe einen Beitrag mit einem Vergleich von direkten Ereignissen geschrieben und delegiert. Ich vergleiche reines js, aber es hat die gleiche Bedeutung für jquery, die es nur einkapselt.

Die Schlussfolgerung lautet, dass die delegierte Ereignisbehandlung für eine dynamische DOM-Struktur vorgesehen ist, bei der gebundene Elemente erstellt werden können, während der Benutzer mit der Seite interagiert (keine erneuten Bindungen erforderlich), und dass die direkte Ereignisbehandlung für statische DOM-Elemente gilt, wenn wir wissen, dass sich die Struktur nicht ändert.

Für weitere Informationen und einen vollständigen Vergleich - http://maciejsikora.com/standard-events-vs-event-delegation/

Die Verwendung von immer delegierten Handlern, von denen ich sehe, dass sie derzeit sehr trendy sind, ist nicht der richtige Weg. Viele Programmierer verwenden sie, weil "sie verwendet werden sollten". In Wahrheit sind direkte Ereignishandler für bestimmte Situationen besser und die Wahl, welche Methode verwendet werden soll durch Kenntnis der Unterschiede.


Wenn Sie viele Ereignishandler anhängen (z. B. jede Zeile einer Tabelle), ist es häufig leistungsmäßig besser, einen einzelnen delegierten Handler an den Container zu verwenden, anstatt viele direkte Handler anzuhängen. Sie können dies an der grundlegenden Tatsache erkennen, dass die Anzahl der Ereignishandler für sich genommen eine Profilmetrik ist.
Cowbert
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.