What is Event Bubbling in JavaScript?
When an event happens on an element, the respective handler is run and is bubbled on to the parent element, the parent element handles the event and the event is bubbled on to its ancestors (except when bubbling is stopped).
Event Bubbling with an example
Let’s look at a sample HTML.
<table id="grid"> <thead></thead> <tbody> <tr> <td>row 1 cell 1</td> <td>row 1 cell 2</td> </tr> <tr> <td>row 2 cell 1</td> <td>row 2 cell 2</td> </tr> <tr> <td>row 3 cell 1</td> <td>row 3 cell 2</td> </tr> <!-- some other tr rows --> </tbody> </table>
When an event happens on any of the TD
element in the above code, the event handler for that TD
element will be fired and executed, once the TD
event handler completes its operation, then its parent event listener is fired. This is called event bubbling.
So, the TR
element handles the event and passes it to the tbody
, the tbody
handles its specific thing and passes it to the table
and so on until the document object is reached.
Event Bubbling in Action
To see this in action, let’s add event listeners for every HTML tag in the above HTML. Here’s the demo in jsfiddle. Just try to click on any td
cell and observe that all the listeners are fired all the way up to the document listener.
$(function(){ $("td").on("click", function(e){ alert($(this).text()); }); });
Now, the above code looks pretty obvious but imagine we have 100 rows (TR) and every row is having 5 columns (TD), then the click event listener will bind to all the 500 (100 * 5) TD elements.
To solve this, we’ll just attach a listener on the grid and see if the click happened on the TD element and then take necessary action. This needs a little work, but it is just a single listener for the entire grid.
In our case, the logic should be pretty easy
$(function(){ $("#grid").on("click", function(e){ var cell= $(e.target); //Get the cell if(cell.is('TD')) alert('Cell data: ' + cell.text()); }); });
Now, if we click on any of the TD
element in the table we should get an alert with the cell text.
But how does it work? Because of the event bubbling.
The TD
element receives a click and there is no handler attached to it so bubbled up to tr
, tbody
these don’t have any listeners it bubbles up to the table element which has a click event handler and is fired.
Note that once the event is handled in the table (grid) it doesn’t stop there it still bubbles up to the document object (because we didn’t stop the bubbling).
Well, this could be a problem because firing every click event until the body of the element will sometimes result in unexpected behavior in the application. We can stop this event from bubbling up the hierarchy.
To catch the bubbling we can have a listener on the parent element so that we can take appropriate action based on the target element. When this is implemented, we will call it an event delegation.
Javascript solution for event delegation
This is a javascript solution to event delegation.
var table = document.getElementById("grid"); table.onclick = function (evt) { var td = evt.target.closest("td"); //evt.target is the element that recieves the click if(!td) return; //if the click happens on any other element we can ignore the handling //do something with the event or take necessary action }
If you are using plain javascript, then you should remember one thing here. The event.target
is always the element that is clicked on and not the element it is bubbled up to.
jQuery event delegation
Instead of verifying if the clicked element is TD or not, jQuery has support for event delegation. So, you can specify what element to listen to when a click happens on a grid.
definition
Event delegation allows us to attach a single event listener, to a parent element, that will fire for all descendants matching a selector, whether those descendants exist now or are added in the future.- jQuery
Here’s how we can achieve event delegation in jQuery.
$(function(){ $("#grid").on("click", "td", function(evt) { alert(" the td content is : " + $(this).text()) }); });
The advantage of creating click events with event delegation is that the elements added dynamically will also receive the click events in the future. So, you don’t have to worry about dynamically adding elements.
One might think that this is the same as having the selector combined with the grid
$("#grid td").on("click", function() {});
The above event is not a delegated listener because we are attaching click event listeners for all the td
elements in the #grid
. Whereas the delegated listener will listen on the grid and if the click happens on the td
element then the listener will be fired.
Conclusion
I’d recommend stopping the bubbling(using e.stopPropogation()
) if you have handled the specific event, otherwise, it will fire the parent listeners if you have any.
With event delegation, we can have some performance improvements on the page.
jQuery event delegation is good but if you have to stop/off the event delegation, then the answer is you can’t! Yes, you cannot stop the event delegation on a specific element inside a parent container.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
Pingback: stopPropogation() vs preventDefault() in JavaScript - Code Rethinked