Articles

What Is the DOM? The Document Object Model, Explained

The DOM is the live in-memory tree browsers build from your HTML, which JavaScript reads and manipulates. Learn how it works and why it matters.

Takina Takina · · 4 min read
A stylized browser window and layout

The DOM — Document Object Model — is the live, in-memory representation of a web page that the browser builds after it parses your HTML. It is not the HTML file itself. It is a tree of objects that the browser keeps in memory, and it is what JavaScript reads and changes when a page updates without reloading. Every button click that shows a dropdown, every form that validates before submitting, every single-page app that swaps content without a full navigation — all of that goes through the DOM.

Understanding the DOM is the bridge between knowing HTML structure and knowing how JavaScript makes pages interactive.

How the browser builds the DOM

When a browser receives an HTML document, it parses it from top to bottom and constructs a tree. Each HTML element becomes a node in that tree. The tree reflects the nesting structure of the markup:

document
└── html
    ├── head
    │   └── title ("My Page")
    └── body
        ├── h1 ("Hello")
        └── p ("This is a paragraph.")

The document object at the root is always present — it represents the entire page. Every element is an Element node, text content inside elements is a Text node, and HTML attributes are accessible as properties on their element nodes.

The browser does not wait for the full document before starting to build the DOM. It parses and constructs incrementally, which is why placing <script> tags at the bottom of <body> (or using the defer attribute) matters: a script that runs too early may try to access nodes that haven’t been parsed yet.

Key DOM APIs

JavaScript interacts with the DOM through a set of built-in APIs. Here are the ones you will reach for most often:

// Find a single element by CSS selector
const heading = document.querySelector('h1');

// Find all matching elements (returns a NodeList)
const cards = document.querySelectorAll('.card');

// Create a new element and add it to the page
const newParagraph = document.createElement('p');
newParagraph.textContent = 'Added dynamically.';
document.body.appendChild(newParagraph);

// Read or change an attribute
const link = document.querySelector('a');
console.log(link.getAttribute('href'));
link.setAttribute('href', 'https://lycoristechnologies.com');

// React to user interaction
const button = document.querySelector('#submit-btn');
button.addEventListener('click', (event) => {
  console.log('Button clicked!', event.target);
});

querySelector and querySelectorAll are the modern way to find elements — they accept any valid CSS selector, so the same selector syntax you use in stylesheets works here too. addEventListener wires up event handlers: click, keydown, input, scroll, and dozens more.

Reflow, repaint, and why excessive DOM work is slow

The DOM is live: changing it triggers visible updates on screen. But those updates are not free. The browser pipeline works roughly like this:

  1. Style — compute which CSS rules apply to each node.
  2. Layout (reflow) — calculate the size and position of every element.
  3. Paint (repaint) — fill in pixels.
  4. Composite — combine layers and display.

When JavaScript changes the DOM — adding elements, changing styles, modifying text — the browser may need to redo some or all of these steps. A change that forces a layout recalculation is called a reflow, and it is expensive because the browser has to recalculate the geometry of potentially many elements.

Worse, reading certain layout properties (like element.offsetWidth) immediately after writing to the DOM forces the browser to flush its pending layout queue right then, creating what is called layout thrashing. The fix is to batch reads and writes: do all your DOM mutations first, then read layout values, not interleaved.

Why frameworks abstract the DOM

Manipulating the DOM directly works fine for small interactions, but as an application grows, manually tracking which elements need updating becomes error-prone and hard to reason about.

This is why libraries like React introduced the concept of the virtual DOM: a lightweight in-memory copy of the DOM tree that React updates first, then diffs against the real DOM, and finally applies only the minimal set of real changes needed. This makes writing UI declaratively (describe what the page should look like given some state, not how to change it step by step) practical and performant.

Newer frameworks have moved further still. Instead of diffing a virtual tree, they use signals or fine-grained reactivity to track exactly which data each DOM node depends on, and update only those nodes when the data changes — with no diffing overhead at all. Solid.js and Svelte are prominent examples of this approach. You can see how this pattern extends to server rendering in React Server Components Explained.

The DOM in practice

You do not need to understand every edge case of the DOM before using it, but knowing what it actually is — a live tree in the browser’s memory, separate from your HTML source file, manipulated through a JavaScript API — makes debugging much less mysterious. When an element isn’t updating, the question becomes: did JavaScript find the right node? Did the event fire? Did a reflow block things? The DOM is the arena where those answers live.

Takina Takina · · 5 min read

Promise.all() vs allSettled() vs race() Compared

Promise.all() fails fast, allSettled() waits for every result, and race() returns whichever promise finishes first — how to choose correctly.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

JavaScript Spread vs Rest Operators, Explained

The spread operator (...) expands an iterable into individual elements; the rest operator collects elements back into an array. Same syntax, opposite jobs.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

ResizeObserver API Explained: Watching Element Size

The ResizeObserver API lets JavaScript watch an element's box size and react without polling or resize-event hacks. How it works and when to use it.

#Web Development #JavaScript #Frontend