Wenn zahlreiche Elemente im Dokument überwacht werden sollen, ist es sehr aufwändig und langsam, diese herauszusuchen und bei jedem denselben Event-Handler zu registrieren. Bei einer solchen Aufgabenstellung können Sie vom Bubbling-Effekt profitieren, das ist das Aufsteigen der Ereignisse im DOM-Baum.
Man macht sich die Verschachtelung der Elemente im DOM-Baum zunutze und überwacht die Ereignisse von verschiedenen Elementen bei einem gemeinsamen, höherliegenden Element, zu dem die Ereignisse aufsteigen. Diese Technik nennt sich Event-Delegation (englisch delegation für Übertragung von Aufgaben). Dabei wird einem zentralen Element die Aufgabe übertragen, die Ereignisse zu verarbeiten, die bei seinen Nachfahrenelementen passieren.
Event-Delegation eignet sich insbesondere dann, wenn viele gleichförmige Elemente in Menüs, Link-Listen, Formularen oder Tabellen JavaScript-Interaktivität benötigen. Ohne Event-Delegation müsste man jedes Element einzeln ansprechen, um dort immer denselben Event-Handler zu registrieren.
Nehmen wir beispielsweise eine Liste mit Links zu Bildern. Wenn JavaScript aktiv ist, soll das Vollbild dokumentintern eingeblendet werden. Ein ähnliches Beispiel hatten wir bereits beim Unterdrücken der Standardaktion – die Umsetzung der Einblendung bleibt weiterhin ausgeklammert.
Wir gehen von folgendem HTML-Gerüst aus:
<ul id="bilderliste"> <li> <a href="bilder/bild1.jpg"> <img src="bilder/thumbnail1.jpg" alt=""> Ebru und Robin auf dem Empire State Building </a> </li> <li> <a href="bilder/bild2.jpg"> <img src="bilder/thumbnail2.jpg" alt=""> Noël und Francis vor dem Taj Mahal </a> </li> <li> <a href="bilder/bild3.jpg"> <img src="bilder/thumbnail3.jpg" alt=""> Isaak und Ahmet vor den Pyramiden von Gizeh </a> </li> <!-- … viele weitere Links mit Thumbnails … --> </ul>
Beim Klick auf einen der Links soll nun das verlinkte Bild eingeblendet werden. Anstatt jedem a
-Element einzeln einen Handler zuzuweisen, registrieren wir ihn beim gemeinsamen Vorfahrenelement ul
mit der ID bilderliste
:
document.getElementById('bilderliste') .addEventListener('click', bilderlistenKlick, false);
In der angegebenen Handler-Funktion bilderlistenKlick
findet nun die Überprüfung des Zielelementes statt.
function bilderlistenKlick (event) { var elementName = event.target.nodeName; var aElement = null; // Überprüfe, ob das Zielelement ein Link oder ein Bild im Link ist: if (elementName === 'A') { // Falls ein Link geklickt wurde, speichere das Zielelement // in der Variable aElement: aElement = event.target; } else if (elementName === 'IMG') { // Falls das Thumbnail-Bild geklickt wurde, suche // das zugehörige Link-Element. Es ist das Elternelement: aElement = event.target.parentNode; } // Zeige das Vollbild, wenn das Zielelement // ein Link ist oder in einem Link liegt: if (aElement) { zeigeVollbild(aElement); // Unterdrücke die Standardaktion: event.preventDefault(); } // Andernfalls mache nichts. }
In dieser Funktion wird das Zielelement des Ereignisses angesprochen und dessen Elementname überprüft. Wenn ein a
-Element geklickt wurde, muss es sich um einen Link auf ein Bild handeln und das Vollbild soll eingeblendet werden.
Das alleine wäre bereits mit der Abfrage if (event.target.nodeName === 'A')
zu erledigen. Das Beispiel hat allerdings bewusst eine Schwierigkeit eingebaut, um Ihnen das Event-Bubbling und das Arbeiten mit dem Zielelement näher zu bringen: In den a
-Elementen liegen zusätzlich img
-Elemente für die Thumbnails. Wenn der Anwender auf diese klickt, soll das Vollbild ebenfalls eingeblendet werden. In dem Fall ist jedoch nicht das a
-Element das Zielelement, sondern das img
-Element.
Aus diesem Grund muss die Abfrage erweitert werden: Handelt es sich um ein a
-Element oder um ein Element, das darin liegt? Falls das Zielelement ein img
-Element ist, steigen wir mittels parentNode
zu seinem a
-Elternelement auf.
Falls ein a
-Element ermittelt werden könnte, wird schließlich die Funktion zeigeVollbild
mit dem Elementobjekt als Parameter aufgerufen. Das Gerüst dieser Funktion sieht so aus:
function zeigeVollbild(aElement) { // Empfange das Elementobjekt als ersten Parameter und // lese dessen href-Attribut mit der Bild-Adresse aus: var bildAdresse = aElement.href; // Blende das Bild ein, auf das der Link zeigt. // Die genaue Umsetzung ist an dieser Stelle ausgeklammert.) window.alert(bildAdresse); }
Dieses Beispiel soll Ihnen die grundlegende Funktionsweise von Event-Delegation veranschaulichen:
Wie Sie schon bei diesem einfachen Beispiel sehen, ist eine aufwändige Untersuchung des DOM-Elementbaumes rund um das Zielelement nötig. Bei Event-Delegation stellt sich oft die Frage, ob das Zielelement in einem anderen Element enthalten ist, auf das gewisse Kriterien zutreffen. Eine allgemeinere und vielseitig einsetzbare Lösung werden Sie später noch kennenlernen.
Capturing (englisch für Einfangen) ist eine Phase beim Event-Fluss. Wir haben sie bereits kurz angesprochen, ohne sie näher zu erklären. Der DOM-Standard definiert drei Phasen, in denen ein Ereignis durch den DOM-Elementbaum wandert (Event-Fluss genannt) und Handler auslöst:
Alle bisher beschriebenen Modelle, ausgehend vom traditionellen über W3C DOM Events und dem Microsoft-Modell, haben Handler für die Bubbling-Phase registriert. Wie wir uns das Bubbling zunutze machen, haben wir bereits bei der Event-Delegation kennengelernt.
Capturing ist ein weiterer Ansatz, um Ereignisse effizient zu überwachen: Wir können ein Event-Handler bei einem höherliegenden Element registrieren, um die Ereignisse zu überwachen, die bei vielen Nachfahrenelemente passieren.
Der Unterschied zwischen Bubbling und Capturing folgender: Nicht alle Ereignisse haben eine Bubbling-Phase. Das heißt, nicht alle Ereignisse steigen auf und lösen die entsprechenden Handler bei ihren Vorfahrenelementen aus. Das hat durchaus seinen Sinn, macht aber die beschriebene Event-Delegation unmöglich. Gäbe es das Event-Capturing nicht, wären Sie gezwungen, alle nicht aufsteigenden Ereignisse direkt bei ihren Zielelementen zu überwachen. Mithilfe des Event-Capturings können Sie auch solche Ereignisse zentral überwachen – denn jedes Ereignis hat eine Capturing-Phase.
Event-Capturing ist nur unter Verwendung der standardisierten Methode addEventListener
möglich. Um Event-Handler für die Capturing-Phase zu registrieren, nutzen Sie wie gewohnt addEventListener
, setzen jedoch den dritten Boolean-Parameter auf true
:
document.addEventListener("focus", captureHandler, true);
Die Vorteile des Capturings liegen darin, nicht aufsteigende Ereignisse bei einem höherliegenden Element zu verarbeiten.
Folgende Ereignisse beispielsweise steigen nicht auf:
load
, z.B. bei Bildern, Objekten und Iframesfocus
und blur
. (Als alternative aufsteigende Ereignisse gibt es allerdings focusin
und focusout
.)mouseenter
und mouseleave
. (Die Ereignisse mouseover
und mouseout
hingegen steigen auf.)Da die Capturing-Phase die erst im Event-Fluss ist, ist es möglich, ein Ereignis schon in dieser Phase abzufangen. Ruft man in der Capturing-Phase die stopPropation
-Methode des Event-Objekts auf, so wird der Fluss abgebrochen und die Ziel- und Bubbling-Phase fallen aus. Das Ereignis erreicht somit das Zielelement nicht.