Conceptly
← All Concepts
🌳

DOM

ClientAn interface that represents an HTML document as a programmable object tree

The DOM (Document Object Model) is the tree-structured object model that the browser creates by parsing an HTML document. Through the DOM API, JavaScript can query, add, modify, and delete elements of the document, and those changes are immediately reflected on screen. It is the foundation that transforms a web page from a static document into a dynamic application.

β–ΆArchitecture Diagram

πŸ” Structure

Dashed line animations indicate the flow direction of data or requests

Why do you need it?

HTML is static text, assembled on the server and delivered to the browser. When a user clicks a button, new data arrives from the server, or a form needs to show immediate validation feedback, there is no way to directly edit the HTML text. Fetching the entire page from the server again is one option, but if the whole page refreshes every time a comment is added, usability collapses. For JavaScript to change only a specific part of the screen, the structure of the document must be held in a form that a programming language can understand and manipulate. The DOM is the interface that turns HTML text into an object tree, allowing a program to access and modify any part of the document.

Why did this approach emerge?

In the mid-1990s early web, a page was simply a matter of receiving completed HTML from the server and displaying it as-is. User interaction amounted to clicking a link to navigate to the next page. When Netscape introduced JavaScript, form validation and simple visual effects became possible, but each browser accessed the document differently. Netscape's document.layers and IE's document.all were mutually incompatible, meaning the same functionality had to be written twice. The W3C finalized DOM Level 1 as a standard in 1998 to solve this compatibility problem. Once all browsers could access the same tree structure through the same API, genuinely dynamic web pages became possible with JavaScript. The subsequent wave of AJAX, jQuery simplifying DOM manipulation, and then React and Vue abstracting DOM updates all unfolded on top of this standardized DOM.

How does it work inside?

When the browser receives HTML, it begins with tokenization -- identifying pieces such as <div>, <p>, and text nodes -- and then connects these tokens into a parent-child tree. This is the DOM tree. The document object is the root, with html, head, body, and individual elements hanging from it as nodes. JavaScript manipulates this tree through the DOM API. document.querySelector('.item') finds a node, element.textContent = 'new content' changes text, and parentNode.appendChild(newNode) inserts a new element. Events also flow through the DOM tree. When a user clicks a button, the event travels from the root down to the target (capturing), is handled at the target, then travels back up to the root (bubbling). When the DOM changes, the browser recalculates the layout of the affected area (reflow) and redraws the screen (repaint). If this happens frequently it affects performance. Adding 100 elements one by one could trigger 100 reflows, but collecting them in a DocumentFragment and adding them all at once reduces reflows. This is also why frameworks like React use a Virtual DOM -- to minimize the number of actual DOM changes.

In Code

Using a ref to point at a DOM node and call focus

import { useEffect, useRef } from "react";

export function SearchBox({ autoFocus }: { autoFocus: boolean }) {
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    if (autoFocus) {
      inputRef.current?.focus();
    }
  }, [autoFocus]);

  return <input ref={inputRef} />;
}

A ref gives you a direct handle to the DOM node, which is what makes imperative actions like `focus` possible.

Find a node with a selector, then change its class and text

function highlightActiveRow() {
  const row = document.querySelector<HTMLElement>("[data-active='true']");
  row?.classList.add("is-highlighted");
  row && (row.textContent = "Active item");
}

Read this as a DOM tree operation: locate an element, then mutate the node itself.

What is it often confused with?

The DOM and the Virtual DOM are easy to confuse, but they operate at different levels. The DOM is the real document tree managed by the browser; every change immediately goes through the rendering pipeline. The Virtual DOM is a lightweight JavaScript object tree that a framework such as React maintains in memory. When state changes, the framework creates a new Virtual DOM, compares it against the previous one (diffing), and reflects only the differences in the actual DOM. Having a Virtual DOM does not make the DOM unnecessary. The Virtual DOM is a strategy for batching DOM updates efficiently, not a replacement for the DOM. For changes to ultimately appear on screen, the actual DOM must change. The DOM and HTML should also be distinguished. HTML is the text that describes the document's initial state; the DOM is the live object that the browser creates by interpreting that text. Modifying the DOM with JavaScript does not change the original HTML file. What you see in the developer tools is not the HTML -- it is the current state of the DOM.

When should you use it?

DOM manipulation is the foundational operation of all frontend development. Whether using a framework or vanilla JavaScript, producing a change on screen is ultimately the act of changing the DOM. Even when React's JSX describes components declaratively, the result is the creation and updating of actual DOM nodes. When performance problems arise, they can usually be traced back to DOM manipulation patterns. Adding 1,000 list items to the DOM all at once will cause rendering to stall, and repeatedly reading and writing element dimensions during scroll causes layout thrashing. The responses in these cases are virtual scrolling -- keeping only visible items in the DOM -- or separating reads and writes into distinct batches. DOM understanding is equally central to debugging. The Elements tab in the developer tools shows the live state of the current DOM, and nodes can be directly edited to immediately verify the effect of changes. When an event is not behaving as expected, checking the Event Listeners tab to see which handlers are attached to which nodes is the starting point for debugging.

Dynamic UI rendering -- changing on-screen content in real time based on user input or server responsesEvent handling -- reacting to user actions such as clicks, scrolls, and keyboard inputForm validation -- checking input values in real time and displaying feedback before sending to the serverSPA routing -- switching screens by replacing only part of the DOM rather than reloading the entire page