# Events, listeners and propagation

*JavaScript Developer Study Guide · Browser and Events · Last updated October 16, 2026 · https://www.certifyforce.com/study-guide/javascript-developer/browser-and-events/events-listeners-and-propagation*

How addEventListener registers, deduplicates and removes listeners, how an event travels through the capture, target and bubble phases, and how to choose between stopPropagation, stopImmediatePropagation and preventDefault, delegate events to a parent, and dispatch custom events with detail and bubbles.

- Certification: Salesforce JavaScript Developer
- Exam section: Browser and Events
- Study guide: https://www.certifyforce.com/study-guide/javascript-developer

**Official exam objective:**

> Given a business requirement, utilize Events, event handlers, and propagation.

## What the exam expects

- Register, deduplicate and remove listeners with `addEventListener`, its options (`once`, `capture`, `passive`) and `removeEventListener`, and predict what an `onclick` property does alongside them.
- Trace the order in which capture, target and bubble listeners run for one click.
- Pick `stopPropagation`, `stopImmediatePropagation` or `preventDefault` from the requirement.
- Implement event delegation with `event.target`, `event.currentTarget` and `closest()`, knowing which events bubble.
- Dispatch custom events with `detail` and `bubbles` and predict when their listeners run.
- Key terms: **listener** (a function registered for an event type on a target); **propagation** (the path an event takes through the target's ancestors); **capture phase** (window down to the target's parent); **bubble phase** (the target's parent back up to window); **default action** (what the browser does on its own, such as submitting a form); **delegation** (one listener on a parent handling events from its descendants).

## Key facts

| Fact | Detail | Source |
|---|---|---|
| Same listener added twice | Same function reference, type and capture flag: added once | [addEventListener()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) |
| `onclick` assigned twice | The second assignment overwrites the first | [Introduction to events](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Events) |
| Default phase | Bubble; capture needs `{ capture: true }` or `true` | [Event bubbling](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling) |
| `once: true` | Listener is removed automatically after its first call | [addEventListener()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) |
| `passive: true` | A `preventDefault()` call inside the listener has no effect | [addEventListener()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) |
| `removeEventListener` match | Type, same function reference and capture flag; otherwise no effect | [removeEventListener()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) |
| `stopPropagation()` | Stops ancestors; same-element listeners and default action still happen | [stopPropagation()](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation) |
| `stopImmediatePropagation()` | No remaining listener runs on this or any other element | [stopImmediatePropagation()](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation) |
| `target` vs `currentTarget` | Deepest element fired on vs element whose listener is running | [Event bubbling](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling) |
| Non-bubbling events | `focus` and `mouseenter`; use `focusin` and `mouseover` to delegate | [focus event](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event), [mouseenter event](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event) |
| `Event`/`CustomEvent` defaults | `bubbles: false`, `cancelable: false`, `detail: null` | [Event()](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event), [CustomEvent()](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) |
| `dispatchEvent()` timing | Listeners run synchronously before it returns | [dispatchEvent()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) |

## Check yourself

- Why does a delegated listener on a list still handle items appended after it was registered?
- When does `stopPropagation()` still let another listener run on the same element?
- Why does a character counter wired to `change` on a `textarea` update only after the user clicks away?
- Which two ways make a listener run only on the first click?
- What does `event.detail` hold in a listener for `new CustomEvent('saved')` dispatched without options?

## Sources

- [Event bubbling](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling)
- [Introduction to events](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Events)
- [EventTarget: addEventListener() method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
- [EventTarget: removeEventListener() method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
- [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event)
- [DOM events](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Events)
- [Event: eventPhase property](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase)
- [Event: stopPropagation() method](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)
- [Event: stopImmediatePropagation() method](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation)
- [Event: preventDefault() method](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
- [CustomEvent: CustomEvent() constructor](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent)
- [Event: Event() constructor](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
- [EventTarget: dispatchEvent() method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent)
- [Element: focus event](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event)
- [Element: mouseenter event](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event)
- [Element: input event](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event)
- [HTMLElement: change event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)
- [Event: currentTarget property](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget)
- [<button>: The Button element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button)
- [Function.prototype.bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)

## About the full unit

The "Core concepts", "Exam traps" and "Worked scenario" sections of this unit, its 14 linked practice questions, and unlimited timed mock exams are included with the JavaScript Developer pack (USD $19): https://www.certifyforce.com/practice-exams
