Orlok: Click-Event auswerten (stopPropagation)

Beitrag lesen

Hallo Linuchs

Ich möchte es umgekehrt machen. Die mit dem EventListener click versehenen Objekte sollen selbst prüfen, ob sie mit dem Klick gemeint sind. Es gab Hinweise von Orlok, dass das gehen soll. Habe aber nicht verstanden, wie das umzusetzen ist.

Also, wenn ich dich richtig verstehe, dann hast du eine Reihe von Elementen, für die bei einem Klick eine bestimmte Aktion ausgeführt werden soll. Diese Aktion soll aber nur dann ausgeführt werden, wenn der Klick auf dem jeweiligen Element selbst erfolgt ist, nicht jedoch, wenn auf eines der Kindelemente geklickt wurde.

<body>
  <ul id="list">
    <li>Das ist normaler Text. <em>Das ist hervorgehobener Text.</em></li>
    <li>Das ist normaler Text. <em>Das ist hervorgehobener Text.</em></li>
    <li>Das ist normaler Text. <em>Das ist hervorgehobener Text.</em></li>
  </ul>
</body>

Nehmen wir also einmal dieses abstrakte Beispiel einer ungeordneten Liste und stellen uns vor, wir wollten beim Klick auf eines der Listenelemente eine Aktion ausführen. Diese Aktion soll aber nur dann ausgeführt werden, wenn auf das LI-Element selbst geklickt wurde, aber nicht bei einem Klick auf das EM-Element, welches ein Kindelement des Listenelementes ist.

Um dieses Ziel zu erreichen gibt es nun verschiedene Möglichkeiten. Eine davon könnte so aussehen, dass wir für jedes Listenelement einen Eventlistener registrieren und dann innerhalb der registrierten Handlerfunktion prüfen, ob der Klick auch wirklich auf dem Element erfolgt ist, für das der Handler registriert wurde.

Dies können wir bewerkstelligen, indem wir den Wert der Eigenschaft target des Eventobjektes, welche das Element referenziert auf das tatsächlich geklickt wurde, mit der Eigenschaft currentTarget des Eventobjektes vergleichen, welche das Element referenziert für das der Handler registriert wurde.

window.addEventListener('DOMContentLoaded', function ( ) {
  var items = document.getElementById('list').children,
      length = items.length;
  for (var index = 0; index < length; index ++) {
    items[index].addEventListener('click', function (event) {
      var target = event.target;
      if (event.currentTarget === target) {
        console.log(target.tagName);
      }
    });
  }
});

Wir iterieren hier also über die Kindelemente der Liste und registrieren für jedes der Listenelemente einen Eventhandler. Dieser enthält die bedingte Anweisung, den Namen des Elements in die Konsole zu schreiben, wenn die Eigenschaften target und currentTarget auf dasselbe Objekt verweisen.

Da wir für jedes Listenelement einen Eventhandler registriert haben, verweist currentTarget hier entsprechend immer auf das jeweilige LI-Element, wohingegen target entweder auf das Listenelement oder auf das EM-Element verweisen kann, eben abhängig davon, wohin tatsächlich geklickt wurde.

Im Ergebnis wird hier also der Wert der Eigenschaft tagName in die Konsole geschrieben, wenn auf ein Listenelement geklickt wurde und bei einem Klick auf den hervorgehobenen Text passiert nichts, denn in diesem Fall würde die Eigenschaft target auf das EM-Element verweisen, wohingegen die Eigenschaft currentTarget das LI-Element referenziert. Der durchgeführte Vergleich würde bei einem Klick auf den hervorgehobenen Text also negativ ausfallen. Die Bedingung wäre nicht erfüllt.

Das wäre also im Prinzip genau das, was wir wollen, nämlich dass Klicks auf Kindelemente ignoriert werden und die jeweilige Aktion nur dann ausgeführt wird, wenn auf das Element geklickt wurde, für das der Eventhandler registriert wurde. – Aber besonders elegant wäre das nicht.

window.addEventListener('DOMContentLoaded', function ( ) {
  var list = document.getElementById('list');
  list.addEventListener('click', function (event) {
    var target = event.target;
    if (target.tagName === 'LI') {
      console.log('bingo');
    }
  });
});

Statt für jedes einzelne Listenelement einen Eventhandler zu registrieren können wir nämlich auch einen Handler für das gemeinsame Elternelement registrieren, also für die Liste selbst. In diesem Fall hilft uns die Eigenschaft currentTarget natürlich nicht weiter, da diese hier nun immer auf die Liste verweisen würde, und nicht mehr auf die Listenelemente.

Aber da wir wissen, dass der Wert der Eigenschaft tagName bei einem LI-Element immer der String LI ist und wir zudem wissen, dass von target immer das Element referenziert wird, bei dem das Ereignis tatsächlich eingetreten ist, können wir entsprechend prüfen, ob auf ein LI-Element geklickt wurde, indem wir den Wert der Eigenschaft tagName mit dem String LI vergleichen.

Wir erreichen hier also ebenfalls unser Ziel, dass die Aktion nur dann ausgeführt wird, wenn auf ein Listenelement geklickt wurde, nicht jedoch bei Klicks auf irgendwelche Kindelemente.

Der Schlüssel für eine effiziente Ereignisbehandlung mittels Event Delegation liegt also in dem Wissen um die Eigenschaft target des Eventobjektes, nämlich dass diese grundsätzlich auf das Element verweist, bei dem das Ereignis tatsächlich eingetreten ist, und in dem Wissen, dass die meisten Ereignisse innerhalb des DOM vom Zielelement aus aufsteigen, sodass Eventhandler bei Elternelementen des über die Eigenschaft target referenzierten Elementes ebenfalls ausgelöst werden.

Es kann also ein einzelner Eventhandler bei einem Element registriert werden, der immer auch dann aufgerufen wird, wenn das entsprechende Ereignis innerhalb des Teilbaums der Kindelemente aufgetreten ist, und es kann anhand der Eigenschaften der Elemente dann in der Handlerfunktion selektiert werden.

Dabei kann man wie in dem Beispiel hier prüfen, ob das Zielelement, also der Wert von target einem bestimmten Elementtyp angehört, indem man den Wert der Eigenschaft tagName oder den Wert der Eigenschaft nodeName mit dem jeweils passenden String vergleicht.

Man könnte auch prüfen, ob die id oder der Wert von className des Zielelementes dem Wert des Elementes entspricht, für das eine Aktion durchgeführt werden soll.

Auch könnte man die Ausführung von Anweisungen davon abhängig machen, ob ein Element ein direktes Kindelement eines anderen Elementes ist, indem man die Eigenschaft parentElement des Zielelementes untersucht. Oder man verwendet eines oder mehrere der tausend anderen Merkmale, die es ermöglichen dasjenige Element, beziehungsweise diejenigen Elemente zu identifizieren, für welche die Aktion ausgeführt werden soll.

  • Also statt eine Unzahl an Eventhandlern zu registrieren und irgendwie umständlich mit Methoden wie stopPropagation zu hantieren, solltest du dir das Leben leicht machen und nur einen einzigen Handler auf einem gemeinsamen Elternelement registrieren.

  • Dieser Handler fängt dann alle Klicks ab, die auf dem Element selbst oder auf irgendeinem Element erfolgt sind, das in direkter Linie von dem Element abstammt, auf dem der Handler registriert wurde.

  • Innerhalb der Handlerfunktion referenzierst du dann über event.target das Element, bei dem das Ereignis tatsächlich eingetreten ist. Also das Element auf das tatsächlich geklickt wurde.

  • Dann prüfst du, ob dieses Element, bei dem das Ereignis eingetreten ist, bestimmte Kriterien erfüllt, also ob es einem bestimmten Elementtyp oder einer Klasse angehört, ob es selbst Kindelemente hat oder es das Kind eines bestimmten Elternelementes ist, usw. usw.

  • Für den Fall, dass der Klick auf einem Element erfolgt ist, das den von dir aufgestellten Kriterien entspricht, führst du dann deine Aktion aus. Sonst nicht.

Gruß,

Orlok