When reading through Stack Overflow posts having to do with Javascript, I sometimes came across the term Event Bubbling and never knew what it was really. I decided to sit down and spend some time learning about it so I wouldn’t be puzzled anymore by it. My main learning resources were two excellent Youtube tutorial videos: Javascript Event Capture, Propogation and Bubbling by Wes Bos and Event Bubbling by The Net Ninja. This is an excellent article as well on the topic. I recommend you at least watch the Youtube tutorials, but I am going to attempt to boil the subject down into a single post here.
Event Bubbling In a Nutshell:
- Events (such as ‘click’ events, for example) are registered not only by the target element where they originate, but by all of that element’s parents all the way up to the global element (i.e. the Window/Browser).
- An Event occurs (a ‘click’, for example) and the click event is registered (this means recorded or noted and recognized by the browser). The registered click event then goes from the Window Object down through the child elements and is registered on each during a ‘Capture Phase’ (
<html>
–><body>
–><div>
–><form>
–><button>
, for example) to find the Target Element (where the click event originated from, i.e. what element was clicked). Once the click event has registered on the Target (‘Target Phase’), it then bubbles back up the DOM Tree (‘Event Bubbling Phase’) and is registered on all parent elements up through the Window Object triggering any Event Listeners or Handlers in the process.
Why it’s important to understand:
- To avoid unintentional Event Handle triggering on parent elements of the target, or to intentionally incorporate handling the event on a parent element.
- If there are any identical Event Listeners on the parent elements of the Event Target, then their respective Event Handlers will be triggered. A click event on element A will trigger element A’s Event Handler as well as Element A’s parents’ Event Handlers for a click event.
For more information and examples, keep reading:
Definitions:
- Window Object: My non-technical understanding is to just think of it as the browser or the window you have open in the browser. It contains all of the elements on the page.
- Event Target: The element that the Event originated from (i.e. the element that was clicked, for example).
- Event Capture: The process of recording events that occur starting at the top of the DOM (the Window Object) and down through the parent elements related to the Event Target.
- Event Propagation: To propagate, literally means to spread and promote widely. The event is ‘spread widely’ and registered throughout the DOM tree from the top (Window Object) down to the event target, and back up to the top again.
- Event Bubbling: The process in which the Event is registered on each successive parent element of the Event Target element all the way up to the Window Object.
- Event Target Phase: This occurs in between the Capture and Bubbling Phase during Event Propagation. During this phase, any listeners on the Event Target for the Event and their respective functions will be triggered and invoked.
The Process:
Event Bubbling happens in the course of what’s called Event Propagation, which has three main parts:
- The Capture Phase: The event is recorded from the top of the DOM and down to find the event target.
- The Target Phase: The Event is registered on the Target (the element it originated from) and any Listeners or Handlers are fired.
- The Event Bubbling Phase: After the Capture and Event Target Phase, the event is registered on each parent element of the Event Target, in order, up through the DOM Tree to the Window Object.
Important Note: Branch paths in the DOM for Events during Event Bubbling are static: If the DOM tree is modified after the Event Listeners have been assigned, the modified DOM tree or added elements will not be used or included in the Event Bubbling process.
For example, if the handling of the Event involves creating/appending/inserting an additional parent element of the Event Target, then the inserted parent element will not register the Event as it bubbles up the DOM tree on future click events originating from the Event Target. The Event will bubble only up through parent elements present in the DOM Tree when the Event Listener for that target was assigned (for instance, at the loading up of the page).
Additionally, an Event Listener that is assigned to a class of elements will not be applied to any elements of that class that are later added to the DOM Tree.
Scenarios Where Understanding Event Bubbling Can Help:
Example (consider the following HTML which contains a list of items with buttons that give the user the option to remove the item from the list, or add an item to the list):
<div> <ul id='theList'> <li id='item_copy'>List Item <button class='btn_remove'>Remove</button> </li> <li>List Item <button class='btn_remove'>Remove</button> </li> </ul> <button id='btn_add'>Add Item</button> </div>
Now, the Javascript to assign Event Listeners and Handlers:
// Add a list item to the list when Add Item clicked: <script> const parent_el = document.getElementById('theList'); const li_content = document.getElementById('item_copy').innerHTML; const add_btn = document.getElementById('btn_add'); add_btn.addEventListener('click', () => { let created_li = document.createElement('li'); created_li.innerHTML = li_content; parent_el.appendChild(created_li); }); // Assigns click event listener and handler for the remove buttons currently on the page: const remove_btns = document.getElementsByClassName('btn_remove'); // A click on .btn_remove removes item from the list: Array.from(remove_btns).forEach(function(btn) { btn.addEventListener('click', (e) => { let target_btn = e.target; let li_to_remove = target_btn.parentElement; li_to_remove.remove(); }); }); </script>
In the above example, the remove buttons will not work on list items that are added by the user with the Add Item button. The reason is because when the Event Listeners were assigned to the Remove buttons, the DOM Tree did not include the added list items created when the user clicks Add Item. So the Event Listeners and click handlers don’t exist on the newly created list items.
This is where Event Bubbling can come in to save the day. You use this process to your advantage to solve this problem by assigning the Event Listener and Handler for the click to the parent element of the user added list items (this would be the <ul> with the id of “theList”).
This way when the user clicks on the remove button on the newly created list item, the click event will bubble up through the parent elements and the Event Listener on <ul> “theList” will be ready and waiting for it. Once the click on the newly added remove button registers on <ul>, it’s target can be found and the remove element function fired.
Example:
// Grab the pre-existing parent element (<ul>): const the_List = document.getElementById('theList'); // Add the listener to #theList and pass in the event: the_List.addEventListener('click', (e) => { // Check to see what the Event Target of the caught click was. // If it was the remove btn, then remove the list item: if (e.target.className == 'btn_remove') { let list_item = e.target.parentElement; list_item.remove(); } });
Now, the list items added by the user after the page loads will be removed when the user clicks the remove button. The <ul> ‘theList’ catches the click event as it bubbles up the DOM tree and handles it by finding the Event Target (the remove button clicked) and removing the parent element of it (the list item).
Hopefully, this summary on Event Bubbling will prove to be helpful to those wanting to get their feet wet in the subject and start to understand what it is and how understanding it can help solve problems like this that come up in your code.