Bubbling and Capturing events
Events are the actions that occur throughout our code. Situations in which user pressing a key on his keyboard, or scrolling his mouse happen in real time and with events we have a way to react to them. This is not limited to the user actions, there are many different events that can happen.
JavaScript event bubbling is there to capture and handle events propagated inside the DOM.
Understanding Event flow
If we have several nested elements handling the same event, we get confused about which event handler will trigger first. This is where understanding the event order becomes necessary.
Usually, an event is propagated towards the target element starting from its parents, and then it will propagate back towards its parent element.
Event flow in Dom
Event Capturing
Event capturing is the scenario in which the events will propagate, starting from the wrapper elements down to the target element that initiated the event cycle.
If you had an event bound to browser’s Window, it would be the first to execute. So, in the following example, the order of event handling will be Window
, Document
, DIV 2
, DIV 1
, and finally, the button
.
Here we can see that event capturing occurs only until the clicked element or the target. The event will not propagate to child elements.
We can use the useCapture argument of the addEventListener()method to register events for capturing phase.
target.addEventListener(type, listener, useCapture)
You can use the following snippet to test out the above example and get hands-on experience in event capturing.
window.addEventListener("click", () => {
console.log('Window');
},true);
document.addEventListener("click", () => {
console.log('Document');
},true);
document.querySelector(".div2").addEventListener("click", () => {
console.log('DIV 2');
},true);
document.querySelector(".div1").addEventListener("click", () => {
console.log('DIV 1');
},true);
document.querySelector("button").addEventListener("click", () => {
console.log('CLICK ME!');
},true);
Event Bubbling
Event bubbling is pretty simple to understand if you know event capturing. It is the exact opposite of event capturing.
Event bubbling will start from a child element and propagate up the DOM tree until the topmost ancestor’s event is handled.
Omitting or setting the useCapture
argument to ‘false’ inside addEventListener()
will register events for bubbling. So, the default behavior of Event Listeners is to bubble events.
In our examples, we used either event capturing or event bubbling for all events. But what if we want to handle events inside both phases?
Let’s take an example of handling the click events of Document and DIV 2 during the bubbling phase while others are handled during capturing.
The click events attached to Window
, DIV 1
, and button
will fire respectively during the capturing, while DIV 2
and Document
listeners are fired in order during the bubbling phase.
window.addEventListener("click", () => {
console.log('Window');
},true);
document.addEventListener("click", () => {
console.log('Document');
}); //will cause bubbling
document.querySelector(".div2").addEventListener("click", () => {
console.log('DIV 2');
}); //will cause bubbling
document.querySelector(".div1").addEventListener("click", () => {
console.log('DIV 1');
},true);
document.querySelector("button").addEventListener("click", () => {
console.log('CLICK ME!');
},true);
Preventing Event Propogation
Sometimes event bubbling and capturing can be annoying if it starts to fire events without our control. This can also cause performance issues if you have a heavily nested element structure because every event will create a new Event Cycle.
In the above scenario, when we click the ‘Delete’ button, the click event of the wrapper element is also triggered. This happens due to the event bubbling.
We can use the stopPropagation()
method to avoid this default behavior. It will stop events from propagating further up or down along the DOM tree.
document.querySelector(".card").addEventListener("click", () => {
$("#detailsModal").modal();
});
document.querySelector("button").addEventListener("click",(event)=>{
event.stopPropagation(); //will stop bubbling
$("#deleteModal").modal();
});
After Using stopPropogation()
Conclusion
JavaScript event bubbling and capturing can be used to handle events effectively inside web applications. Understanding the event flow and how capturing and bubbling works will help you optimize your application with correct event handling.
For example, if there are any unexpected event firings in your application, understanding event capturing and bubbling can save you time to investigate the issue.