If you ever worked with JavaScript you must have done some sort of event handling, maybe added an Onclick on a button or some kind of Keyboard event like KeyDown or KeyUp etc.
But have you ever noticed that sometimes you have two onclick functions one in the parent div and another in the child div and when you click on the child div the function of the parent div is also fired?
For example
In this piece of code I have a parent div which have two child div and this is what the functions look like
After adding some css this is how it looks on the browser
Now if I click on either child 1 or child 2 I get this in the logs
Click here to see this in action
But we only clicked on Child div why did this happen?
This behavior of DOM (Document Object Model) in Web Browser is called Event Propagation
Event Propagation
Event Propagation can be defined as how events travel in the DOM, when you click on a button, that event doesn't ends right there, instead it travels till the top of the chain, So if you check the HTML that I added above you can see that there is one more div which is parent of parent div so when you click on the child 1, and try to apply a console log on that top div you'll see this output.
You can find all this in the link I provided just uncomment the correct part you want to see
Now you noticed the pattern in which functions are running? Firstly the child div ran , that's where we clicked and from there it went all the way up following the hierarchy.
This can be explained by two sub parts of Event Propagation
Event Bubbling
So the default behavior that you see in the event propagation is what we call Event Bubbling, as I explained earlier the pattern that we saw in our example where event started to fire from the target element i.e Child Div and from there all the way up to the top of hierarchy like this.
This also explains the term 'Bubbling' the events are bubbled and goes up as bubbles do. Helps in Imagining 😁.
Event Capturing
In the most Simple Terms Event Capturing is the exact Opposite of Event Bubbling.
So if I define it, Event Capturing is a method of Event Propagation in which the event is propagated from the top of the hierarchy and goes all the way down to the target element.
You can check out the Replit Link that I added above to see this in action.
One thing to note is that to apply event capturing you have to use an addEventListener through JavaScript, I tried to look for a way to do it directly with the onclick in HTML but didn't found it anywhere.
So when you create addEventListener just pass true as an argument, which indicates that we don't want the default behavior i.e. Event Bubbling but instead Event Capturing.
Like this
document.getElementById("parent").addEventListener("click", parent_div, true);
So this time our Structure looks like this
And the output be like this if we click on Child
Now when I was learning about this topic I was curious that what would happen If I remove the true argument from one of the hierarchy?
I mean we added true in all the steps like this
What if I remove true from parent?
So I tried to do that and the output was
Why did this happen?
Well as I mentioned before if we don't specify true as an argument then the default behaviour i.e. Event Bubbling gets fired.
So in this case our grandparent div was indicated with event capturing so it followed the Event Capturing principle but as it entered the parent div, there was no indication of Event Capturing so it followed the normal convention and child function was fired first.
Now that we have understood both Event Bubbling and Event Capturing lets talk about its applications, how can we use this to make our code better and more effecient.
So lets take a basic example,
Suppose you have a list of fruits and when you click on a fruit you want to simply return a string which concatenates the fruit name in a pre defined string like 'I like FruitNameHere'
Now normally you can add a function to each and every element of the list pass the fruit name in it or use some event property to send it to the function as an argument to do the processing.
But a more easy or clean way to do this could be that we don't add any onclick function on the list elements at all, instead just add an OnClick on the Parent element like this
Open replit's output in new tab for this one otherwise you won't be able to see the object when you log the event.
So you have to click at two places in this order
- On any fruit
- Outside of the fruit area so basically in the area of parent div
Now when you do this you will see two really big objects logged in the console. Like this
Now when you open the first object (Considering that you clicked in the order that I mentioned above)
You'll see a really big object being opened up, now when you scroll through it you'll see a lot of properties which can be used in a lot of ways but for now keep scrolling and look for target property and when you open it look for another property innerText now this property will have the value of the fruit that you clicked on.
Pretty interesting right? You must have used event.target before too but now you know what it actually means, it shows the all the properties of the element which was actually clicked regardless of Event Propagation so in our case although we added onclick function only on the parent div we can still see the value of our element which was actually clicked.
Now innerText was having the value of the fruit because that's what we entered in that element
But its not over yet, you cant simple log event.target.innerText
right now in the function what if someone clicked outside of the child in the parent div? You'll get the whole list of innerText of elements of Parent returned.
So what can we do about this?
Well now open the second object that you clicked on scroll again to the target and look for id this time, it will have the id that we mentioned in the parent element. So for this problem we can write our code something like this
Here we checked if the id is equal to parent or not, now if someone clicked on apple the id property would be empty or atleast won't be parent so then we will log the name of the fruit using innerText else just log that it's Parent element
Now this is only one use case or way that I told you, which was good for this problem but you can make a lot of different approaches like you have a list of videos, and you have to get its source , just search in the object and you'll find a property named src if you are using video
tag obviously. So yeah there are a lot of possibilities on how you use it.
This whole concept is what we call Event Delegation
So by definition Event Delegation from GeeksforGeeks
Event Delegation is basically a pattern to handle events efficiently. Instead of adding an event listener to each and every similar element, we can add an event listener to a parent element and call an event on a particular target using the .target property of the event object.
Now its not just that Event Delegation helps in making the code look clean and easy there are more advantages of it like,
Performance Optimization - It Improves the performance by reducing the number of event listeners from the DOM. Instead of attaching an event listener to each individual child element, you attach a single event listener to the parent element.
Memory - Since less event listeners are added it decrease the memory usage, compared to attaching an event listener to each and every element.
and ofcourse Code Readability , Easy Maintenance are other perks of it.
Looks pretty good right? Event Propagation is awesome!!
It sure is but it also has some disadvantages of it. Lets talk about that.
So lets take another scenario which looks like this
Now again we have a Grandparent div and a Parent div and inside that we have two buttons add and delete.
Now whenever I click on add button or delete button, it will simply bubble up and will go till Grandparent div, but this time I dont want to run the functions of GrandParent div and Parent div.
Now you must be thinking that, whats the problem? Just add that condition that was used before to check from where the event is coming from. But in how many levels you will add that condition? Right now maybe I can add it in both Grandparent and Parent div but this will not how a real HTML will look like , it would be far more complex and deep. It would be really difficult to maintain and keep track if we keep adding if conditions in each function.
Instead we can use something known as event.stopPropagation().
Event.stopPropagation()
As the name suggests it stop the Propagation and hence will stop the bubbling or capturing so if I add this line directly in the function of add and delete or maybe just in Parent div (if its working in your individual case).
It won't bubble up anymore , here is any example of before and after I added event.stopPropagation() in the add_function.
Before
After
There is one more function like this which is event.preventDefault();
Event.preventDefault()
So what it basically do is it stops the normal working of any event in JavaScript, So if you have any form and you add event.preventDefault() in the submit button, it wont simply submit.
Why you would need it?
You can validate the form before sending, like checking the password length or any other kind of validation according to the need.
In the Replit link you can see that if you don't remove the preventDefault function you can see the log statement, but if its removed the form will directly be submitted.
There are other usecases too, try using it with anchor tag or button tag, or with the one I used in my recent projects Notion Clone
Where I was using a Contenteditable Div and when I was pressing Enter key the cursor was going to next line and I didn't wanted this default behaviour so I used preventDefault function and add the condition like this
So now anytime Enter key was pressed its default behaviour was stopped.
You can find all these in the Replit Link that I provided, try it, make changes in it , play with it.
Thank you for reading this far, let me know what you think about this blog in the comments 😁.
Top comments (4)
A good read. ✨
Thanks a lot, really appreciate it
There is also
event.stopImmediatePropagation()
Thanks a lot for pointing that out, never heard about it before, I'll read about it and then add it