JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeTopicsjavascriptEvents
PrevNext
javascript
beginner
10 min read

Events

custom-events
dom
event-bubbling
event-delegation
events
performance

DOM events flow through three phases — capture, target, and bubble — and event delegation leverages bubbling to handle events on dynamic child elements with a single parent listener.

Key Points

1Event Propagation

Events flow through three phases: capture (down from window), target (the element itself), and bubble (back up to window). Most events bubble by default.

2Event Delegation

Attach a single listener to a parent element and use event.target to identify the child that triggered it — works for dynamically added elements and reduces memory usage.

3addEventListener Options

Supports capture (phase selection), once (auto-remove), passive (performance optimization for scroll/touch), and signal (AbortController-based removal).

4Stopping Events

stopPropagation() prevents bubbling to ancestors; stopImmediatePropagation() also blocks other handlers on the same element. preventDefault() cancels the default action but doesn't stop propagation.

5Custom Events

Create with new CustomEvent('name', { detail, bubbles }) and dispatch with element.dispatchEvent() — supports the same propagation model as native events.

What You'll Learn

  • Understand the three phases of DOM event propagation
  • Know how event delegation works and when to use it
  • Use addEventListener options effectively including passive and signal

Deep Dive

The DOM event system is built on a three-phase propagation model. Understanding how events travel through the DOM tree is essential for writing efficient event handling code and is a staple of JavaScript interviews.

The Three Event Phases

When an event fires, it travels through the DOM in three phases:

  1. Capture phase: The event travels down from window through the DOM tree toward the target element. Handlers registered with { capture: true } fire during this phase.
  2. Target phase: The event reaches the element that triggered it. Both capture and bubble handlers on the target fire here.
  3. Bubble phase: The event travels back up from the target to window. This is the default phase — handlers registered without capture: true fire here.

Most events bubble, but some do not — focus, blur, scroll, and load do not bubble. Use focusin/focusout as bubbling alternatives to focus/blur.

Event Delegation

Instead of attaching listeners to every child element, attach one listener to a common parent and use event.target to identify which child triggered the event. This pattern has three advantages: fewer listeners means lower memory usage, it works automatically for elements added dynamically after the listener is attached, and it simplifies setup/teardown code. Use event.currentTarget to reference the element the listener is attached to (the parent), and event.target for the element that was actually clicked.

addEventListener Options

The third argument to addEventListener accepts an options object:

  • capture (boolean): Listen during capture phase instead of bubble.
  • once (boolean): Automatically remove the listener after it fires once.
  • passive (boolean): Promises the handler will not call preventDefault(). Critical for touchstart and wheel events — browsers can optimize scrolling performance when they know preventDefault() won't be called.
  • signal (AbortSignal): Remove the listener by calling controller.abort() — the modern alternative to keeping function references for removeEventListener.

stopPropagation vs stopImmediatePropagation

event.stopPropagation() prevents the event from continuing to the next phase (stops bubbling up or capturing down), but other handlers on the same element still fire. event.stopImmediatePropagation() stops propagation and also prevents any remaining handlers on the same element from executing.

preventDefault

event.preventDefault() cancels the browser's default action (form submission, link navigation, checkbox toggle) but does not stop propagation — the event still bubbles. Only works on cancelable events (check event.cancelable). Setting { passive: true } makes preventDefault() a no-op.

Custom Events

Create custom events with new CustomEvent('eventName', { detail: { key: 'value' } }) and dispatch them with element.dispatchEvent(event). Listeners access the payload via event.detail. Custom events support the same bubbling behavior as native events when bubbles: true is set.

Common Gotchas

Anonymous functions passed to addEventListener cannot be removed with removeEventListener because there is no reference to match. Use named functions, store references, or use AbortController with the signal option. When using object methods as event handlers, this refers to the element, not the object — use arrow functions or .bind() to preserve context.

Fun Fact

The addEventListener method wasn't available in Internet Explorer until IE9 (2011). Before that, IE used its own proprietary attachEvent method, which didn't support the capture phase and bound 'this' to window instead of the element. This incompatibility spawned an entire generation of libraries — jQuery's primary selling point was normalizing event handling across browsers.

Learn These First

DOM

beginner

Continue Learning

DOM

beginner

Debounce & Throttle

intermediate

Practice What You Learned

What is event delegation in JavaScript and why is it useful?
junior
events
Event delegation is a pattern where you attach a single event listener to a parent element instead of individual listeners on each child. It works because events bubble up through the DOM, and you can check event.target to determine which child was clicked.
Explain event bubbling, capturing, and how stopPropagation works.
mid
events
Events propagate through the DOM in three phases: capturing (top-down), target, and bubbling (bottom-up). stopPropagation() prevents the event from continuing to the next phase, while stopImmediatePropagation() also blocks remaining handlers on the current element.
Previous
Error Handling
Next
Functions
PrevNext