Orlok: Event nicht weiterreichen stopPropagation

Beitrag lesen

Hallo Linuchs

Danke für deine sehr ausführliche Darlegung.

Offenbar nicht ausführlich genug. – Aber daran lässt sich arbeiten. ;-)

Aber ich habe schon den addEventListener nicht verstanden.

Verstanden hast du sicher, dass alle Elemente eines HTML-Dokumentes, inklusive ihrer Attribute und Inhalte, beim Parsen in eine entsprechende Repräsentation des DOM übersetzt werden. Das heißt, sie werden in einer Objektstruktur abgebildet, auf die du über entsprechende Schnittstellen mittels JavaScript zugreifen kannst.

Bestimmte auf diese Weise von der Ausführungsumgebung erzeugte Objekte werden nun automatisch mit der Methode addEventListener ausgestattet, und zwar alle HTML-Elemente sowie zum Beispiel auch das globale Objekt window. Oder auch das Objekt document, welches eine Eigenschaft des globalen Objektes ist und welches das jeweilige HTML-Dokument repräsentiert.

Wenn du nun diese Methode auf einem entsprechenden Objekt aufrufst, dann sind mindestens zwei Argumente zu übergeben, nämlich zuerst ein String, der den Namen des Ereignisses enthält welches du überwachen willst, sowie als zweites eine Funktion – oder eine Referenz auf eine solche, die dann aufgerufen werden soll, wenn das Ereignis schließlich eintritt – den Eventhandler.

<body>
  <button type="button">Push</button>
  <script>

    var button = document.body.firstElementChild;
    button.addEventListener('click', handler);

    function handler (event) {
      console.log(this === event.currentTarget); // true
    };

  </script>
</body>

In diesem Beispiel haben wir drei HTML-Elemente notiert, nämlich body und dessen zwei Kindelemente button und script. Innerhalb des Scripts haben wir dann zunächst das button-Element referenziert und einer gleichnamigen Variable als Wert zugewiesen. Dabei haben wir uns den Umstand zu Nutze gemacht, dass das Objekt, welches das body-Element repräsentiert, in der Eigenschaft mit demselben Namen des Objektes document hinterlegt ist. Da unser button-Element zudem das erste Kindelement von body ist, kann es hier über die Eigenschaft firstElementChild von body referenziert werden.

Die nächste Anweisung besteht nun darin, einen Event-Listener für das button-Objekt zu registrieren.

Wir referenzieren dieses also zunächst über den Bezeichner der zuvor definierte Variable und rufen dann die Methode addEventListener auf dem Objekt auf, wobei wir den String 'click' als erstes Argument übergeben und eine Referenz auf die nachfolgend definierte Funktion mit dem Bezeichner handler als zweites Argument. Damit haben wir dann die Funktion handler als Eventhandler registriert für den Button und das Ereignis click. Wenn nun auf den Button geklickt wird, dann wird die Funktion handler automatisch aufgerufen.

That’s it!

Beziehungsweise noch nicht ganz. ;-) Denn wir sollten noch einen Blick auf die Funktion handler werfen, für die nämlich ein Parameter mit dem (willkürlichen) Bezeichner event deklariert wurde.

Wie ich in meinem ersten Posting schon erwähnte, wird beim Aufruf einer als Eventhandler registrierten Funktion automatisch das Objekt als Argument übergeben, welches das Ereignis repräsentiert – das sogenannte Event-Objekt – über dessen Eigenschaften verschiedene Informationen über das entsprechende Ereignis abrufbar sind. Das heißt, wenn die Funktion handler aus dem Beispiel bei einem Klick auf den Button aufgerufen wird, dann wird der deklarierte Parameter event beim Funktionsaufruf automatisch mit diesem Objekt initialisiert.

Die Eigenschaft currentTarget des Event-Objektes enthält nun immer eine Referenz auf das Objekt, für welches der aufgerufene Eventhandler registriert wurde, ebenso wie die Funktionsvariable this, weshalb in unserem Beispiel als Ergebnis des Vergleichs der Wert true in die Konsole geschrieben wird.

Nun wandeln wir das Ursprungsbeispiel einmal etwas ab:

<body>
  <button type="button">Push</button>
  <script>

    document.body.addEventListener('click', function (event) {
      console.log(event.target.tagName); // BODY oder BUTTON
      console.log(event.currentTarget.tagName); // BODY
    });

  </script>
</body>

Hier haben wir das selbe HTML, nur ein etwas anderes Script. Statt die Methode addEventListener für das Objekt zu registrieren, welches das button-Element repräsentiert, registrieren wir ihn hier direkt für body. Außerdem referenzieren wir keine anderswo definierte Funktion sondern übergeben der Methode bei ihrem Aufruf direkt ein Funktionsobjekt, wobei für die Funktion wieder ein Parameter event deklariert wird.

Im Funktionskörper dieser anonymen Funktion ist nun zunächst die Anweisung enthalten, den Wert der Eigenschaft tagName des Objektes in die Konsole zu schreiben, welches in der Eigenschaft target des beim Aufruf übergebenen Event-Objektes hinterlegt ist. Darüber hinaus ist die Anweisung enthalten, das gleiche mit dem Wert der Eigenschaft currentTarget des Event-Objektes zu machen.

Da currentTarget immer auf das Objekt verweist, für welches der aufgerufene Eventhandler registriert wurde, wird aufgrund dieser zweiten Anweisung bei jedem Klick irgendwo innerhalb von body entsprechend der String BODY in die Konsole geschrieben. Was davor ausgegeben wird, hängt jedoch davon ab, wohin geklickt wurde!

Denn der Wert der Eigenschaft target zeigt immer auf das Objekt, bei dem das Ereignis tatsächlich eingetreten ist. Wird nun also auf den Button geklickt, so ist das Objekt welches das button-Element repräsentiert als Wert dieser Eigenschaft hinterlegt, wenn hingegen auf body geklickt wird, so wird dieses Objekt referenziert. Entsprechend wird also, je nach dem wohin geklickt wurde, zuerst entweder BODY oder BUTTON ausgegeben.

Darüber hinaus ist außerdem zu beachten, dass wenn nun auf den Button geklickt wird, schließlich sowohl BUTTON als auch BODY in die Konsole geschrieben wird, denn das Ereignis tritt nicht nur isoliert beim Zielobjekt auf, sondern wandert durch die Baumstruktur des DOM.

Sprich, es passiert zunächst in absteigender Richtung das globale Objekt window, das Objekt document, dann das in der Eigenschaft documentElement von document hinterlegte Objekt, welches das HTML-Element repräsentiert – und dann dessen Kindelement body. Das ist die Capturing-Phase. Diese wird gefolgt von der Target-Phase, die eintritt, wenn das Ereignis schließlich beim Zielobjekt button angekommen ist. Wenn das Event-Objekt also bis button durchgereicht wurde, dann wird der Ereignispfad umgedreht und es beginnt die sogenannte Bubbling-Phase, das heißt, das Ereignis propagiert auf dem selben Weg wieder hoch bis zum globalen Objekt und passiert auf dem Weg dieselben Objekte.

Da zu diesen Objekten nun auch das Objekt zählt, welches das body-Element repräsentiert und für das wir in dem Beispiel einen Event-Listener registriert haben, wird dieser entsprechend aufgerufen. Gäbe es also keine Bubbling-Phase, würde die Handlerfunktion in diesem Beispiel bei einem Klick auf den Button überhaupt nicht aufgerufen werden.

Was du nun als unwillkommene Nebenwirkung dieser Reise des Ereignisses durch das DOM empfunden hast, nämlich das dein Eventhandler aufgerufen wird obwohl das Ereignis bei einem Kindelement eingetreten ist, ist eigentlich eine unglaublich praktische Sache!

Denn da man wie gesehen das Zielobjekt, bei dem das Ereignis aufgetreten ist, über die Eigenschaft target des Event-Objektes referenzieren kann, ist es überhaupt nicht nötig, die Verbindung des Eventhandlers mit dem überwachten Objekt explizit durch Registrierung herzustellen. Sondern statt dessen kann man den Eventhandler bei einem gemeinsamen Elternelement registrieren und dann innerhalb der Funktion selektieren. → Event Delegation

So spart man sich viel Schreibarbeit! ;-)

Das heißt, alles was du tun musst ist, Kriterien zu finden, nach denen du die potentiellen Zielelemente innerhalb der Handlerfunktion selektieren kannst …

list.addEventListener('click', function (event) {
  let item = event.target;
  if (item.className === 'menuLevel2') {
    // do something
  }
});

Viele Grüße,

Orlok

--
„Das Wesentliche einer Kerze ist nicht das Wachs, das seine Spuren hinterlässt, sondern das Licht.“ Antoine de Saint-Exupéry