web-dev-qa-db-de.com

Was ist DOM Event Delegation?

Kann mir jemand bitte erklären Event Delegation in JavaScript und wie ist es nützlich?

176
Xenon

Die DOM-Ereignisdelegation ist ein Mechanismus zum Reagieren auf UI-Ereignisse über einen einzelnen gemeinsamen Elternteil und nicht über jedes Kind.

Wenn ein Ereignis für ein Element ausgelöst wird, Folgendes tritt auf :

Das Ereignis wird an sein Ziel EventTarget gesendet und alle dort gefundenen Ereignis-Listener werden ausgelöst. Bubbling Ereignisse lösen dann alle weiteren gefundenen Ereignis-Listener aus, indem sie der übergeordneten Kette von EventTarget folgen upward und nach Ereignis-Listenern suchen, die bei jedem aufeinanderfolgenden Ereignis registriert sind EventTarget. Diese Aufwärtsausbreitung wird bis einschließlich Document fortgesetzt.

Ereignis-Bubbling bietet die Grundlage für die Ereignis-Delegierung in Browsern. Jetzt können Sie einen Ereignishandler an ein einzelnes übergeordnetes Element binden. Dieser Handler wird ausgeführt, wenn das Ereignis auftritt auf einem seiner untergeordneten Knoten (und einem seiner untergeordneten Knoten). Dies ist eine Ereignisdelegation. Hier ist ein Beispiel davon in der Praxis:

<ul onclick="alert(event.type + '!')">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
</ul>

Wenn Sie in diesem Beispiel auf einen der untergeordneten <li> - Knoten klicken, wird die Warnung "click!" Angezeigt, obwohl kein an <li> Gebundener Klick-Handler vorhanden ist. Sie klickten auf. Wenn wir onclick="..." An jeden <li> Binden, erhalten Sie den gleichen Effekt.

Also, was ist der Vorteil?

Stellen Sie sich vor, Sie müssen jetzt dynamisch neue <li> - Elemente über die DOM-Manipulation zur obigen Liste hinzufügen:

var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);

Without Wenn Sie die Ereignisdelegation verwenden, müssen Sie die Ereignisbehandlungsroutine "onclick" Erneut an das neue Element <li> Binden, damit sie wie folgt funktioniert seine Geschwister. Mit Event Delegation müssen Sie nichts tun. Fügen Sie einfach den neuen <li> Zur Liste hinzu und Sie sind fertig.

Dies ist absolut fantastisch für Web-Apps mit Event-Handlern, die an viele Elemente gebunden sind und in denen neue Elemente dynamisch im DOM erstellt und/oder entfernt werden. Mit der Ereignisdelegierung kann die Anzahl der Ereignisbindungen drastisch reduziert werden, indem sie auf ein gemeinsames übergeordnetes Element verschoben werden. Außerdem kann Code, der dynamisch neue Elemente im laufenden Betrieb erstellt, von der Logik der Bindung der Ereignishandler abgekoppelt werden.

Ein weiterer Vorteil der Ereignisdelegierung besteht darin, dass der von Ereignis-Listenern verwendete Gesamtspeicherbedarf abnimmt (da die Anzahl der Ereignisbindungen abnimmt). Bei kleinen Seiten, die häufig entladen werden (d. H. Benutzer navigieren häufig zu verschiedenen Seiten), kann dies keinen großen Unterschied machen. Für langlebige Anwendungen kann dies jedoch von Bedeutung sein. Es gibt einige wirklich schwer auffindbare Situationen, in denen aus dem DOM entfernte Elemente immer noch Speicher beanspruchen (d. H. Sie lecken), und oft ist dieser lecken Speicher an eine Ereignisbindung gebunden. Mit der Ereignisdelegation können Sie untergeordnete Elemente zerstören, ohne zu vergessen, die Bindung ihrer Ereignislistener aufzuheben (da der Listener dem Vorfahren angehört). Diese Arten von Speicherlecks können dann eingedämmt werden (wenn nicht beseitigt, was manchmal verdammt schwierig ist. IE Ich sehe dich an).

Hier sind einige konkretere Codebeispiele für die Delegierung von Ereignissen:

289
Crescent Fresh

Mit der Ereignisdelegierung können Sie das Hinzufügen von Ereignis-Listenern zu bestimmten Knoten vermeiden. Stattdessen wird der Ereignis-Listener einem übergeordneten Element hinzugefügt. Dieser Ereignis-Listener analysiert Bubble-Ereignisse, um eine Übereinstimmung für untergeordnete Elemente zu finden.

JavaScript Beispiel:

Angenommen, wir haben ein übergeordnetes UL-Element mit mehreren untergeordneten Elementen:

<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>

Sagen wir auch, dass etwas passieren muss, wenn auf jedes untergeordnete Element geklickt wird. Sie können jedem einzelnen LI-Element einen eigenen Ereignis-Listener hinzufügen. Was passiert jedoch, wenn LI-Elemente häufig hinzugefügt und aus der Liste entfernt werden? Das Hinzufügen und Entfernen von Ereignis-Listenern wäre ein Albtraum, insbesondere wenn sich der Code zum Hinzufügen und Entfernen an verschiedenen Stellen in Ihrer App befindet. Die bessere Lösung besteht darin, dem übergeordneten UL-Element einen Ereignis-Listener hinzuzufügen. Wenn Sie jedoch den Ereignis-Listener zum übergeordneten Element hinzufügen, woher wissen Sie, auf welches Element geklickt wurde?

Einfach: Wenn das Ereignis das UL-Element erreicht, überprüfen Sie die Zieleigenschaft des Ereignisobjekts, um einen Verweis auf den tatsächlich angeklickten Knoten zu erhalten. Hier ist ein sehr einfaches JavaScript-Snippet, das die Delegation von Ereignissen veranschaulicht:

// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
if(e.target && e.target.nodeName == "LI") {
    // List item found!  Output the ID!
    console.log("List item ", e.target.id.replace("post-"), " was clicked!");
       }
 });

Fügen Sie dem übergeordneten Element zunächst einen Listener für Klickereignisse hinzu. Wenn der Ereignis-Listener ausgelöst wird, überprüfen Sie das Ereigniselement, um sicherzustellen, dass es der Elementtyp ist, auf den reagiert werden soll. Wenn es sich um ein LI-Element handelt, boom: Wir haben, was wir brauchen! Wenn es sich nicht um ein Element handelt, das wir möchten, kann das Ereignis ignoriert werden. Dieses Beispiel ist ziemlich einfach - UL und LI sind ein direkter Vergleich. Versuchen wir etwas schwierigeres. Lassen Sie uns einen Eltern-DIV mit vielen Kindern haben, aber alles, was uns interessiert, ist ein A-Tag mit der classA-CSS-Klasse:

  // Get the parent DIV, add click listener...
  document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target was the clicked element
if(e.target && e.target.nodeName == "A") {
    // Get the CSS classes
    var classes = e.target.className.split(" ");
    // Search for the CSS class!
    if(classes) {
        // For every CSS class the element has...
        for(var x = 0; x < classes.length; x++) {
            // If it has the CSS class we want...
            if(classes[x] == "classA") {
                // Bingo!
                console.log("Anchor element clicked!");
                // Now do something here....
            }
        }
    }

  }
});

http://davidwalsh.name/event-delegate

27
Osama AbuSitta

dom event delegation ist etwas anderes als die Definition der Informatik.

Es bezieht sich auf die Behandlung von Bubbling-Ereignissen aus vielen Elementen, wie beispielsweise Tabellenzellen, aus einem übergeordneten Objekt, wie beispielsweise der Tabelle. Dies vereinfacht den Code, insbesondere beim Hinzufügen oder Entfernen von Elementen, und spart Speicherplatz.

7
kennebec

Delegation ist eine Technik, bei der ein Objekt ein bestimmtes Verhalten nach außen ausdrückt, in Wirklichkeit jedoch die Verantwortung für die Implementierung dieses Verhaltens auf ein zugeordnetes Objekt delegiert. Dies klingt zunächst sehr ähnlich wie das Proxy-Muster, erfüllt jedoch einen ganz anderen Zweck. Die Delegierung ist ein Abstraktionsmechanismus, der das Verhalten von Objekten (Methoden) zentralisiert.

Allgemein gesprochen: Verwenden Sie die Delegierung als Alternative zur Vererbung. Vererbung ist eine gute Strategie, wenn eine enge Beziehung zwischen übergeordnetem und untergeordnetem Objekt besteht, die Vererbung jedoch Objekte sehr eng miteinander verbindet. Delegation ist oft die flexiblere Möglichkeit, eine Beziehung zwischen Klassen auszudrücken.

Dieses Muster wird auch als "Proxy-Ketten" bezeichnet. Einige andere Entwurfsmuster verwenden die Delegierung - der Status, die Strategie und die Besuchermuster hängen davon ab.

6
Ewan Todd

Das Delegationskonzept

Wenn sich in einem übergeordneten Element viele Elemente befinden und Sie Ereignisse auf diesen Elementen verarbeiten möchten, binden Sie keine Handler an die einzelnen Elemente. Binden Sie stattdessen den einzelnen Handler an den übergeordneten Handler und rufen Sie den untergeordneten Handler von event.target ab. Diese Website bietet nützliche Informationen zum Implementieren der Ereignisdelegierung. http://javascript.info/tutorial/event-delegation

5
Joseph

Die Ereignisdelegierung behandelt ein Ereignis, das bubbles einen Ereignishandler für ein Containerelement verwendet, aber das Verhalten des Ereignishandlers nur aktiviert, wenn das Ereignis für ein Element im Container eingetreten ist, das einer bestimmten Bedingung entspricht. Dies kann die Behandlung von Ereignissen an Elementen im Container vereinfachen.

Angenommen, Sie möchten einen Klick auf eine Tabellenzelle in einer großen Tabelle verarbeiten. Sie können could eine Schleife schreiben, um einen Klick-Handler an jede Zelle anzuschließen ... oder Sie können einen Klick-Handler an die Tabelle anschließen und die Ereignisdelegierung verwenden, um sie nur für Tabellenzellen auszulösen (und keine Tabellenüberschriften oder Leerzeichen in einer Zeile um Zellen usw.).

Dies ist auch nützlich, wenn Sie Elemente zum Container hinzufügen und daraus entfernen möchten, da Sie sich nicht um das Hinzufügen und Entfernen von Ereignishandlern für diese Elemente kümmern müssen. Haken Sie einfach das Ereignis in den Container ein und behandeln Sie das Ereignis, wenn es sprudelt.

Hier ist ein einfaches Beispiel (es ist absichtlich ausführlich, um Inline-Erklärungen zu ermöglichen): Behandlung eines Klicks auf ein td -Element in einer Containertabelle:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    // Find out if the event targeted or bubbled through a `td` en route to this container element
    var element = event.target;
    var target;
    while (element && !target) {
        if (element.matches("td")) {
            // Found a `td` within the container!
            target = element;
        } else {
            // Not found
            if (element === this) {
                // We've reached the container, stop
                element = null;
            } else {
                // Go to the next parent in the ancestry
                element = element.parentNode;
            }
        }
    }
    if (target) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<table id="container">
    <thead>
        <tr>
            <th>Language</th>
            <th>1</th>
            <th>2</th>
            <th>3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th class="rowheader">English</th>
            <td>one</td>
            <td>two</td>
            <td>three</td>
        </tr>
        <tr>
            <th class="rowheader">Español</th>
            <td>uno</td>
            <td>dos</td>
            <td>tres</td>
        </tr>
        <tr>
            <th class="rowheader">Italiano</th>
            <td>uno</td>
            <td>due</td>
            <td>tre</td>
        </tr>
    </tbody>
</table>

Bevor wir auf die Details eingehen, wollen wir uns daran erinnern, wie DOM-Ereignisse funktionieren.

DOM-Ereignisse werden vom Dokument an das Zielelement gesendet ( Erfassung Phase) und dann vom Zielelement zurück zum Dokument geleitet ( Blasenbildung Phase ). Diese Grafik in der alten DOM3 events spec (jetzt ersetzt, aber die Grafik ist noch gültig) zeigt es wirklich gut:

enter image description here

Nicht alle Ereignisse sprudeln, aber die meisten, einschließlich click.

Die Kommentare im obigen Codebeispiel beschreiben, wie es funktioniert. matches prüft, ob ein Element mit einem CSS-Selektor übereinstimmt, aber Sie können natürlich auch überprüfen, ob etwas auf andere Weise Ihren Kriterien entspricht, wenn Sie keinen CSS-Selektor verwenden möchten .

Dieser Code wurde geschrieben, um die einzelnen Schritte ausführlich aufzurufen, aber in vage modernen Browsern (und auch in IE wenn Sie eine Polyfüllung verwenden) können Sie closest und contains anstelle der Schleife:

var target = event.target.closest("td");
    console.log("You clicked a td: " + target.textContent);
} else {
    console.log("That wasn't a td in the container table");
}

Live Beispiel:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    var target = event.target.closest("td");
    if (target && this.contains(target)) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<table id="container">
    <thead>
        <tr>
            <th>Language</th>
            <th>1</th>
            <th>2</th>
            <th>3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th class="rowheader">English</th>
            <td>one</td>
            <td>two</td>
            <td>three</td>
        </tr>
        <tr>
            <th class="rowheader">Español</th>
            <td>uno</td>
            <td>dos</td>
            <td>tres</td>
        </tr>
        <tr>
            <th class="rowheader">Italiano</th>
            <td>uno</td>
            <td>due</td>
            <td>tre</td>
        </tr>
    </tbody>
</table>

closest überprüft das Element, für das Sie es aufrufen, um festzustellen, ob es mit dem angegebenen CSS-Selektor übereinstimmt, und gibt in diesem Fall dasselbe Element zurück. Wenn nicht, überprüft es das übergeordnete Element, um festzustellen, ob es übereinstimmt, und gibt das übergeordnete Element zurück, wenn dies der Fall ist. Ist dies nicht der Fall, wird das übergeordnete Element des übergeordneten Elements usw. überprüft. Daher wird in der Vorfahrenliste das "nächstgelegene" Element gefunden, das der Auswahl entspricht. Da dies möglicherweise über das Containerelement hinausgeht, überprüft der obige Code mithilfe von contains, ob ein übereinstimmendes Element im Container gefunden wurde. Durch das Verknüpfen des Ereignisses mit dem Container haben Sie angegeben, dass Sie nur möchten Elemente zu behandeln innerhalb diesem Container.

Zurück zu unserem Tabellenbeispiel: Wenn Sie eine Tabelle in einer Tabellenzelle haben, stimmt diese nicht mit der Tabellenzelle überein, die die Tabelle enthält:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    var target = event.target.closest("td");
    if (target && this.contains(target)) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<!-- The table wrapped around the #container table -->
<table>
    <tbody>
        <tr>
            <td>
                <!-- This cell doesn't get matched, thanks to the `this.contains(target)` check -->
                <table id="container">
                    <thead>
                        <tr>
                            <th>Language</th>
                            <th>1</th>
                            <th>2</th>
                            <th>3</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <th class="rowheader">English</th>
                            <td>one</td>
                            <td>two</td>
                            <td>three</td>
                        </tr>
                        <tr>
                            <th class="rowheader">Español</th>
                            <td>uno</td>
                            <td>dos</td>
                            <td>tres</td>
                        </tr>
                        <tr>
                            <th class="rowheader">Italiano</th>
                            <td>uno</td>
                            <td>due</td>
                            <td>tre</td>
                        </tr>
                    </tbody>
                </table>
            </td>
            <td>
                This is next to the container table
            </td>
        </tr>
    </tbody>
</table>
4
T.J. Crowder

Es ist im Grunde, wie die Assoziation zum Element hergestellt wird. .click gilt für das aktuelle DOM, während .on (mit Delegierung) weiterhin für neue Elemente gilt, die nach der Ereigniszuordnung zum DOM hinzugefügt wurden.

Welches ist besser zu verwenden, würde ich sagen, es hängt vom Fall ab.

Beispiel:

<ul id="todo">
   <li>Do 1</li>
   <li>Do 2</li>
   <li>Do 3</li>
   <li>Do 4</li>
</ul>

.Klick Event:

$("li").click(function () {
   $(this).remove ();
});

Ereignis .on:

$("#todo").on("click", "li", function () {
   $(this).remove();
});

Beachten Sie, dass ich den Selektor in der .on getrennt habe. Ich erkläre warum.

Nehmen wir an, dass wir nach dieser Assoziation Folgendes tun:

$("#todo").append("<li>Do 5</li>");

Hier werden Sie den Unterschied bemerken.

Wenn das Ereignis über .click verknüpft wurde, wird Task 5 dem Klickereignis nicht gehorchen und wird daher nicht entfernt.

Wenn es über .on verknüpft wurde und der Selektor separat ist, wird es gehorchen.

2
Lucas

Ein Delegat in C # ähnelt einem Funktionszeiger in C oder C++. Durch die Verwendung eines Delegaten kann der Programmierer einen Verweis auf eine Methode in einem Delegatenobjekt einkapseln. Das Delegate-Objekt kann dann an den Code übergeben werden, der die referenzierte Methode aufrufen kann, ohne beim Kompilieren wissen zu müssen, welche Methode aufgerufen wird.

Siehe diesen Link -> http://www.akadia.com/services/dotnet_delegates_and_events.html

1
Bhaskar

Die Ereignisdelegierung nutzt zwei häufig übersehene Funktionen von JavaScript-Ereignissen: das Ereignis-Bubbling und das Zielelement. Wenn ein Ereignis für ein Element ausgelöst wird, z. B. ein Mausklick auf eine Schaltfläche, wird dasselbe Ereignis auch für alle Vorfahren dieses Elements ausgelöst . Dieser Vorgang wird als Ereignis-Bubbling bezeichnet. Das Ereignis sprudelt vom Ursprungselement zum oberen Rand des DOM-Baums.

Stellen Sie sich eine HTML-Tabelle mit 10 Spalten und 100 Zeilen vor, in der etwas geschehen soll, wenn der Benutzer auf eine Tabellenzelle klickt. Zum Beispiel musste ich einmal jede Zelle einer Tabelle dieser Größe bearbeitbar machen, wenn ich darauf klickte. Das Hinzufügen von Ereignishandlern zu jeder der 1000 Zellen wäre ein schwerwiegendes Leistungsproblem und möglicherweise eine Ursache für Speicherlecks, bei denen der Browser abstürzt. Stattdessen würden Sie mithilfe der Ereignisdelegierung dem Tabellenelement nur einen Ereignishandler hinzufügen, das Klickereignis abfangen und bestimmen, auf welche Zelle geklickt wurde.

1
Priti jha