What is event bubbling & capturing?
Did you ever click on some element and some other event got fired? And you did not know why is it happening so?
It's most probably got to do with event bubbling and capturing, let us understand what are these concepts and how can they be used for our benefit rather than being troubled by them.
Here two main concepts occur namely event bubbling and event capturing, event capturing happens first and then bubbling takes place.
The cycle is shown below
Event Bubbling
Whenever an event occurs (eg click) the browser scans the DOM tree in the bottom to top trend, starting from the target (an element which was clicked) to the window, and triggers all the event handlers it finds along the way.
For eg let's consider the below code
<div id="grandparent">
Grandparent
<div id="parent">
Parent
<div id="child">Child</div>
</div>
</div>
After we style it a bit we get the below output on our screen
And also we add click event listeners to all the three boxes
document.querySelector('#grandparent').addEventListener('click', () => {
console.log('grandparent clicked');
});
document.querySelector('#parent').addEventListener('click', () => {
console.log('parent clicked');
});
document.querySelector('#child').addEventListener('click', () => {
console.log('child clicked');
});
What will happen if we click the child box??
Since the browser bubbles up the events by default, the browser will start from the target element ie the element which was clicked and move upwards the DOM tree and while doing so will execute all the click event handlers, therefore the output will be
Event capturing / trickling
Event capturing, also known as trickling is just the opposite of event bubbling.
In event capturing browser starts from the window and moves down till the target element and executes all the event handlers which it encounters in its way.
The cycle of bubbling and capturing happens all the time behind the scenes, but by default events are executed in the bubbling phase, to change this behaviour and execute the events in the capturing phase we can pass the third argument to the eventListener, useCapture
It is a boolean value.
eventListener(’click’, () ⇒ {}, useCapture)
If useCapture is a truthy value then that particular event will be triggered during the capturing phase. If it's falsy or is not provided it will execute in the bubbling phase.
eg
document.querySelector('#grandparent').addEventListener(
'click',
() => {
console.log('grandparent clicked');
},
true
);
document.querySelector('#parent').addEventListener(
'click',
() => {
console.log('parent clicked');
},
true
);
document.querySelector('#child').addEventListener(
'click',
() => {
console.log('child clicked');
},
true
);
In this case, since we pass true as the third argument, all the events will be triggered during the capturing phase (top-down)
One case to test the understanding is:
document.querySelector('#grandparent').addEventListener(
'click',
() => {
console.log('grandparent clicked');
},
true
);
document.querySelector('#parent').addEventListener(
'click',
() => {
console.log('parent clicked');
},
false
);
document.querySelector('#child').addEventListener(
'click',
() => {
console.log('child clicked');
},
true
);
Try to guess what will be the output when we click the child box?
It will be
How?
Let us traverse the whole cycle-
First, the capturing phase starts from the window and the first event it comes across is grandparent, browser sees that true is passed as the third argument, so it executes the event, and prints ‘grandparent clicked’.
Next, it finds the parent, here the third argument is false, so this event is skipped.
Next, it reaches the target element and since its third argument is true, its event is executed and ‘child clicked’ is printed.
Now starts the bubbling cycle,
The target element is already executed.
Next, the parent is encountered, since its third is false, its event is executed and ‘parent clicked’ gets printed.
Next, the grandparent is met but it was already executed in the capturing cycle.
You can try different combinations and test your understanding.
e.stopPropagation
The bubbling or capturing cycle can be stopped using e.stopPropagation, e is the event object available in all event handlers, the moment e.stopPropagation is encountered the current event is executed and the cycle stops there.
Event Delegation.
Event delegation is a use case of event bubbling which comes in use when there are a lot of child elements. We can use one event handler on the parent to deal with all the events of the children
Consider the case wherein you are making a video library, we have all the videos listed on the video listing page, on clicking any of the videos we open the single video page. Here we are performing same kind of operation on all the videos, so rather than making one event handler for each video, we can make one event handler on the parent div which can handle all the events.
Consider:
<div id="video-container">
<div id="video1">video1</div>
<div id="video2">video2</div>
<div id="video3">video3</div>
<div id="video4">video4</div>
<div id="video5">video5</div>
</div>
And the javascript
document.querySelector('#video-container').addEventListener('click', (e) => {
console.log(e.target);
});
Here when we click on any of the videos, the event gets bubbled up to the #video-container and inside its event handler, we get access to the target element using e.target
Suppose we click on video 5, we can see the following in the console.
This way we have all the data of the target element and we can perform any desired operation using its data.
We can take advantage of event bubbling, capturing / trickling and event delegation to decide the order of event execution, write less code and keep our files small using event delegation, at the same time one should know that all the events don't get bubbled up.
Refer to https://en.wikipedia.org/wiki/DOM_events#Events for the list of events.
References
- Akshay Saini's youtube: Akshay Saini
- Wikipedia: Wikipedia
- javascript . info: javascript.info