Geschrieben von

Event-Bubbling und -Capturing

WebDev

Wie in diesem Artikel beschrieben, gibt es beim Event-Handling 3 Techniken, um Events zu registrieren. Die Methode addEventListener ist dabei mittlerweile Standard, wenn man allen Elementobjekten einen Event-Handler zuweisen möchte. In diesem Artikel gehe ich auf die Event-Phasen Bubbling und Capturing näher ein.

Aufbau von addEventListener

Um einem Element einen Event-Handler mit addEventListener zuzuweisen, lautet die Syntax bspw. folgendermaßen:

element.addEventListener('submit', macheEtwas, false);

Nach dem Element-Objekt und dem Aufruf von addEventListener besitzt die Methode 3 Parameter:

  • Ereignistyp: Hier wird festgelegt, wann addEventListener die Funktion ausführen soll. Beispiele sind “click” oder “submit”.
  • Funktion: Im zweiten Parameter wird die Funktion, die ausgeführt werden soll, mitgegeben.
  • Event-Phase: Hier wird die Event-Phase bestimmt. Diese wird mit einem Boolean-Parameter (true oder false) angegeben. false steht für die Event-Bubbling, true für Event-Capturing.

Event-Bubbling und -Capturing sind also 2 mögliche Phasen innerhalb des addEventListener. Bevor wir hier näher eintauchen, müssen wir zunächst das Prinzip der Event-Delegation verstehen.

Event-Delegation

Event-Delegation kommt zum Einsatz, wenn man für gleiche Elemente im DOM Events registrieren will. Dabei muss nicht jedes Element einzeln angesprochen werden, sondern kann mittels einer globalen Funktion eine Aktion ausführen. Hier wird einem zentralen HTML-Element eine Funktion übergeben, die dann für alle untergeordneten Elemente gilt.

Beispiel: In einer ul-Liste gibt es 10 Links. Diese sollen beim Klick ein JavaScript ausführen. Statt jeden dieser 10 Links eine Event-Funktion zuzuweisen, kann man mittels Event-Delegation nur das ul-Element ansteuern und festlegen, dass beim Klick auf Links (= Ziel-Element) innerhalb genau dieser ul-Liste etwas passieren soll.

Event-Phasen

Nun gibt es in der Event-Delegation verschiedene Event-Phasen, die bestimmen, wie Events im DOM-Baum registriert werden können und eine Aktion beim Ziel-Element ausführen. Spricht man von Event-Phasen so beginnt der DOM-Baum beim globalen Objekt window und endet dann beim Zielobjekt. Hier werden dann folgende Phasen durchlaufen:

  1. Event-Capturing: Hier erfolgt ein Absteigen zum Ziel-Element. Das Event steigt in der DOM-Hierarchie von der höchsten Ebene im DOM-Baum (window) bis zum Ziel-Element ab. Wenn auf dem Weg ein Event registriert wird, löst es die Funktion aus.
  2. Event-Targeting: Beim Ziel-Element angekommen wird die Funktion ausgeführt und geht nun in die Bubbling-Phase über.
  3. Event-Bubbling: Hier erfolgt dann ein Aufsteigen vom Ziel-Element hinauf zum Ausgangspunkt (window) und löst bei den Vorfahren-Elementen, wo das Event registriert wird, die Funktion aus.

Praktisches Beispiel und stopPropagation

Gehen wir vom folgenden HTML-Code aus:

<div>
<p>Hier steht Text</p>
<ul>
<li>Listenpunkt 1</liv>
<li>Listenpunkt 2</li>
</ul>
</div>

Im Frontend würde das mit etwas CSS so aussehen:

Hier steht Text

  • Listenpunkt 1
  • Listenpunkt 2

Gehen wir davon aus wir legen ein Event auf das Element ul. Klickt man nun auf einen Listenpunkt (li), dann würde man sich erwarten, dass das Event auch auslöst. Das ist auch der Fall. Beim Bubbling steigt beim Klick das Event auf und löst bei dem höher liegenden Element aus. Beim Bubbling werden also innerhalb eines Elements auch alle Kindelemente überwacht. Man unterscheidet hier in Ziel-Element und verarbeitetes Element. Das Ziel-Element wäre in diesem Fall li und das verarbeitete Element ul, da auf ul der Event-Handler liegt.

Möchte man nun auf Informationen der ausgelösten Events zugreifen, muss man auch zwischen Ziel-Element und verarbeitetes Element unterscheiden:

  • Mit target wird auf das Ziel-Element zugegriffen.
  • Mit currentTarget wird auf das verarbeitete Element zugegriffen.

Kehren wir nochmal zu unserer div-Box zurück:

Hier steht Text

  • Listenpunkt 1
  • Listenpunkt 2

Würde man einen Event-Handler auf dem Element div registrieren, dann wäre dieses Element immer das verarbeitete Element. Je nachdem, wo das Event dann stattfindet, spricht man vom Ziel-Element. Beispiele:

  • Nutzer klickt auf li: Das Event steigt hoch und löst bei div aus. div ist das verarbeitete Element, li das Ziel-Element.
  • Nutzer klickt auf p: Das Event steigt hoch und löst bei div aus. div ist das verarbeitete Element, p das Ziel-Element.

Würde man nun auf li und div ein Event registrieren, hätte das bei Bubbling die Folge, dass es beide Male ausgelöst wird. Zum einen direkt auf dem li und zum anderen aufgrund des Bubblings auf dem div. In solchen Fällen kann man das mit der Methode stopPropagation verhindern.

Google Tag Manager und Event-Phasen

Die Google Tag Manager Klick-Trigger haben ihren Ausgangspunkt beim Knoten “document”. Im GTM hat man 2 verschiedene Klick-Trigger zur Auswahl:

  • Alle Elemente
  • Nur Links

Hinsichtlich Event-Delegation gibt es hier bestimmte Besonderheiten, die zu beachten sind.

Trigger: Nur Links
Wenn hier ein Event auf einem Element registriert wird, dann wandert dieses hoch zum “document” und löst dann aus. Der “Nur Links”-Trigger arbeitet also mit Event-Bubbling. Gehen wir davon aus, wir haben diesen Trigger im Einsatz. Immer wenn ein Nutzer auf ein a-Tag klickt, wird dieser ausgelöst. Unser Link wäre zudem folgendermaßen beispielhaft im DOM verschachtelt:
Google Tag Manger Event-Phasen

Bei einem Klick auf das a-Tag würde der Trigger nicht auf Diesem direkt auslösen, sondern erst ganz nach oben zum Document-Knoten wandern und dann feuern:
Google Tag Manager Event-Phasen

Es kann jedoch sein, dass mit der Anweisung .stopPropagation() das Event-Bubbling gestoppt wird. Sprich, bei einem Klick auf einen Link wäre Google Tag Manager nicht mehr in der Lage, das Event auszulösen:
Google Tag Manager Event-Phasen

Es gibt jedoch neben .stopPropagation() auch die Anweisung .preventDefault(). Wenn .preventDefault() im Einsatz ist, kann der GTM weiterhin Events abfangen. Wird aber in jQuery “return false” gesetzt, so kann der GTM keine Events auslösen.

Trigger: Alle Elemente
Das oben genannte Problem mit .stopPropagation() beim “Nur Links”-Event besteht beim Klick-Trigger “Alle Elemente” nicht. Dieser Trigger arbeitet nicht mit der Bubbling-Phase, sondern mit Capturing. Wenn also – aus welchen Gründen auch immer – die Event-Delegation gestoppt wird und vom Entwickler-Team nicht rausgenommen werden kann, so hat man die Möglichkeit mit dem Alle-Elemente-Trigger zu arbeiten. Diesen kann man dann auch so definieren, dass auf Links reagiert wird.