My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Bubbling and Capturing events

Priyansh Maurya's photo
Priyansh Maurya
·Sep 7, 2021·

3 min read

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

s

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.

as

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.

capturing

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.

capturing-2

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.

event-sudo

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()

cx

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.