Before we begin, let's consider a scenario. Say we're building a User card. It has the photo of the user, their name and a button to send a message to them. If we click anywhere on the card, a description of the user appears. If we click the send message button, a textbox appears to send them a message.
The design is simple. We develop the structure with HTML and CSS. As for the interactions, we add a click handler to the card, which opens the user details and a click handler to the button in the card that opens the message panel.
Yeah, that should work, right? Let's try it out and see for ourselves.
Well, it didn't work. You're right if you think that by clicking the button, we are indirectly clicking the card. Both the event handlers are executed when we click the send message button. So, how do we implement this?
To figure this out, we must first understand how JavaScript tracks the events in the DOM. We must determine when JavaScript executes the event handlers. We must learn about Event Propagation in JavaScript DOM.
Event Propagation - movement of Events through the DOM
JavaScript uses Event Propagation to handle how events travel through the Document Object Model (DOM) when an event occurs and reaches the target element, triggering further actions based on the event.
It happens in three phases - Capturing, Targeting and Bubbling.
1. Capturing - This is the first phase in the event propagation process. When an event is triggered, it starts from the root of the DOM tree and then moves down towards the target element. By default, event handlers are not executed in this phase.
2. Targeting - This phase starts once the event reaches the element where the event was triggered. Any event handlers associated with the target element for the particular event get executed in this phase.
3. Bubbling - After targeting, the event traces back its path and moves back up to the root of the DOM. It is in this phase that the event handlers execute. JavaScript executes the associated event handlers in order as the event moves up the DOM tree.
If you got this, you must have figured out what is happening in our example. When we click the button, the click event starts capturing. It first captures the .user
element, then the .user__description
and then the .user__cta
, which is the button. Upon targeting the button, it renders the message panel as per the event handler. Then, it starts bubbling, and when it reaches the .user
element, it executes the associated event handler and renders the details, replacing the message panel.
Now, we have figured out what is happening here. Let's see how we can deal with this.
Manipulating Event Propagation
As you must have understood, to make our example work, we must stop the propagation once the event handler for the button executes. We have two utilities for this - event.stopPropagation()
and event.stopImmediatePropagation()
. Both of them block the propagation of the event once encountered. There is only one difference in their work.
The event.stopPropagation()
stops the event from going to the next element. All handlers associated with the current element for that particular event will still get executed. This does not happen in event.stopImmediatePropagation()
where propagation stops immediately, and no event handlers execute even if they belong to the current element.
With this knowledge, let's update our solution.
Executing in the Capture Phase.
I said earlier that event handlers do not execute during the capture phase by default. You must be thinking if there is some way to do that. True, there is a way to do this. Ironically, the solution to this problem is also true
.
You must have used the addEventListener()
utility with two parameters to attach events to elements - the event type and the handler function. It also takes a third parameter called useCapture
. Its default value is false
. If we pass true
to this, then the handler will be executed at capturing instead of bubbling.
element.addEventListener('click',handlerFn,true);
Well, here's a question for you folks. What will happen if we call event.stopPropagation()
in a handler executed during the capture phase? Comment down the answers in the comment section 😉.
Optimization with Event Delegation
Event delegation is a technique where you delegate the event listener to the parent element to listen to events triggered by its children. We usually do this when the children perform similar actions upon an event.
Think of a Navbar with many links. Upon clicking a link, an associated dropdown menu opens. The direct way to do this will be to attach a listener to every link. But that would result in too many event handlers all doing almost the same task.
We can implement event delegation here to optimize this. We can attach the listener to the direct parent of the links. So, instead of having multiple listeners, we will have a single listener that will listen to clicks on any child link.
But how do we determine which link we clicked? For that, we look at the target
property of the event
. This property holds the node where the event was triggered. With it, we can determine the link we clicked and then open the respective dropdown accordingly.
That's how Event Delegation works. Here's the demonstration for the above example.
That's all I had to say
Hey there, we've come to the end of this article! I hope it was helpful and informative for you. I'm excited to hear your thoughts and feedback, so don't hesitate to drop a comment below.
By the way, if you want to connect with me outside of this platform, you can find me on Twitter, LinkedIn, and GitHub. Here are the links:
Thanks for reading this far, and keep exploring new things!🌟
Top comments (1)
If we call event.stopPropagation() in the event handler that has truthy useCapture, the event propagation will stop at the capture phase and will not proceed to further capture phases, not to mention event targeting and bubbling phases.