JavaScript event handling: stopPropagation vs stopImmediatePropagation
Table of contents
In JavaScript, when any event like a click or mouse-over occurs on a dom element, it goes through the capturing and bubbling phases.
Consider the following code:
<!-- eventHandlingExample.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Difference Between event.stopPropagation() and event.stopImmediatePropagation()</title>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
div {
display: flex;
align-items: center;
justify-content: center;
}
#grandparent {
width: 400px;
height: 400px;
background-color: rgb(147, 147, 252);
}
#parent {
width: 250px;
height: 250px;
background-color: rgb(255, 129, 129);
}
#child {
width: 100px;
height: 100px;
background-color: rgb(137, 255, 137);
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<script>
const gp = document.getElementById("grandparent");
const p = document.getElementById("parent");
const c = document.getElementById("child");
// Capturing
gp.addEventListener("click", (e) => { console.log("Capture: Grandparent clicked!") }, { capture: true } );
p.addEventListener( "click", (e) => { console.log("Capture: Parent clicked!") }, { capture: true } );
c.addEventListener( "click", (e) => { console.log("Capture: Child clicked!") }, { capture: true } );
// Bubbling
gp.addEventListener("click", (e) => { console.log("Bubble: Grandparent clicked!") });
p.addEventListener( "click", (e) => { console.log("Bubble: Parent clicked!") });
// p.addEventListener( "click", (e) => { e.stopPropagation(); console.log("Bubble: Parent clicked!"); });
// p.addEventListener( "click", (e) => { e.stopImmediatePropagation(); console.log("Bubble: Parent clicked!"); });
c.addEventListener( "click", (e) => { console.log("Bubble: Child clicked!") });
// Other Events on Parent
p.addEventListener("click", (e) => { console.log("Alpha Bubble: Parent clicked!") });
p.addEventListener("click", (e) => { console.log("Beta Bubble: Parent clicked!") });
</script>
</body>
</html>
and its output when the <div id="child"></div>
is clicked.
Capture Phase
During the capturing phase, the event is triggered from the outermost dom element, going inwards till it reaches the element it occurred on. In the example, if we have click listeners attached to all three dom elements, the grandparent, parent and child divs. When a click is registered on the child div, the capturing phase starts triggering the listeners of the grandparent and then the parent and then the child. So, we see the messages in the console in that order. To listen to events in the capture phase, send the third argument to addEventListener
as { capture: true }.
Bubble Phase
During the bubbling phase, the event is triggered from the actual dom element the event occurred on, going outwards. So in the example, if we have click listeners attached to all three dom elements, the grandparent, parent and child divs. When a click is registered on the child div, the capturing phase starts triggering the listeners of the grandparent and then the parent and then the child. So, we see the messages in the console in that order.
[Note: WebDevSimplified has a great video on this if you want to learn more.]
stopPropagation vs stopImmediatePropagation
Using stopPropagation
, as the name suggests, stops events from being propagated (captured or bubbled) to any parent or child element of the current element on which the event had occurred. However stopPropagation
does not prevent any other event listeners for the same event from being triggered if they are attached to the same element.
In contrast, if we replace stopPropagation
with stopImmediatePropagation
, any further listener for the same event on any parent, child or the element itself will not be triggered.
Let's change the parent's event listener to:
p.addEventListener( "click", (e) => { e.stopPropagation(); console.log("Bubble: Parent clicked!"); });
Output when child
is clicked:
If we just replace the e.stopPropagation()
with e.stopImmediatePropagation()
we get the following output.
So, we see that, for both stopPropagation
and stopImmediatePropagation
, none of the 'click' listeners for grandparent
div and child
div is triggered.
However, in the case of stopPropagation
, the other 'click' listeners on parent
div are triggered but when we use stopImmediatePropagation
even the other 'click' listeners on parent
are not triggered.
P.S., Reading an article or watching a video will only do so much. I suggest that you play around with the linked codepen and check the behaviors for yourself.
Some things you could try are:
Click on the different elements and check the console for which listeners were triggered.
Change which element has the stopPropagation/stopImmediatePropagation calls
Use your imagination
Happy Coding !!!