Event nicht weiterreichen stopPropagation
Linuchs
- css
- javascript
Moin,
habe am Smartphone festgestellt, dass ein Menü, welches durch CSS hover aufgeklappt werden soll, nicht angezeigt wird.
Also muss man drauftippen, was dem onclick entspricht. Das funktioniert zunächst, es klappt auf.
Doch wie wieder zuklappen? Ich tippe (oder klicke) auf das per display:block geöffete ul und bekomme den Javascript-Fehler TypeError: obj.childnodes is undefined
Die Funktion toggleDisplay wird zweimal aufgerufen. Einmal wie gewünscht für das UL, dann aber nochmal für dessen untergeordnetes LI, was zum Fehler führt, weil dieses LI kein child hat.
Also möchte ich das onclick Event stoppen. Das gelingt mir nicht.
function toggleDisplay(obj) {
// fuer handheld
alert ( obj.tagName );
if ( obj.tagName == "LI" ) {
obj.childnodes[0].style.display = "block"; // child-UL ausklappen
} else {
obj.style.display = "none"; // diese UL einklappen
}
if ( obj.stopPropagation ) obj.stopPropagation()
else obj.cancelBubble = true; // click nicht weiterreichen
}
<ul class="em08 klapp_left">
<li onclick="toggleDisplay(this)"><img class=ic_kl src="img/###sprache_de.gif###sprache_en.gif###sprache_nl.gif###sprache_nds.png###"
title="###deutsch###english###Nederlands###plattdüütsch###" />
<ul onclick="toggleDisplay(this)">
<li><a href="?lg=de">deutsch</a></li>
<li><a href="?lg=en">english</a></li>
<li><a href="?lg=nl">Nederlands</a></li>
</ul>
</li>
</ul>
Wie stoppe ich das Durchreichen des onclick Events?
Linuchs
Hallo Linuchs
Zunächst einmal sollte HTML und JavaScript nicht vermischt werden, das ist schlechte Praxis, also registriere deine Eventhandler nicht inline sondern im Script, vorzugsweise unter Verwendung der Methode addEventListener.
Darüber hinaus gibt es keine Eigenschaft mit dem Bezeichner childnodes
, es sei denn du hättest sie selbst angelegt, was wohl eher unwahrscheinlich ist. Die von der Schnittstelle Node
bereitgestellte Methode heißt childNodes
; Beachte das große ‚N‘ – JavaScript ist case-sensitive.
Außerdem sollte auch JavaScript und CSS nicht vermischt werden, das ist ebenfalls schlechte Praxis. Setze also nicht das Style-Attribut des Elements sondern setze statt dessen eine Klasse und entferne sie wieder. Siehe hierzu den Artikel zu classList.
Weiterhin ist die Methode stopPropagation
eine Methode der Schnittstelle Event
, nicht der Schnittstelle EventTarget
. Das heißt, sie wird nicht an die Elemente vererbt sondern an das Event-Objekt, welches einer als Handler registrierten Funktion bei ihrem Aufruf automatisch als erstes Argument übergeben wird.
Was du hier gemacht hast liest sich geparst etwa so:
listItem.onclick = function (event) {
toggleDisplay(this);
};
Als Handler registriert hast du also eine anonyme Funktion, die im Kontext des Objektes ausgeführt wird, für welches du sie registriert hast. Sprich, die Variable this
wird beim Aufruf mit dem Objekt initialisiert, welches das entsprechende Element repräsentiert. Das hast du ja schon ganz richtig erkannt.
Durch die Closure geht dir hier aber das Event-Objekt verloren, sodass ein Zugriff auf stopPropagation
innerhalb der Funktion toggleDisplay
nicht mehr möglich ist!
Statt über die Variable this
kann das Objekt, in dessen Kontext der Handler ausgeführt wird, übrigens auch über die Eigenschaft currentTarget
des Event-Objektes referenziert werden, weshalb der Verlust dieses Objektes schwerer wiegt als der Verlust des Kontextes.
Wenn du eine Funktion direkt als Handler registrierst, hast du Zugriff auf beides:
listItem.addEventListener('click', function (event) {
// hier kann listItem sowohl über this
// als auch über event.currentTarget
// referenziert werden
});
Merke: Der Wert der Eigenschaft currentTarget
ebenso wie der Wert der Variable this
ist in einer direkt als Handler registrierten Funktion immer eine Referenz auf das Objekt, für welches der Handler registriert wurde.
Anders verhält es sich mit dem Wert der Eigenschaft target
des Event-Objektes. Dieser enthält eine Referenz auf das Objekt, bei dem das Ereignis tatsächlich eingetreten ist.
menu.addEventListener('click', function (event) {
// referenziere das Element, bei dem das Ereignis eingetreten ist
var element = event.target;
});
Du brauchst nach dem Prinzip der Event Delegation für dein Menü nur einen einzigen Eventhandler für das entsprechende Ereignis registrieren und dann kannst du über event.target
selektieren, was jeweils passieren soll, abhängig davon, bei welchem Zielelement das Ereignis eingetreten ist.
Hierzu würde es sich freilich anbieten, entsprechende Klassen zu definieren, um die Elemente innerhalb der Handlerfunktion entsprechend zu selektieren. Einen Aufruf von stopPropagation
könntest du dir dann komplett sparen.
Viele Grüße,
Orlok
@@Orlok
Außerdem sollte auch JavaScript und CSS nicht vermischt werden, das ist ebenfalls schlechte Praxis.
Ja.
Setze also nicht das Style-Attribut des Elements sondern setze statt dessen eine Klasse und entferne sie wieder.
Nein. Setze bzw. entferne das hidden
-Attribut. Siehe hierzu dieses Posting.
LLAP 🖖
Hallo Orlok,
danke für deine sehr ausführliche Darlegung. Aber ich habe schon den addEventListener nicht verstanden. Die Funktion toggleDisplay wird bei click nicht aufgerufen es wird auch kein Javascript-Fehler gezeigt:
function toggleDisplay(obj) {
alert ( "tagName=[" +obj.tagName +" id=[" +obj.id +"]" );
}
document.getElementById ("lg_einklappen").addEventListener ('click', toggleDisplay, true);
<ul>
<li id="lg_einklappen">X</li>
<li><a href="?lg=de">deutsch</a></li>
...
Die Sektion des Programms habe ich abgespeckt und hier zum Testen ausgekoppelt: http://remso.de/test_javascript_toggleDisplay.htm
Linuchs
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
Hallo Orlok,
mein Thema ist "Event nicht weiterreichen".
Und genau da habe ich nicht kapiert, wo das in deinem Text verborgen ist, bzw. wie ich das erreichen kann. Kann doch soo schwer nicht sein, hatten wir doch schon vor 20 Jahren in der Groß-EDV mit Oracle. War irgendwas mit "throw ..." (werfen).
Es nutzt mir nichts, wenn sämtliche (übergeordneten, untergeordneten) Events feuern.
<!DOCTYPE html>
<head>
<title>test_javascript_addEventListener.htm</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
</head>
<body>
<style type="text/css">
div {
height: 10em;
background-color: #8f8;
}
</style>
<div id="demo_1" onclick="funktion1()">
<p id="demo_2">1. Klick auf diesen Absatz, um den Text auszutauschen.</p>
<p id="demo_3">2. Klick auf dieses Rechteck zum Farbwechsel.</p>
<p id="demo_4">3. Klick unterhalb des Rechtecks, um diesen Text zu ändern.</p>
</div>
<script type="text/javascript">
function funktion1 () {
document.getElementById("demo_1").style.backgroundColor = "#f88";
}
document.addEventListener("click", function(){
document.getElementById("demo_4").innerHTML = "3. unterhalb des Rechtecks wurde geklickt";
});
document.getElementById("demo_2").addEventListener("click", function(){
document.getElementById("demo_2").innerHTML = "1. Text wurde getauscht";
});
</script>
</body>
</html>
Ich klicke also auf den 1. Absatz, um den Text zu tauschen. Das funktioniert soweit. Aber dann geht's weiter wie in der Schlachterei: "Darf es etwas mehr sein?" Das Rechteck wird umgefärbt und im Text von Absatz 3 wird erklärt, es wurde unterhalb des Rechtecks geklickt.
Hilfe, ich will nur eine Scheibe Wurst und nicht den ganzen Laden !!!
Linuchs
Hallo Linuchs
mein Thema ist "Event nicht weiterreichen".
Das habe ich schon vernommen. ;-)
Und genau da habe ich nicht kapiert, wo das in deinem Text verborgen ist, bzw. wie ich das erreichen kann.
Die offenbar leider verborgene Botschaft meines Textes war, dass es überhaupt nicht notwendig ist, das Ereignis daran zu hindern durch das DOM zu propagieren.
Kann doch soo schwer nicht sein, hatten wir doch schon vor 20 Jahren in der Groß-EDV mit Oracle. War irgendwas mit "throw ..." (werfen).
Ja, die throw
-Anweisung gibt es auch in JavaScript, allerdings wird der Effekt nicht unbedingt der sein, den du dir anscheinend davon erhoffst. ;-)
Es nutzt mir nichts, wenn sämtliche (übergeordneten, untergeordneten) Events feuern.
Mit „übergeordnet“ oder „untergeordnet“ hat das nichts zu tun. Das Event feuert.
<!DOCTYPE html> <head>
Das öffnende Tag des html
-Elementes fehlt. → <html lang="de">
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
Du verwendes HTML5, also kannst du auch einfach <meta charset="UTF-8">
schreiben. Auch sollte, selbst wenn es sich nur um eine Testseite handelt, dennoch eine Angabe zum Viewport gemacht werden:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Weiter im Text:
</head> <body> <style type="text/css"> div { height: 10em; background-color: #8f8; } </style>
Das Element style
muss Kindelement von head
sein, in body
hat es nichts verloren. Außerdem ist die Angabe type="text/css"
überflüssig, da dies der Standardwert ist. Die kann also weg.
<div id="demo_1" onclick="funktion1()"> <p id="demo_2">1. Klick auf diesen Absatz, um den Text auszutauschen.</p> <p id="demo_3">2. Klick auf dieses Rechteck zum Farbwechsel.</p> <p id="demo_4">3. Klick unterhalb des Rechtecks, um diesen Text zu ändern.</p> </div>
Erwähnte ich schon, dass Eventhandler nicht im HTML registriert werden sollten, sondern direkt im Script? Davon abgesehen wäre eine ordered list hier wohl semantischer, aber sei’s drum …
<script type="text/javascript">
Auch hier ist die Angabe des type
-Attributes überflüssig weil Standard. Also weg damit.
function funktion1 () { document.getElementById("demo_1").style.backgroundColor = "#f88"; } document.addEventListener("click", function(){ document.getElementById("demo_4").innerHTML = "3. unterhalb des Rechtecks wurde geklickt"; }); document.getElementById("demo_2").addEventListener("click", function(){ document.getElementById("demo_2").innerHTML = "1. Text wurde getauscht"; });
Ich klicke also auf den 1. Absatz, um den Text zu tauschen. Das funktioniert soweit. Aber dann geht's weiter wie in der Schlachterei: "Darf es etwas mehr sein?" Das Rechteck wird umgefärbt und im Text von Absatz 3 wird erklärt, es wurde unterhalb des Rechtecks geklickt.
Es passiert genau das, was du mit deinen Anweisungen angeordnet hast. ;-)
Du hast zunächst mal einen Eventhandler für das Container-Element mit der ID 'demo_1'
und das Ereignis click
registriert. Dieser Handler wird immer aufgerufen, wenn innerhalb dieses div
s geklickt wird. Das heißt, beim ersten Klick wird dieses div
rot gefärbt und so bleibt es. Dabei spielt es wie gesagt auch keine Rolle, ob der Klick direkt auf diesem div
oder auf einem seiner Kindelemente erfolgt ist, da das Ereignis im DOM aufsteigt und den für das div
registrierten Handler entsprechend auslöst.
Als nächstes hast du für das Objekt document
– diesmal glücklicherweise unter Verwendung der Methode addEventListener
– einen weiteren Eventhandler registriert, wieder für das Ereignis click
. Dieser wird aus dem selben Grund wie bei dem für das div
registrierten Handler immer aufgerufen, wenn irgendwo bei einem seiner Abkömmlinge geklickt wird. Da document
das Dokument repräsentiert, wird von diesem Handler schlicht jeder Klick abgefangen. Entsprechend wird also bei jedem Klick der Inhalt des Elementes mit der ID 'demo_4'
ersetzt.
Schließlich hast du noch einen dritten Eventhandler registriert, und zwar für den Absatz mit der Kennzeichnung 'demo_2'
. Dieser Handler wird nur dann aufgerufen, wenn du tatsächlich auf dieses Element klickst. Sonst nicht.
Wenn du nun also auf diesen ersten Absatz mit der ID 'demo_2'
klickst, dann passiert folgendes:
Das Ereignis click
wird gefeuert, sprich es wird ein entsprechendes Event-Objekt erzeugt und in das DOM eingespeist.
Dabei wird es als erstes dem globalen Objekt window
übergeben, dann passiert es document
und html
und body
und so weiter und so fort. → Die Capturing-Phase, in der noch nichts weiter passiert, da du für diese Phase keinen Eventhandler registriert hast.
Dann kommt das Event-Objekt bei seinem Zielobjekt an, nämlich dem Absatz auf den geklickt wurde, hier also das Element mit der ID 'demo_2'
. → Die Target-Phase, in der nun der für dieses Objekt registrierte Eventhandler aufgerufen wird, sodass dessen Anweisung, den Inhalt dieses Absatzes zu ersetzen ausgeführt wird. Entsprechend wird der Inhalt von 'demo_2'
auf „1. Text wurde getauscht“ geändert.
Das wäre bezogen auf dieses Beispiel nun der richtige Zeitpunkt, um die weitere Ausbreitung des Ereignisses im DOM zu unterbinden, wenn man das denn wollte, damit die anderen beiden für das Ereignis click
registrierten Handler nicht aufgerufen werden, wenn auf diesen Absatz geklickt wird:
document.getElementById('demo_2').addEventListener('click', function (event) {
// verhindere die weitere Ausbreitung im DOM
event.stopPropagation( );
// ersetze den textuellen Inhalt des Elements
this.textContent = "1. Text wurde getauscht";
});
Dazu müsstest du wie du siehst deinen Eventhandler etwas umschreiben. Die Weitergabe des Ereignisses wird nämlich durch eine Methode des Event-Objektes unterbunden, welches wie in meinen anderen Postings bereits ausführlich beschrieben, der Handlerfunktion bei ihrem Aufruf als Argument übergeben wird. Zwar könntest du auch über arguments[0]
darauf zugreifen, aber das wäre Quatsch, also notieren wir einen entsprechenden Parameter für die Funktion. → event
Haben wir das gemacht, stehen dir hier sogar zwei Varianten zur Verfügung, wie du das Ereignis an seiner Weiterreise hindern kannst. Nämlich erstens die Methode stopPropagation
, die verhindert, dass andere Objekte innerhalb des DOM von dem Ereignis erreicht werden, und zweitens die Methode stopImmediatePropagation
, deren Aufruf verhindert, dass andere Eventhandler erreicht werden, also auch solche nicht, welche unter Umständen für dasselbe Objekt registriert wurden.
Da wir (du) hier nur verhindern willst, dass Eventhandler aufgerufen werden, die für andere Objekte registriert wurden, genügt es also, auf dem Event-Objekt die Methode stopPropagation
aufzurufen.
Das Ergebnis wäre dann, dass tatsächlich nur der Inhalt von 'demo_2'
ersetzt wird, und sonst passiert nichts mehr weiter. – Dabei sei übrigens noch angemerkt, dass ich hier in meinem modifizierten Eventhandler innerHTML
durch textContent
ersetzt habe. Es ist nämlich nicht notwendig den HTML-Parser anzuwerfen, nur um den textuellen Inhalt eines Elementes zu ersetzen. Aber das nur nebenbei …
Nehmen wir nun an, wir hätten das Ereignis nicht daran gehindert, weiter durch das DOM zu propagieren, also ganzo so, wie es in deinem Code notiert ist, dann würde es folgendermaßen weitergehen:
Nachdem der Eventhandler auf 'demo_2'
aufgerufen und dessen Inhalt ersetzt wurde, ist die Target-Phase beendet. Nun wird der Ereignispfad umgedreht. Das heißt, das Ereignis tritt nun in seine Bubbling-Phase ein, sprich, es passiert nun in umgekehrter Reihenfolge dieselben Objekte, die es schon in der Capturing-Phase passiert hat, als es zum Zielobjekt herabgestiegen ist.
Die erste Station ist nun also das Elternelement von 'demo_2'
, nämlich das Container-Element div
mit der ID 'demo_1'
. Da nun für dieses Element, beziehungsweise das Objekt das es repräsentiert ebenfalls ein Eventhandler für das Ereignis click
registriert wurde, und weil das stop propagation flag
des Ereignisses hier ja nun nicht gesetzt ist, wird dem zur Folge auch dieser Handler aufgerufen und es wird der Hintergrund des div
-Elementes rot eingefärbt.
Nachdem dieser Eventhandler also abgearbeitet ist, setzt das Ereignis seine Reise fort und landet schließlich kurz vor der endgültigen Zielline bei dem Objekt document
. Da nun für dieses Objekt und den Ereignistyp click
ebenfalls ein Eventhandler registriert wurde, wird auch dieser Handler aufgerufen und die darin notierte Anweisung ausgeführt, sodass der Elementinhalt des letzen Absatzes mit der ID 'demo_4'
durch den Textknoten mit dem Wert „3. unterhalb des Rechtecks wurde geklickt“ ersetzt wird.
Du siehst also: Das von dir beschriebene Verhalten entspricht exakt dem Verhalten, dass du durch deine Anweisungen implementiert hast. ;-)
Hilfe, ich will nur eine Scheibe Wurst und nicht den ganzen Laden !!!
Die Scheibe Wurst hast du jetzt, denn du hast nun hoffentlich verstanden, wie sich das Ereignis durch das DOM bewegt und wie du die Ausbreitung unter Verwendung der Methoden stopPropagation
und stopImmediatePropagation
des Event-Objektes an der richtigen Stelle unterbinden kannst.
Aber es geht mir hier nicht darum, dich mit einer Scheibe Wurst abzuspeisen, sondern ich versuche dir das Kochen beizubringen!
Will sagen, ich habe dir das alles hier nicht aufgeschrieben weil mir langweilig ist, sondern weil es mein Ziel ist, dass du verstehst was du hier eigentlich machst. Denn die Förderung des Verständnisses ist meiner Ansicht nach der Hauptzweck dieses Forums und der Grund für mein Engagement.
Der eigentliche Punkt auf den ich in meinen Beiträgen hier im Thread nämlich hinaus will ist, dass der ganze Ansatz mit stopPropagation
hier ganz einfach grundsätzlich unsinnig ist. Denn was wäre denn die Folge?
Die Folge wäre, dass du für jeden Listeneintrag einen Eventhandler registrierst und dann die Anweisung event.stopPropagation()
einfügst, damit deine für Elternelemente registrierten Handler nicht ebenfalls aufgerufen werden. → Und das ist einfach unpraktikabel!
Es sollte hier also nicht darum gehen, die Ausbreitung des Ereignisses zu verhindern, sondern im Gegenteil, sich diese zu Nutze zu machen, weshalb ich dir hier versucht habe das Konzept der Event Delegation näherzubringen.
Um also bei dem von dir hier selbst angebrachten Beispiel zu bleiben: Statt für jeden einzelnen Absatz einen Eventhandler zu registrieren, solltest du nur einen Handler hinzufügen, und zwar bei dem gemeinsamen Elternelement 'demo_1'
. Oder du gehst gegebenenfalls noch ein paar Stufen weiter in der Hierarchie deines DOM und registrierst den Handler zum Beispiel gleich für das body
-Element.
Dieser globale Eventhandler würde dann jedesmal aufgerufen werden, wann immer irgendwo geklickt wird, und du kannst dann innerhalb dieser Funktion die Entsprechenden Anweisungen vornehmen, eben abhändig davon, bei welchem Element das Ereignis aufgetreten ist. Durch bedingte Anweisungen und über event.target
, wo wie beschrieben immer eine Referenz auf das Objekt hinterlegt ist, bei dem das Ereignis tatsächlich aufgetreten ist.
Das Ziel ist also, in der Funktion zu selektieren und nicht bei der Registrierung der Eventhandler …
Viele Grüße,
Orlok