• Bubbling and Capturing in JavaScript
  • Dulanka Karunasena
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Z Zhaojin
  • Proofread by: KimYangOfCat, jaredliw

Describe event capture and event bubbling in JavaScript

JavaScript event bubbling is designed to capture and process events propagated within the DOM. But do you know the difference between event bubbling and event capture?

In this article, I’ll use relevant examples to discuss all you need to know about this topic.

Propagation of event flow

Before we get to event capture and event bubbling, let’s look at how the next event is propagated within the DOM.

If we had several nested elements handling the same event, we would be confused about which event handler would fire first. At this point, it becomes necessary to understand the order in which events propagate.

Typically, an event is propagated from the parent element to the target element, and then it is propagated back to the parent element.

JavaScript events are divided into three phases:

  • Capture phase: Events propagate from the parent element to the target element, fromWindowThe object begins to propagate.
  • Target phase: The element at which the event reaches the target element or starts the event.
  • Bubbling phase: In contrast to the capture phase, the event propagates to the parent element untilWindowObject.

The following figure will give you a closer look at the lifecycle of event propagation:

Now that you have a rough idea of the event flow within the DOM, let’s take a look at how event capture and bubbling come into play.

What is event capture

Event capture is the initial scenario for event propagation, starting with the wrapping element and ending with the target element that initiates the event lifecycle.

If you have an event bound to the browser’s Window object, it will be the first to be executed. So, in the following example, the order of events will be Window, Document, DIV 2, DIV 1, and finally button.

Here we can see that event capture occurs only on the clicked element or target, and the event is not propagated to child elements.

We can register capture phase events using the useCapture parameter of the addEventListener() method.

target.addEventListener(type, listener, useCapture)
Copy the code

You can use the following code to test the above example and get some hands-on experience with event capture.

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);
Copy the code

What is event bubbling

Event bubbling is easy to understand if you know about event capture, which is the opposite of event capture.

Event bubbling starts with a child element and propagates up the DOM tree until the uppermost parent element event is processed.

Omitting or setting the useCapture parameter to false in addEventListener() registers events for the bubble phase. So, event listeners listen for bubbling events by default.

In our example, we use event capture or event bubbling for all events. But what if we want to process events in both phases?

For example, Document and DIV 2 click events are handled in the bubble phase, and other events are handled in the capture phase.

Click events connected to Window, DIV 1, and Button fire separately during capture, while DIV 2 and Document listeners fire sequentially during the bubble phase.

window.addEventListener("click".() = > {
    console.log('Window');
  },true);

document.addEventListener("click".() = > {
    console.log('Document');
  }); // Registered as bubbling

document.querySelector(".div2").addEventListener("click".() = > { 
    console.log('DIV 2');
  }); // Registered as bubbling

document.querySelector(".div1").addEventListener("click".() = > {
    console.log('DIV 1');
  },true);

document.querySelector("button").addEventListener("click".() = > {
    console.log('CLICK ME! ');
  },true);
Copy the code

I think you now have a good understanding of event flow, event bubbling, and event capture. So, let’s take a look at when you can use event bubbling and event capturing.

Application of event capture and bubbling

Normally, we only need to execute a function globally to use event propagation. For example, we can register a document-wide listener that will run if an event occurs in the DOM.

Similarly, we can use event capture and bubbling to change the user interface.

Suppose we have a table that allows the user to select cells, and we need to display the selected cells to the user.

In this case, it would not be a good practice to assign event handlers to each cell. It ultimately leads to code duplication.

As a solution, we could use a separate event listener and use event bubbling and capturing to handle these events.

Therefore, I created a separate event listener for the table, which will be used to change the style of the cell.

document.querySelector("table").addEventListener("click".(event) = >
  {       
     if (event.target.nodeName == 'TD')
         event.target.style.background = "rgb(230, 226, 40)"; });Copy the code

In the event listener, I use nodeName to match the clicked cell, and if it matches, the color of the cell changes.

How can events be prevented from spreading

Sometimes it gets annoying if events bubble up and capture starts to spread out of our control.

If you have a heavily nested element structure, this can also cause performance problems because each event creates a new event cycle.

In this case, when I click the delete button, the click event for the wrapped element is also triggered. This is caused by the bubbling of the event.

We can avoid this behavior by using the stopPropagation() method, which prevents events from propagating further up or down the DOM tree.

document.querySelector(".card").addEventListener("click".() = >{$("#detailsModal").modal();
});

document.querySelector("button").addEventListener("click".(event) = >{
    event.stopPropagation(); // Stop bubbling
    $("#deleteModal").modal();
});
Copy the code

This paper summarizes

JavaScript event capture and bubbling can be used to efficiently handle events in Web applications. Understanding the flow of events and how capture and bubbling work will help you optimize your application with proper event handling.

For example, if any unexpected events start up in your application, understanding event capture and bubbling can save you time troubleshooting problems.

So I encourage you to try the examples above and share your experiences in the comments section.

Thanks for reading!

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.