Event Bubbling
Event bubbling is the default propagation mechanism (sequence) in the DOM, where an event first occurs on a targets element (e.g. <button>), it first runs on that element, then bubbles up to its parent, then grandparent (e.g <div>), and so on up the DOM three , until it reaches the root (i.e. <html>).
Event bubbling allows the parent elements to handle events that originate from their descendants, nesting element. By attaching event listeners to these elements, you can respond to user interactions, for instance: you can add event listener to a <div> with a <button> in it, such that when the <button> inside got clicked, the <div> border changes colour.
Example Event Bubbling using Button
As an example: for the following code, you get event bubbling from <button> to its parent <div>, to the grandparent <div>, … , till the very top level root <html> element (that triggers their corresponding handleClick events).


(Source code: index-lightdom-basic.html)
Stop Propagation (Event Bubbling)
You can stop the propagation of events (bubbling) via calling the .stopPropagation() function. For instance, if in the handleClick function, we check if the current event’s element is container-level3 and if true stop the propagation, you’ll get the following:

(Source code: index-lightdom-stop-propegation.html)
Event Bubbling in Web Component
Event bubbling in web component are controlled by the bubbles and composed property of an event:
- if an event have its
bubblesproperty set totrue, then the event will bubble inside the shadow root - if an event have its
composedproperty (read only) beingtruethen the event can bubble through shadow boundary, in another word, it will escape the shadow root into the outside DOM element (element outside the shadow root will see it)
Consider the following custom event triggered on button click:
| |
It will bubble up inside the shadow root, but it will never cross the shadow boundary and fire the listener outside the shadow root. (i.e. html, body, and div containers outside the shadow root)
Example via Custom Event:
Two confetti button, one with composed property set to true, one set to false


(Source code: index-shadow-confetti-composed-true-false.html)
For Native Events:
In the above example we have created a custom event to manually configure the composed property, but for the pre-existing events for DOM elements, it is in fact a read-only property that cannot be configured, usually all UA-dispatched UI events (events that originate from the browser’s user input system and propagate through the DOM as standard, native UI events) such as click/touch/mouseover/copy/paste are composed (true). That is they will propagate through the shadow boundary.
But for other events that are not UA-dispatched, the composed property will be set to false, and the event will not bubble through the shadow boundary and trigger the listeners in the light dom. You can check if an event is composed (true) via the following:
| |
(see more at this documentation: https://developer.mozilla.org/en-US/docs/Web/API/Event/composed#examples)