Event Bubbling and Capturing in JavaScript
How to Handle JavaScript Event Propagation Life Cycle
JavaScript event bubbling is there to capture and handle events propagated inside the DOM. But do you know, there are differences between event bubbling and capturing?
So, in this article, I will discuss all you need to know about the topic with relevant examples.
Understanding the Event Flow
Before jumping to bubbling and capturing, let’s find out how an event is propagated inside the DOM.
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.
There are three phases in a JavaScript event,
- Capture Phase: Event propagates starting from ancestors towards the parent of the target. Propagation starts from
Window
object. - Target Phase: The event reaches the target element or the element which started the event.
- Bubble Phase: This is the reverse of capture. Event is propagated towards ancestors until
Window
object.
The following diagram will give you a further understanding of the event propagation life cycle.
Since now you understand event flow inside DOM, let’s see how event capturing and bubbling come into the picture.
What is 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.
What is 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.
I think now you have a good understanding of event flow, event bubbling, and event capturing. So, let’s see when we can use event bubbling and event capturing.
Usage of Event Capturing & Bubbling?
Usually, event propagation can be used whenever we need to execute a function globally. So, for example, we can register document-wide listeners, which will run if an event occurs inside the DOM.
Similarly, we can use bubbling and capturing to perform UI changes.
Suppose we have a table that allows users to select cells, and we need to indicate the selected cells to the user.
In this case, assigning event handlers to each cell will not be a good practice. It will ultimately result in code repetition.
As a solution, we can use a single event listener and make use of event bubbling and capturing to handle these events.
So, I have created a single event listener for table
and it will be used to change the styles of the cells.
document.querySelector("table").addEventListener("click", (event) =>
{
if (event.target.nodeName == 'TD')
event.target.style.background = "rgb(230, 226, 40)";
}
);
Inside the event listener, I have used nodeName
to match the clicked cell and if it matches, the cell color will be changed.
Preventing Event Propagation
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 I 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 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(); //stop bubbling
$("#deleteModal").modal();
});
Build applications differently
OSS Tools like Bit offer a new paradigm for building modern apps.
Instead of developing monolithic projects, you first build independent components. Then, you compose your components together to build as many applications as you like. This isn’t just a faster way to build, it’s also much more scalable and helps to standardize development.
It’s fun, give it a try →
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.
So, I invite you to try out the above examples and share your experience in the comments section.
Thank you for Reading !!!