Skip to content
RungsySign in

The DOM & Events

The tree the browser actually renders, and how events travel through it.

40 minDifficulty 2/5browserAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.

Before this

Why this exists

HTML is text on disk. What the browser actually renders and what JavaScript actually manipulates is a live, in-memory tree of objects built from that text — the DOM. Change the DOM and the page updates instantly; the original HTML file never changes and was only ever the initial blueprint.

The mental model

Picture the DOM as a family tree of nested boxes, one object per HTML element, each holding references to its parent, its children, and its siblings. `document.querySelector` walks this tree to find a node; `node.appendChild` grafts a new branch onto it; the browser re-renders whatever part of the tree changed.

How it works

The DOM is not the HTML source

The browser parses HTML text once, at load, into a tree of DOM nodes — and from then on, JavaScript reads and writes that tree directly, not the original text. `document.documentElement.outerHTML` can show you HTML that never existed in the source file, because the DOM was mutated after the page loaded.

Events travel down, then back up

A click on a deeply nested button doesn't just fire on that button — it first travels DOWN from the document to the target (the capture phase), then back UP from the target to the document (the bubble phase). Most listeners run during bubbling, which is why a click handler on a parent `<div>` fires for clicks on any child inside it, unless bubbling is stopped.

Event delegation exploits bubbling on purpose

Instead of attaching a click listener to each of a hundred list items, attach one listener to their shared parent and inspect `event.target` inside it to find out which item was actually clicked. This works because the click bubbles up to the parent regardless of which child fired it, and it means new items added later work automatically with no new listener needed.

Reading layout properties forces a synchronous recalculation

Reading `element.offsetHeight` after changing styles forces the browser to immediately recompute layout to give you an accurate number — it can't lazily defer this the way it defers most rendering work. Reading many layout properties in a loop, interleaved with style changes, causes repeated forced recalculation and can visibly slow down a page.

The mechanism

The browser parses HTML once into the DOM tree. From then on, every read (`querySelector`) and write (`appendChild`, setting `textContent`) operates on that live tree, not the original text. The browser watches for DOM changes and schedules a re-render of whatever visually changed, batching updates where it can.

flowchart TD
  HTML[HTML text] -->|parsed once at load| DOM[DOM tree in memory]
  DOM -->|querySelector, getElementById| Node[A specific node]
  Node -->|appendChild, removeChild, textContent =| DOM
  DOM -->|style/layout recalculated| Render[Pixels on screen]
Diagram source for The DOM & Events.

What people get wrong

Viewing a page's source (Ctrl+U / View Source) shows you the current state of the page.
View Source shows the original HTML text as delivered by the server; the DOM inspector (Elements panel) shows the live, current tree, which JavaScript may have changed substantially since load. This causes real confusion when debugging a dynamically-rendered app — the source view can look nothing like what's actually on screen, because a framework rewrote the DOM after load.
Stopping event propagation stops the event from doing anything else at all.
`stopPropagation()` only stops the event travelling further up (or down) the DOM tree to other elements' listeners — it does not prevent the browser's default action for that element, which requires `preventDefault()` separately. Developers often call one when they meant the other, leading to bugs like a form still submitting despite a click handler that 'stopped' the event.
Attaching a listener to every list item individually is the normal way to handle clicks in a list.
Event delegation — one listener on the parent, checking `event.target` — handles the same interaction with far fewer listeners and automatically covers items added later. Per-item listeners don't scale to large or dynamic lists and require manual cleanup whenever items are removed, which delegation avoids entirely.

When not to use it

You're building a UI with a framework like React that manages its own virtual representation of the tree.
Let the framework own DOM mutations through its normal rendering cycle rather than reaching for `document.querySelector` directly — mixing manual DOM manipulation with a framework's own updates causes the two to disagree about what's actually there.
You need to read a layout value (like an element's height) many times during an animation loop.
Batch reads separately from writes, or cache the value once per frame, to avoid repeated forced synchronous layout recalculation.

Terms

DOM (Document Object Model)
The live, in-memory tree of node objects the browser builds from HTML, which JavaScript reads and writes to change what's rendered.
Event bubbling
The phase where an event travels from its target element upward through each ancestor, triggering any listeners registered along the way.
Event delegation
Attaching one listener to a shared ancestor instead of many listeners to individual children, relying on bubbling and `event.target` to identify the actual source.
Forced reflow
A synchronous, immediate layout recalculation triggered by reading certain layout properties right after a style change, rather than the browser's normal batched rendering.

In an interview

How would you efficiently handle click events for a list of 10,000 items that can grow dynamically?

  • event delegation: one listener on the parent container
  • check event.target (or closest) inside the handler to identify which item was clicked
  • avoids attaching and cleaning up thousands of individual listeners, and automatically covers items added later

Can you recall it?

What is event delegation, and why does it work given how events propagate through the DOM?

Keep track of this

Add The Browser Platform to your map and Rungsy will schedule reviews so you actually remember it.