First of all, let's start by understanding what an event is. An event is a specific occurrence or happening that is usually initiated by the user or the browser, however, it can also be initiated by Javascript, but this last case is rare.
In simple terms, using the most typical scenario—which is when a user does something—we could say that an event is an occurrence that gets triggered when a user interacts with the web page. These interactions might be clicking a button, pressing a key, moving the mouse pointer, etc.
Now, the other part to understand is event handling. Every time these events occur, let's say, the user clicks on a button, we want to do something about it right? We want that button to have functionality. Well, this is where event handlers come in. Event handlers are the functions or pieces of code that are executed in response to these events or, in this case, to the user interactions. Following the previous example, the event handler will respond to the clicking of the button by the user, and, as we said, these handlers are nothing more than code.
Okay, so we already know that there are interactions that are handled, that is, the events and the event handlers. However, as we know, not just buttons can trigger events. In Javascript, we can assign event listeners to most of the elements. For example, what would happen if you had a piece of code like the following (a button inside a div):
In this example, both have event listeners attached to them and, as you can see, both alerts in the event handlers of the two elements—button and div—are shown. Why are they both being shown? Well, if you think about it, when you click on the button, you are also clicking on the div, right? The answer is yes. When you click on the button, you are not only clicking the button but the parent of the button as well—in this case, the div—which is why you see two alerts, one for the button and one for the div. And if there were more divs creating a deeper nesting, the same would apply to them. For example:
In this case, the other divs will also be clicked when clicking on the button and, because those divs have event listeners attached to them, they are triggered as well.
This is what we call event propagation. It's the process by which an event in the DOM (Document Object Model) is transmitted or “propagated” through the hierarchy of elements. When an event occurs on an element, it triggers event handlers not only on the target element but also on its ancestors or descendants.
But then, what is the problem? How does this affect us? Okay, the problem may or may not be, it all depends on what you want your app to do. “div” tags are usually used to wrap components up, so it doesn't mean you will have to attach event listeners to each of them, but in case you actually wanted to, what we just explained is what would happen, and it may be the case where you don't want that to happen. For example, a real-life scenario where that would affect the functionality of a part of your app is the following:
What we have on the image is a list of cards of places to book—let's say our app is about booking places or making a reservation—and basic information is displayed on them. However, when you click on the cards, you want to open another page where you can see more information about the service. But also, we can see a blue button that says “Book”; that button would open another page so you can make the payment. However, if you click on the blue button, you will be opening both pages because, technically, you are clicking on both elements, the blue button, and the div that is the whole container of the card. Maybe this is a trivial example, but it might be something you wouldn't like to happen, or, you might be faced with another problem.
And why do you think this is all happening? You're right, it's because of Event Propagation. When you click on the button, you are also clicking the whole card, and remember, the whole card and the button have different functionalities—they open different pages for you.
Other examples where event propagation might affect are these:
The first picture is taken from a website that offers paid courses, and what you see is part of the list of the contents of one of the courses. As you can see, it is a card with a checkbox inside of it. The checkbox is a mark that specifies you've completed that lecture—which allows you to have better control of your progress—and if you click outside of the checkbox, you open the video of that lecture. Maybe you just want to check or uncheck a video checkbox without actually opening it. Thanks to event propagation, you would be checking the checkbox, but also opening the video (maybe you don't want that.)
This doesn't actually happen on that website though, because they are stopping the propagation. We will talk about this later.
The second image is from a platform you already know, YouTube. This is the thumbnail of a video. As you already know, you can preview the video by hovering the mouse over the thumbnail and these two icons—mute and captions—appear on the thumbnail. Maybe you would like to click on those, but not to open the actual video. Because of event propagation, that's what would actually happen, opening the video when you meant to only click on one of the icons. However, this doesn't happen on YouTube because, again, they stop the propagation.
Event Propagation Phases
Event Propagation has three phases, and that is what we are going to see next. These three phases are the Capturing Phase, the Target Phase, and the Bubbling Phase.
Capturing Phase
During the capturing phase, the event is first captured and handled by the outermost element, and then propagated to the inner elements. The event travels from the outermost ancestor down to the target element, and the event handlers that are registered during this process are executed. For example, take a look at this picture:
As we know, The HTML document consists of a tree of elements, with the <html> element serving as the outermost container for other elements, and is from here where the event will start traveling all the way down to the innermost element that triggers the event. And while it makes its way down, it will be registering events, handling them, and propagating them inward.
In the example of the image, the event, let's say caused by a click on Element 1, is traveling through all of these elements (starting from the <html>) until it reaches the target, which is the Element 1 that we clicked.
Target Phase
This phase is when the event reaches the target element. The target element will always be the innermost element of where we clicked. All this time we've used the example with a button that is inside a div, well, the innermost element in that same example would be the button that we clicked; so that would be the target. This is something important to remember, the target element will always be the innermost element of where we click.
Bubbling Phase
With bubbling, the event is first captured and handled by the innermost element, and then propagated to the outer elements. This is exactly the inverse of the capturing phase, that is, the event travels from the innermost element, which is the target element, to the outermost ancestor, which is the <html>, and the event handlers that are registered during this process are executed. Take a look at the image:
While it makes its way up, it will be registering events, handling them, and propagating them outward.
As you can see, it's, basically, the inverse of the capturing phase. The trigger is fired by Element 1 when clicked and then travels all the way up to the <html>.
Examples / Demonstrations
Something you have to know is that, by default, the addEventListener (which is the method we use to attach event handlers to elements) works in a “bubbling mode,” that is, the event handlers will be executed starting from the innermost element to the outermost element. Take a look at the piece of code below.
When you click on the “click me” button, the order of execution of these elements is from the innermost, in this case, the “click me” button, to the outermost, in this case, the div with the number 3, and you can see that by seeing the text on the alerts that are being shown.
If you want to activate the “capturing mode,” you will have to pass the boolean value “true” as the third parameter in the addEventListener function.
Try the code below and you will see the order of the alerts inverted. That is because the event handlers are being executed from the outermost to the innermost, which is how capturing works.
Finally, we just want to show you that the target element will always be the innermost element, no matter what mode it is on. In the following example, we have the same code working in the bubbling mode, and the target is the button, which is the innermost element as you can see when you click on the button. All the elements display the same element as the target element, even if they are not the current element being executed. Pay attention to the alerts.
Now, if it is in capturing mode, the same result is yielded, the innermost is always the target element. You can also try clicking on the other elements than the button, and realize that the target element keeps being the innermost of where you click.
Stopping the event propagation
We have a method that we can use to stop the event propagation if we want to, that is, stopPropagation(). It is used like this:
If you click on the button now, you see that only its event handler is executed, and not the ancestors' event handlers (the div tags.)
If you change to the capturing mode and stop the propagation in the outermost ancestor, you will see only the alert of that outermost ancestor being shown.
Events that don't propagate
There are some events that don’t propagate. When we say that an event doesn’t propagate, it means that the event doesn’t follow the typical event flow through the DOM hierarchy. The event doesn’t traverse up or down the DOM tree from the target element to the root or vice versa. For events that don’t propagate, one or more of these phases are skipped. These events are typically handled directly on the target element and don't involve ancestors or descendants.
The list of events that don't propagate in the DOM is relatively small, and they are generally related to user interface interactions. Here are some of the commonly used events that don’t propagate:
focus: Fired when an element receives focus.
blur: Fired when an element loses focus.
mouseenter: Fired when the mouse enters an element.
mouseleave: Fired when the mouse leaves an element.
input: Fired when the value of an <input>, <select>, or <textarea> element changes.
change: Fired when the value of an <input>, <select>, or <textarea> element changes and the element loses focus.
scroll: Fired when the document view or an element is scrolled.
resize: Fired when the document view is resized.
wheel: Fired when the mouse wheel rolls up or down over an element.
So, if you ever stumble upon a code using even listeners, that is not working as expected or behaving strangely, it may be because of how certain events work (like these,) and you might want to delve into that.
Summary
To summarize, we will give you the important points we consider you should remember.
Event propagation is the process by which an event in the DOM is transmitted or “propagated” through the hierarchy of elements.
An event is a specific occurrence or happening that is usually initiated by the user or the browser.
An event handler is the code that will be executed when an event happens.
In modern Javascript, addEventListener is the method you will use to attach event listeners to an element.
Events propagate, which means all the elements that have event listeners attached, and that are in the range of where you click on will be triggered.
To stop the event propagation, you use the method stopPropagation().
Event propagation has three phases: the Capturing Phase, the Target Phase, and the Bubbling Phase.
The Capturing Phase goes from the outermost element to the innermost element.
The Bubbling Phase goes from the innermost element to the outermost element.
The target element will always be the innermost element.
Certain events do not propagate, like these: focus, blur, mouseenter, mouseleave, input, change, scroll, resize, and wheel.