Suggest an editImprove this articleRefine the answer for “Event bubbling”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Event bubbling is the mechanism by which an event that happened on a nested element is passed upward through the DOM tree to its ancestors: from `target` to the parent, then to `body`, `html`, `document` and `window`.** Handlers run at every level where they exist. Bubbling is exactly what makes event delegation and global listeners possible, and `event.stopPropagation()` is what stops it. ```javascript document.body.addEventListener('click', (e) => { console.log('the click reached body from', e.target.tagName); }); ``` **Key point:** an event does not stay on the element, it travels up, and any ancestor can hear it.Shown above the full answer for quick recall.Answer (EN)Image**Event bubbling is the mechanism by which an event that happened on a nested element is passed up the DOM tree to its parents.** In other words: when you click a button, the browser first handles the event on the button itself, then lifts it to the container, to `body` and even to `document`. ## Theory ### TL;DR - The event starts on a specific element (`event.target`) and then travels up the chain of ancestors. - At every level the handlers attached to that element run. - `event.target` is where the event happened, `event.currentTarget` is whose handler is running right now. - Bubbling enables event delegation: one listener on the parent serves any number of children, including ones added dynamically. - `event.stopPropagation()` is what stops the upward travel. - Not every event bubbles: `focus`, `blur`, `mouseenter` and `mouseleave` do not. ### Quick example ```html <div id="parent"> <button id="child">Click me</button> </div> ``` ```javascript document.querySelector('#child').addEventListener('click', () => { console.log('Button'); }); document.querySelector('#parent').addEventListener('click', () => { console.log('Parent'); }); document.body.addEventListener('click', () => { console.log('BODY'); }); ``` A click on the `#child` button logs: ```text Button Parent BODY ``` The handler on the clicked element ran first, then the event bubbled up the tree all the way to `body`. ### The mechanism of bubbling 1. The event starts on a specific element, that is `event.target`. 2. Then it is passed upward: `target` -> its parent -> ... -> `document` -> `window`. 3. At every level the handlers, if any, are invoked. Visually the path looks like this: ```text <button> -> <div> -> <body> -> <html> -> document -> window ``` Every level can "hear" the event as long as it has a handler. Formally, bubbling is the third phase of event propagation. The full cycle is: capturing from `window` down to the target, then the target phase, then bubbling back up. A plain `addEventListener(type, handler)` listens on the bubbling phase; to listen during capturing you pass `{ capture: true }`. ```javascript parent.addEventListener('click', (e) => { console.log(e.target.id); // child: where the click happened console.log(e.currentTarget.id); // parent: whose handler is running }); ``` ### Why bubbling is useful **1. Event delegation (fewer handlers).** You can attach one handler to a parent element and catch events from all of its children. ```html <ul id="menu"> <li>Home</li> <li>Catalogue</li> <li>Contacts</li> </ul> ``` ```javascript menu.addEventListener('click', (e) => { if (e.target.tagName === 'LI') { console.log('Item clicked:', e.target.textContent); } }); ``` This works even for new `<li>` elements added dynamically, because the listener sits on the list, not on each item. Without bubbling the approach would be impossible. **2. Global listeners.** You can listen on the whole document, for example to close a dropdown on an outside click or to log activity: ```javascript document.addEventListener('click', (e) => { console.log('Click on the page'); }); ``` ### Stopping the bubbling Sometimes you do not want the event to travel any further. In that case you use: ```javascript event.stopPropagation(); ``` ```javascript child.addEventListener('click', (e) => { console.log('Button'); e.stopPropagation(); // bubbling stopped }); ``` Now the console shows only: ```text Button ``` Note that `stopPropagation()` does not cancel the browser's default action, that is what `preventDefault()` is for. ### What does not bubble Some events do not bubble, for example: - `blur` and `focus` - `mouseenter` and `mouseleave` They have bubbling counterparts: `focusin` and `focusout` instead of `focus` and `blur`, and `mouseover` and `mouseout` instead of `mouseenter` and `mouseleave`. You can check any particular event through the `event.bubbles` property. ### Summary table | Concept | Description | | --- | --- | | **Event bubbling** | The process by which an event rises from a nested element to its parents | | **event.target** | The element where the event happened | | **event.currentTarget** | The element whose handler is running right now | | **event.stopPropagation()** | Stops the bubbling | | **Used for** | Delegation, global handlers, logging | ### Common mistakes - **Confusing `target` with `currentTarget`.** In delegation you must check `e.target` (the real target), while `this` and `currentTarget` always point at the element that holds the listener. - **Checking only `e.target.tagName`.** If the `<li>` contains a `<span>` or an icon, that child becomes the target. `e.target.closest('li')` is the reliable version. - **Adding `stopPropagation()` just in case.** It breaks delegation and global listeners on `document`, and the bug surfaces far away from the call. - **Expecting `focus` or `mouseenter` to bubble.** They do not; for delegation use `focusin` or `mouseover`. - **Assuming bubbling is the only phase.** A listener with `{ capture: true }` fires before every bubbling handler, including the one on the target itself.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.