Event bubbling in JavaScript - javascript

There is something I can't seem to understand about the definition of bubbling.
I know that event bubbling means event propagation from inner element to outer element. I also know that you can "choose" you way of propagation with the last optional parameter of addEventListener().
The thing I do not understand is this one:
<body>
<div id = "divi">
<p id = "para">Paragraph</p>
</div>
<script>
var elementd = document.getElementById('divi');
elementd.addEventListener("click", MessageD);
function MessageD(){
alert('A message');
}
</script>
</body>
Here I attach a click event to my div element. By default, the click event is bubbling, which means he is going up through the DOM. But when I click on my paragraph, the click event is executed.
To my understanding, that means that the click event propagates to the "p" child element first, or DOWN the DOM. Can someone explain to me why and what happens? Thanks!
--- EDIT AFTER SOME ANSWERS ---
Maybe what troubles me is better explained through this example:
<body>
<style>
#myDiv, #results {
margin: 50px;
}
#myDiv {
padding: 10px;
width: 200px;
text-align: center;
background-color: #000;
}
#myDiv div {
margin: 10px;
background-color: #555;
}
</style>
<div id="myDiv">
<div>Text 1</div>
<div>Text 2</div>
<div>Text 3</div>
<div>Text 4</div>
</div>
<div id="results"></div>
<script>
var myDiv = document.getElementById('myDiv'),
results = document.getElementById('results');
myDiv.addEventListener('mouseover', function() {
results.innerHTML += "I'm in.<br />";
}, false);
myDiv.addEventListener('mouseout', function() {
results.innerHTML += "I'm out.<br />";
}, false);
</script>
</body>
If you try running this code, you'll be "out" and "in" any time you enter and exit a child div... meaning you'll be "in" in child divs.
Doesn't that means that the events propagate from the parent to the child, or down way?
--- FINAL EDIT ---
I finally understood: I made a confusion between an event and an event handler.
An event is just a click, so anything in a page can receive it. Once you have clicked, this click event is going to go to the top of the DOM, and when it encounters click event handler, the function associated to the event handler will be executed.
Thanks everyone!

To my understanding, that means that the click event propagates to the "p" child element first, or DOWN the DOM.
No. In that case the p element is the target of event. Event propagates to the div element. div element has a click handler and it's executed.
This is how events work. Target of the event is an element that has triggered the event, the target can be the element that you have attached the listener to or one of it's children/descendants. target property of the event object refers to the target element of the event and when addEventListener is used for attaching an event listener, this value of the handler refers to the owner of the handler, in this case the div element.
The fact that you have attached an event listener has nothing to do with how event works. Click events are fired whenever the user clicks. It's you, the developer, that attaches an event listener for listening to specific event triggered for an element. If you don't attach a listener, still the event is triggered.

When you click on the paragraph, it's not being handled there, so it propagates up to the DIV. If you use this version of the code, it shows which element the event is being handled by. The alert says divi, not para.
var elementd = document.getElementById('divi');
elementd.addEventListener("click", MessageD);
function MessageD() {
alert(this.id);
}
<div id="divi">
<p id="para">Paragraph</p>
</div>

Related

querySelector bug <a> [duplicate]

Can anyone please tell me the exact difference between currentTarget and target property in JavaScript events with example and which property is used in which scenario?
Events bubble by default. So the difference between the two is:
target is the element that triggered the event (e.g., the user clicked on)
currentTarget is the element that the event listener is attached to.
target = element that triggered event.
currentTarget = element that has the event listener.
Minimal runnable example
window.onload = function() {
var resultElem = document.getElementById('result')
document.getElementById('1').addEventListener(
'click',
function(event) {
resultElem.innerHTML += ('<div>target: ' + event.target.id + '</div>')
resultElem.innerHTML += ('<div>currentTarget: ' + event.currentTarget.id + '</div>')
},
false
)
document.getElementById('2').dispatchEvent(
new Event('click', { bubbles:true }))
}
<div id="1">1 click me
<div id="2">2 click me as well</div>
</div>
<div id="result">
<div>result:</div>
</div>
If you click on:
2 click me as well
then 1 listens to it, and appends to the result:
target: 2
currentTarget: 1
because in that case:
2 is the element that originated the event
1 is the element that listened to the event
If you click on:
1 click me
instead, the result is:
target: 1
currentTarget: 1
Tested on Chromium 71.
For events whose bubbles property is true, they bubble.
Most events do bubble, except several, namely focus, blur, mouseenter, mouseleave, ...
If an event evt bubbles, the evt.currentTarget is changed to the current target in its bubbling path, while the evt.target keeps the same value as the original target which triggered the event.
It is worth noting that if your event handler (of an event that bubbles) is asynchronous and the handler uses evt.currentTarget. currentTarget should be cached locally because the event object is reused in the bubbling chain (codepen).
const clickHandler = evt => {
const {currentTarget} = evt // cache property locally
setTimeout(() => {
console.log('evt.currentTarget changed', evt.currentTarget !== currentTarget)
}, 3000)
}
If you use React, from v17, react drops the Event Pooling.
Therefore, the event object is refreshed in the handler and can be safe to use in asynchronous calls (codepen).
↑is not always true. onClick event's currentTarget is undefined after the event handler finishes. In conclusion, always cache the event's properties locally if you are going to use them after a synchronous call.
From react docs
Note:
As of v17, e.persist() doesn’t do anything because the SyntheticEvent
is no longer pooled.
And many other things that are too long to be pasted in an answer, so I summarized and made a blog post here.
If this isn't sticking, try this:
current in currentTarget refers to the present. It's the most recent target that caught the event that bubbled up from elsewhere.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
If click on the P tag in above code then you will get three alert,and if you click on the div tag you will get two alert and a single alert on clicking the form tag.
And now see the following code,
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<script>
function fun(event){
alert(event.target+" "+event.currentTarget);
}
</script>
<form>FORM
<div onclick="fun(event)">DIV
<p>P</p>
</div>
</form>
We just removed onclick from the P and form tag and now when we click we on P tag we get only one alert:
[object HTMLParagraphElement] [object HTMLDivElement]
Here event.target is [object HTMLParagraphElement],and event.curentTarget is [object HTMLDivElement]:
So
event.target is the node from which the event originated,
and
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached.To know more see bubbling
Here we clicked on P tag but we don't have listener on P but on its parent element div.
Event.currentTarget is the element to which the event handler has been
attached, as opposed to Event.target, which identifies the element on
which the event occurred and which may be its descendant.
Source: MDN
target always refers to the element in front of addEventListener - it's the element on which the event originated.
currentTarget tells you - if this is an event that's bubbling - the element that currently has the event listener attached (which will fire the event handler if the event occurs).
See this CodePen for an example. If you open up developer tools and click the square, you'll see that first the div is the target and the currentTarget, but the event bubbles up to the main element - then main element becomes the currentTarget, while the div is still the target. Note the event listener needs to be attached to both elements for the bubbling to occur.
event.target is the node from which the event originated, ie. wherever you place your event listener (on paragraph or span), event.target refers to node (where user clicked).
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached. Ie. if we attached our event listener on paragraph node, then event.currentTarget refers to paragraph while event.target still refers to span.
Note: that if we also have an event listener on body, then for this event-listener, event.currentTarget refers to body (ie. event provided as input to event-listerners is updated each time event is bubbling one node up).
Here's a simple scenario to explain why it's needed. Let's say there are some messages that you show to the user with the format below, but you also want to give them the freedom to close them (unless you have a special mental disorder), so here are some message panes:
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
and when the user clicks on the [x] button on each, the whole corresponding pane must be removed.
Here's the HTML code for the pane:
<div class="pane">
A message will be here
<span class="remove-button">[x]</span>
</div>
Now where do you want to add the click event listener? The user clicks on [x], but you want to remove the pane, so:
If you add the click event listener to the [x], then you will have to find its parent on DOM and remove it... which is possible but ugly and "DOM dependent".
And if you add the click event listener to the pane, clicking "everywhere on the pane" will remove it, and not just clicking on its [x] button.
So what can we do? We can use the "Bubbles Up" feature of the event system:
"Events are raised and bubble up the DOM tree regardless of the existence of any event handlers."
In our example, this means that even if we add the event handlers to the panes, we will be able to catch the events raised specifically by the [x] button clicks (because events bubble up). So there can be difference between where an event is raised, and where we catch and handle it.
Where it's raised will be in the event.target, and where it's caught will be in the event.currentTarget (where we're currently handling it). So:
let panes = document.getElementsByClassName("pane");
for(let pane of panes){
pane.addEventListener('click', hndlr);
}
function hndlr(e){
if(e.target.classList.contains('remove-button')){
e.currentTarget.remove();
}
}
(The credit of this example goes to the website JavaScript.info)
An experiment:
document.addEventListener("click", (e) => {
console.log(e.target, e.currentTarget);
});
document.querySelector("p").click();
output:
<p></p>
#document
The target (<p></p>) seems to be the element clicked, while the currentTarget (#document) is the element that is listening for the click event.

Accessing data attribute of clicked element with javascript [duplicate]

Can anyone please tell me the exact difference between currentTarget and target property in JavaScript events with example and which property is used in which scenario?
Events bubble by default. So the difference between the two is:
target is the element that triggered the event (e.g., the user clicked on)
currentTarget is the element that the event listener is attached to.
target = element that triggered event.
currentTarget = element that has the event listener.
Minimal runnable example
window.onload = function() {
var resultElem = document.getElementById('result')
document.getElementById('1').addEventListener(
'click',
function(event) {
resultElem.innerHTML += ('<div>target: ' + event.target.id + '</div>')
resultElem.innerHTML += ('<div>currentTarget: ' + event.currentTarget.id + '</div>')
},
false
)
document.getElementById('2').dispatchEvent(
new Event('click', { bubbles:true }))
}
<div id="1">1 click me
<div id="2">2 click me as well</div>
</div>
<div id="result">
<div>result:</div>
</div>
If you click on:
2 click me as well
then 1 listens to it, and appends to the result:
target: 2
currentTarget: 1
because in that case:
2 is the element that originated the event
1 is the element that listened to the event
If you click on:
1 click me
instead, the result is:
target: 1
currentTarget: 1
Tested on Chromium 71.
For events whose bubbles property is true, they bubble.
Most events do bubble, except several, namely focus, blur, mouseenter, mouseleave, ...
If an event evt bubbles, the evt.currentTarget is changed to the current target in its bubbling path, while the evt.target keeps the same value as the original target which triggered the event.
It is worth noting that if your event handler (of an event that bubbles) is asynchronous and the handler uses evt.currentTarget. currentTarget should be cached locally because the event object is reused in the bubbling chain (codepen).
const clickHandler = evt => {
const {currentTarget} = evt // cache property locally
setTimeout(() => {
console.log('evt.currentTarget changed', evt.currentTarget !== currentTarget)
}, 3000)
}
If you use React, from v17, react drops the Event Pooling.
Therefore, the event object is refreshed in the handler and can be safe to use in asynchronous calls (codepen).
↑is not always true. onClick event's currentTarget is undefined after the event handler finishes. In conclusion, always cache the event's properties locally if you are going to use them after a synchronous call.
From react docs
Note:
As of v17, e.persist() doesn’t do anything because the SyntheticEvent
is no longer pooled.
And many other things that are too long to be pasted in an answer, so I summarized and made a blog post here.
If this isn't sticking, try this:
current in currentTarget refers to the present. It's the most recent target that caught the event that bubbled up from elsewhere.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
If click on the P tag in above code then you will get three alert,and if you click on the div tag you will get two alert and a single alert on clicking the form tag.
And now see the following code,
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<script>
function fun(event){
alert(event.target+" "+event.currentTarget);
}
</script>
<form>FORM
<div onclick="fun(event)">DIV
<p>P</p>
</div>
</form>
We just removed onclick from the P and form tag and now when we click we on P tag we get only one alert:
[object HTMLParagraphElement] [object HTMLDivElement]
Here event.target is [object HTMLParagraphElement],and event.curentTarget is [object HTMLDivElement]:
So
event.target is the node from which the event originated,
and
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached.To know more see bubbling
Here we clicked on P tag but we don't have listener on P but on its parent element div.
Event.currentTarget is the element to which the event handler has been
attached, as opposed to Event.target, which identifies the element on
which the event occurred and which may be its descendant.
Source: MDN
target always refers to the element in front of addEventListener - it's the element on which the event originated.
currentTarget tells you - if this is an event that's bubbling - the element that currently has the event listener attached (which will fire the event handler if the event occurs).
See this CodePen for an example. If you open up developer tools and click the square, you'll see that first the div is the target and the currentTarget, but the event bubbles up to the main element - then main element becomes the currentTarget, while the div is still the target. Note the event listener needs to be attached to both elements for the bubbling to occur.
event.target is the node from which the event originated, ie. wherever you place your event listener (on paragraph or span), event.target refers to node (where user clicked).
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached. Ie. if we attached our event listener on paragraph node, then event.currentTarget refers to paragraph while event.target still refers to span.
Note: that if we also have an event listener on body, then for this event-listener, event.currentTarget refers to body (ie. event provided as input to event-listerners is updated each time event is bubbling one node up).
Here's a simple scenario to explain why it's needed. Let's say there are some messages that you show to the user with the format below, but you also want to give them the freedom to close them (unless you have a special mental disorder), so here are some message panes:
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
and when the user clicks on the [x] button on each, the whole corresponding pane must be removed.
Here's the HTML code for the pane:
<div class="pane">
A message will be here
<span class="remove-button">[x]</span>
</div>
Now where do you want to add the click event listener? The user clicks on [x], but you want to remove the pane, so:
If you add the click event listener to the [x], then you will have to find its parent on DOM and remove it... which is possible but ugly and "DOM dependent".
And if you add the click event listener to the pane, clicking "everywhere on the pane" will remove it, and not just clicking on its [x] button.
So what can we do? We can use the "Bubbles Up" feature of the event system:
"Events are raised and bubble up the DOM tree regardless of the existence of any event handlers."
In our example, this means that even if we add the event handlers to the panes, we will be able to catch the events raised specifically by the [x] button clicks (because events bubble up). So there can be difference between where an event is raised, and where we catch and handle it.
Where it's raised will be in the event.target, and where it's caught will be in the event.currentTarget (where we're currently handling it). So:
let panes = document.getElementsByClassName("pane");
for(let pane of panes){
pane.addEventListener('click', hndlr);
}
function hndlr(e){
if(e.target.classList.contains('remove-button')){
e.currentTarget.remove();
}
}
(The credit of this example goes to the website JavaScript.info)
An experiment:
document.addEventListener("click", (e) => {
console.log(e.target, e.currentTarget);
});
document.querySelector("p").click();
output:
<p></p>
#document
The target (<p></p>) seems to be the element clicked, while the currentTarget (#document) is the element that is listening for the click event.

When using event.target.getAttribute() on <button> element, it returns null (seemingly) at random? [duplicate]

Can anyone please tell me the exact difference between currentTarget and target property in JavaScript events with example and which property is used in which scenario?
Events bubble by default. So the difference between the two is:
target is the element that triggered the event (e.g., the user clicked on)
currentTarget is the element that the event listener is attached to.
target = element that triggered event.
currentTarget = element that has the event listener.
Minimal runnable example
window.onload = function() {
var resultElem = document.getElementById('result')
document.getElementById('1').addEventListener(
'click',
function(event) {
resultElem.innerHTML += ('<div>target: ' + event.target.id + '</div>')
resultElem.innerHTML += ('<div>currentTarget: ' + event.currentTarget.id + '</div>')
},
false
)
document.getElementById('2').dispatchEvent(
new Event('click', { bubbles:true }))
}
<div id="1">1 click me
<div id="2">2 click me as well</div>
</div>
<div id="result">
<div>result:</div>
</div>
If you click on:
2 click me as well
then 1 listens to it, and appends to the result:
target: 2
currentTarget: 1
because in that case:
2 is the element that originated the event
1 is the element that listened to the event
If you click on:
1 click me
instead, the result is:
target: 1
currentTarget: 1
Tested on Chromium 71.
For events whose bubbles property is true, they bubble.
Most events do bubble, except several, namely focus, blur, mouseenter, mouseleave, ...
If an event evt bubbles, the evt.currentTarget is changed to the current target in its bubbling path, while the evt.target keeps the same value as the original target which triggered the event.
It is worth noting that if your event handler (of an event that bubbles) is asynchronous and the handler uses evt.currentTarget. currentTarget should be cached locally because the event object is reused in the bubbling chain (codepen).
const clickHandler = evt => {
const {currentTarget} = evt // cache property locally
setTimeout(() => {
console.log('evt.currentTarget changed', evt.currentTarget !== currentTarget)
}, 3000)
}
If you use React, from v17, react drops the Event Pooling.
Therefore, the event object is refreshed in the handler and can be safe to use in asynchronous calls (codepen).
↑is not always true. onClick event's currentTarget is undefined after the event handler finishes. In conclusion, always cache the event's properties locally if you are going to use them after a synchronous call.
From react docs
Note:
As of v17, e.persist() doesn’t do anything because the SyntheticEvent
is no longer pooled.
And many other things that are too long to be pasted in an answer, so I summarized and made a blog post here.
If this isn't sticking, try this:
current in currentTarget refers to the present. It's the most recent target that caught the event that bubbled up from elsewhere.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
If click on the P tag in above code then you will get three alert,and if you click on the div tag you will get two alert and a single alert on clicking the form tag.
And now see the following code,
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<script>
function fun(event){
alert(event.target+" "+event.currentTarget);
}
</script>
<form>FORM
<div onclick="fun(event)">DIV
<p>P</p>
</div>
</form>
We just removed onclick from the P and form tag and now when we click we on P tag we get only one alert:
[object HTMLParagraphElement] [object HTMLDivElement]
Here event.target is [object HTMLParagraphElement],and event.curentTarget is [object HTMLDivElement]:
So
event.target is the node from which the event originated,
and
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached.To know more see bubbling
Here we clicked on P tag but we don't have listener on P but on its parent element div.
Event.currentTarget is the element to which the event handler has been
attached, as opposed to Event.target, which identifies the element on
which the event occurred and which may be its descendant.
Source: MDN
target always refers to the element in front of addEventListener - it's the element on which the event originated.
currentTarget tells you - if this is an event that's bubbling - the element that currently has the event listener attached (which will fire the event handler if the event occurs).
See this CodePen for an example. If you open up developer tools and click the square, you'll see that first the div is the target and the currentTarget, but the event bubbles up to the main element - then main element becomes the currentTarget, while the div is still the target. Note the event listener needs to be attached to both elements for the bubbling to occur.
event.target is the node from which the event originated, ie. wherever you place your event listener (on paragraph or span), event.target refers to node (where user clicked).
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached. Ie. if we attached our event listener on paragraph node, then event.currentTarget refers to paragraph while event.target still refers to span.
Note: that if we also have an event listener on body, then for this event-listener, event.currentTarget refers to body (ie. event provided as input to event-listerners is updated each time event is bubbling one node up).
Here's a simple scenario to explain why it's needed. Let's say there are some messages that you show to the user with the format below, but you also want to give them the freedom to close them (unless you have a special mental disorder), so here are some message panes:
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
and when the user clicks on the [x] button on each, the whole corresponding pane must be removed.
Here's the HTML code for the pane:
<div class="pane">
A message will be here
<span class="remove-button">[x]</span>
</div>
Now where do you want to add the click event listener? The user clicks on [x], but you want to remove the pane, so:
If you add the click event listener to the [x], then you will have to find its parent on DOM and remove it... which is possible but ugly and "DOM dependent".
And if you add the click event listener to the pane, clicking "everywhere on the pane" will remove it, and not just clicking on its [x] button.
So what can we do? We can use the "Bubbles Up" feature of the event system:
"Events are raised and bubble up the DOM tree regardless of the existence of any event handlers."
In our example, this means that even if we add the event handlers to the panes, we will be able to catch the events raised specifically by the [x] button clicks (because events bubble up). So there can be difference between where an event is raised, and where we catch and handle it.
Where it's raised will be in the event.target, and where it's caught will be in the event.currentTarget (where we're currently handling it). So:
let panes = document.getElementsByClassName("pane");
for(let pane of panes){
pane.addEventListener('click', hndlr);
}
function hndlr(e){
if(e.target.classList.contains('remove-button')){
e.currentTarget.remove();
}
}
(The credit of this example goes to the website JavaScript.info)
An experiment:
document.addEventListener("click", (e) => {
console.log(e.target, e.currentTarget);
});
document.querySelector("p").click();
output:
<p></p>
#document
The target (<p></p>) seems to be the element clicked, while the currentTarget (#document) is the element that is listening for the click event.

jQuery .on() delegation with selector that matches nested elements

// Example A
$("#delegate").on("click", function(event) {
// executes on click on any descendant of #delegate or self (not a delegate at all)
// 'this' is #delegate
});
// Example B
$("#delegate").on("click", "#outer", function(event) {
// executes on click on any descendant of #outer or self
// 'this' is #outer or #inner (depends on the actual click)
});
$("#delegate").on("click", "#inner", function(event) {
// executes on click on any descendant of #inner or self (nothing happens when clicking #outer)
// 'this' is #inner
});
// Example C (now it's getting weird)
$("#delegate").on("click", "div", function(event) {
// executes twice, when clicking #inner, because the event passes #outer when bubbling up
// one time 'this' is #inner, and the other time 'this' is #outer
// stopPropagation() // actually prevents the second execution
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="delegate">
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
</div>
How do you explain this behaviour logically?
There is exactly one click event, which starts on #inner, bubbles through #outer and finally reaches #delegate.
The event is catched (exactly) once by the #delegate's on-handler. The handler checks if event's history contains any div elements.
If this applies, the callback function should be called once. That's what I would expect. "Single Event, single Handler, single Condition, single Callback".
It gets more crazy if you take a look at the stopPropagation() behaviour. You can actually avoid the second execution, though the event has already reached #delegate. stopPropagation(should not work here.
What kind of "magic" is done in the implementation of the on-delegation logic? Do event-bubbling and program flow split up in any way?
Please don't post "practical advice" in the first place ("Use xyz instead!"). I'd like to understand why the code works the ways it does.
As you have bound events on all the divs in two ways:
Using id attribute of the element div.
Using the tag name of the element div.
so if you event.stopPropagation(); on the last one still the alert will come two times because you have cliked the div and also the #inner (for instance.)
Check the snippet below.
$("#delegate").on("click", function(event) {
alert(this.id);
});
/*
$("#delegate").on('click', "#outer", function(event) {
alert(this.id);
});
$("#delegate").on('click', "#inner", function(event) {
alert(this.id);
});
*/
// now it's getting weird
$("#delegate").on("click", "div", function(event) {
event.stopPropagation();
alert(this.id);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="delegate">
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
</div>
The way event delegation works is that jQuery walks up the DOM tree from the innermost target to the element that the delegation was bound to, testing each element to see if it matches the selector. If it does, it executes the handler with this bound to that element.
When event.stopPropagation is called, it sets a flag in the event object. The loop that walks the DOM tree also calls event.isPropagationStopped(). If propagation is stopped, it breaks out of the loop.
In other words, jQuery is doing its own bubbling and propagation stopping when it implements delegation, it's not making use of the browser's bubbling (except that this bubbling is necessary for the initial event to be triggered on #delegate, so that the jQuery loop will run).
Everything works as expected. Have a look at this fiddle:
$("#delegate").on("click", "div", function(event) {
// event.stopPropagation();
alert('div: ' + $(this).attr('id'));
});
Clicking #inner will fire the above event. The event will bubble up to #outer since #inner is a descendent of #outer. Since #outer also is a <div, the event will be fired on #outer, also. Logically, clicking on #inner will first alert "div: inner" and then "div: outer".
Calling event.stopPropagation() tells the event not to bubble up, so #outer stays uninvoked.
Other than in this fiddle:
<div id="delegate">
<div id="first">
first
</div>
<div id="second">
second
<div id="third">
third, inside second
</div>
</div>
</div>
Clicking on third will first alert third, then second and then stop, because #first is not a parent but a sibling.

stopPropagation vs. stopImmediatePropagation

What's the difference between event.stopPropagation() and event.stopImmediatePropagation()?
stopPropagation will prevent any parent handlers from being executed stopImmediatePropagation will prevent any parent handlers and also any other handlers from executing
Quick example from the jquery documentation:
$("p").click(function(event) {
event.stopImmediatePropagation();
});
$("p").click(function(event) {
// This function won't be executed
$(this).css("background-color", "#f00");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Note that the order of the event binding is important here!
$("p").click(function(event) {
// This function will now trigger
$(this).css("background-color", "#f00");
});
$("p").click(function(event) {
event.stopImmediatePropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Surprisingly, all other answers only say half the truth or are actually wrong!
e.stopImmediatePropagation() stops any further handler from being called for this event, no exceptions
e.stopPropagation() is similar, but does still call all handlers for this phase on this element if not called already
What phase?
E.g. a click event will always first go all the way down the DOM (called “capture phase”), finally reach the origin of the event (“target phase”) and then bubble up again (“bubble phase”). And with addEventListener() you can register multiple handlers for both capture and bubble phase independently. (Target phase calls handlers of both types on the target without distinguishing.)
And this is what the other answers are incorrect about:
quote: “event.stopPropagation() allows other handlers on the same element to be executed”
correction: if stopped in the capture phase, bubble phase handlers will never be reached, also skipping them on the same element
quote: “event.stopPropagation() [...] is used to stop executions of its corresponding parent handler only”
correction: if propagation is stopped in the capture phase, handlers on any children, including the target aren’t called either, not only parents
...and: if propagation is stopped in the bubble phase, all capture phase handlers have already been called, including those on parents
A fiddle and mozilla.org event phase explanation with demo.
A small example to demonstrate how both these propagation stoppages work.
var state = {
stopPropagation: false,
stopImmediatePropagation: false
};
function handlePropagation(event) {
if (state.stopPropagation) {
event.stopPropagation();
}
if (state.stopImmediatePropagation) {
event.stopImmediatePropagation();
}
}
$("#child").click(function(e) {
handlePropagation(e);
console.log("First event handler on #child");
});
$("#child").click(function(e) {
handlePropagation(e);
console.log("Second event handler on #child");
});
// First this event will fire on the child element, then propogate up and
// fire for the parent element.
$("div").click(function(e) {
handlePropagation(e);
console.log("Event handler on div: #" + this.id);
});
// Enable/disable propogation
$("button").click(function() {
var objectId = this.id;
$(this).toggleClass('active');
state[objectId] = $(this).hasClass('active');
console.log('---------------------');
});
div {
padding: 1em;
}
#parent {
background-color: #CCC;
}
#child {
background-color: #000;
padding: 5em;
}
button {
padding: 1em;
font-size: 1em;
}
.active {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
<div id="child"> </div>
</div>
<button id="stopPropagation">Stop Propogation</button>
<button id="stopImmediatePropagation" ">Stop Immediate Propogation</button>
There are three event handlers bound. If we don’t stop any propagation, then there should be four alerts - three on the child div, and one on the parent div.
If we stop the event from propagating, then there will be 3 alerts (all on the inner child div). Since the event won’t propagate up the DOM hierarchy, the parent div won’t see it, and its handler won’t fire.
If we stop propagation immediately, then there will only be 1 alert. Even though there are three event handlers attached to the inner child div, only 1 is executed and any further propagation is killed immediately, even within the same element.
I am a late comer, but maybe I can say this with a specific example:
Say, if you have a <table>, with <tr>, and then <td>. Now, let's say you set 3 event handlers for the <td> element, then if you do event.stopPropagation() in the first event handler you set for <td>, then all event handlers for <td> will still run, but the event just won't propagate to <tr> or <table> (and won't go up and up to <body>, <html>, document, and window).
Now, however, if you use event.stopImmediatePropagation() in your first event handler, then, the other two event handlers for <td> WILL NOT run, and won't propagate up to <tr>, <table> (and won't go up and up to <body>, <html>, document, and window).
Note that it is not just for <td>. For other elements, it will follow the same principle.
event.stopPropagation will prevent handlers on parent elements from running.
Calling event.stopImmediatePropagation will also prevent other handlers on the same element from running.
From the jQuery API:
In addition to keeping any additional
handlers on an element from being
executed, this method also stops the
bubbling by implicitly calling
event.stopPropagation(). To simply
prevent the event from bubbling to
ancestor elements but allow other
event handlers to execute on the same
element, we can use
event.stopPropagation() instead.
Use
event.isImmediatePropagationStopped()
to know whether this method was ever
called (on that event object).
In short: event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running.
1)event.stopPropagation():
=>It is used to stop executions of its corresponding parent handler only.
2) event.stopImmediatePropagation():
=> It is used to stop the execution of its corresponding parent handler and also handler or function attached to itself except the current handler.
=> It also stops all the handler attached to the current element of entire DOM.
Here is the example: Jsfiddle!
Thanks,
-Sahil
Here is a demo to illustrate the difference:
document.querySelectorAll("button")[0].addEventListener('click', e=>{
e.stopPropagation();
alert(1);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
e.stopImmediatePropagation();
alert(1);
});
document.querySelectorAll("button")[0].addEventListener('click', e=>{
alert(2);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
alert(2);
});
<div onclick="alert(3)">
<button>1...2</button>
<button>1</button>
</div>
Notice that you can attach multiple event handlers to an event on an element.
event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running. For example, see below jQuery code block.
$("p").click(function(event)
{ event.stopImmediatePropagation();
});
$("p").click(function(event)
{ // This function won't be executed
$(this).css("color", "#fff7e3");
});
If event.stopPropagation was used in previous example, then the next click event on p element which changes the css will fire, but in case event.stopImmediatePropagation(), the next p click event will not fire.
Here I am adding my JSfiddle example for stopPropagation vs stopImmediatePropagation.
JSFIDDLE
let stopProp = document.getElementById('stopPropagation');
let stopImmediate = document.getElementById('stopImmediatebtn');
let defaultbtn = document.getElementById("defalut-btn");
stopProp.addEventListener("click", function(event){
event.stopPropagation();
console.log('stopPropagation..')
})
stopProp.addEventListener("click", function(event){
console.log('AnotherClick')
})
stopImmediate.addEventListener("click", function(event){
event.stopImmediatePropagation();
console.log('stopimmediate')
})
stopImmediate.addEventListener("click", function(event){
console.log('ImmediateStop Another event wont work')
})
defaultbtn.addEventListener("click", function(event){
alert("Default Clik");
})
defaultbtn.addEventListener("click", function(event){
console.log("Second event defined will also work same time...")
})
div{
margin: 10px;
}
<p>
The simple example for event.stopPropagation and stopImmediatePropagation?
Please open console to view the results and click both button.
</p>
<div >
<button id="stopPropagation">
stopPropagation-Button
</button>
</div>
<div id="grand-div">
<div class="new" id="parent-div">
<button id="stopImmediatebtn">
StopImmediate
</button>
</div>
</div>
<div>
<button id="defalut-btn">
Normat Button
</button>
</div>

Categories

Resources