DOM: Document Object Model

The DOM is a tree representation of a web page created by the browser.

DOM: Document Object Model

The DOM is the backbone of every interactive web page. It is how the browser represents your HTML as a structured object that JavaScript can read and modify in real time, making dynamic web applications possible without requiring a full page reload on every interaction.

What Is the DOM

When a browser loads an HTML page, it parses the markup from top to bottom and builds the Document Object Model (DOM), a tree-like representation of every element, attribute, and piece of text in the document. Each part of the HTML becomes a node in this tree, and every node has a defined relationship to the nodes around it through parent, child, and sibling connections.

The DOM is not the same as your HTML source code. It is a live, in-memory representation that the browser maintains separately. When JavaScript modifies the DOM, the page updates on screen immediately without a reload. When you use browser developer tools and inspect an element, you are looking at the current state of the DOM, which may differ significantly from the original HTML if JavaScript has made modifications since the page loaded.

The DOM is also not specific to HTML. XML documents have their own DOM representation, and the same underlying specification covers both. In web development, however, the term DOM almost always refers to the HTML document object model that browsers expose to JavaScript through the document global object.

DOM Tree Structure

The DOM is organised as a hierarchical tree with the document node at the root. Every element in your HTML becomes a node in this tree, nested according to how elements are nested in the markup. Text content, attributes, and comments also become their own nodes within the tree.

Document
 └── html
      ├── head
      │    └── title ("My Page")
      └── body
           ├── h1 ("Welcome")
           └── p ("Hello, world.")

In this tree, the html element is the child of the document root. The head and body elements are children of html and siblings of each other. The h1 and p elements are children of body. The text content inside each element is itself a separate text node that is a child of its containing element.

Types of DOM Nodes

Node TypeWhat It RepresentsExample
DocumentThe root of the entire HTML document treedocument
ElementAn HTML tag and its attributes<div>, <p>, <img>
TextThe text content inside an elementThe string "Hello, world." inside a <p> tag
AttributeA named attribute on an elementclass="hero", href="/"
CommentAn HTML comment node<!-- this is a comment -->
DocumentFragmentA lightweight container for building subtrees off the live DOMUsed for batching DOM insertions before adding to the page

Common DOM Operations in JavaScript

JavaScript interacts with the DOM through a set of methods and properties available on the document object and on individual element nodes. These operations cover selecting elements, reading and writing content, modifying styles and classes, creating new elements, and removing existing ones.

// Select elements
document.getElementById('myId')
document.querySelector('.my-class')        // first match
document.querySelectorAll('p')             // all matches (NodeList)

// Read and write content
element.textContent = 'New text'           // plain text, safer
element.innerHTML = '<strong>Bold</strong>' // parses HTML

// Change styles and classes
element.style.color = 'red'
element.classList.add('active')
element.classList.remove('hidden')
element.classList.toggle('open')

// Read element dimensions (triggers reflow)
const width = element.offsetWidth
const rect = element.getBoundingClientRect()

// Create and insert new elements
const btn = document.createElement('button')
btn.textContent = 'Click me'
document.body.appendChild(btn)

// Insert relative to an existing element
parent.insertBefore(newEl, referenceEl)
referenceEl.insertAdjacentElement('afterend', newEl)

// Remove elements
element.remove()
parent.removeChild(element)

Traversing the DOM

Once you have a reference to a DOM node, you can navigate to related nodes using traversal properties. This is useful when you need to work with an element's parent, siblings, or children without making an additional selector query.

element.parentElement          // the parent element
element.children               // live HTMLCollection of child elements
element.firstElementChild      // first child element
element.lastElementChild       // last child element
element.nextElementSibling     // next sibling element
element.previousElementSibling // previous sibling element
element.closest('.container')  // nearest ancestor matching a selector

The DOM and Performance

DOM operations are significantly more expensive than regular JavaScript operations. The browser must maintain consistency between the DOM, the CSSOM, and what is displayed on screen. Certain operations force the browser to pause and recalculate layout immediately, which can cause performance problems if triggered repeatedly.

Reading layout properties such as element.offsetWidth, element.scrollTop, or getBoundingClientRect() after making style changes forces the browser to perform a synchronous reflow, meaning it must finish recalculating layout before it can return a value. Doing this inside a loop that also writes to the DOM creates a pattern called layout thrashing, where the browser reflows on every iteration.

  • Batch DOM updates: Make multiple changes at once rather than modifying one element at a time, so the browser can process them in a single pass.
  • Use DocumentFragment for bulk insertions: Build a subtree of new elements in a DocumentFragment before inserting it into the live DOM. This triggers only one reflow instead of one per inserted element.
  • Cache selector results: Avoid calling querySelector or getElementById inside loops. Store the reference in a variable and reuse it.
  • Separate reads from writes: Read all layout properties you need first, then make all your DOM changes. Alternating reads and writes forces repeated reflows.
  • Use textContent instead of innerHTML for plain text: textContent is faster because it does not parse HTML, and it eliminates the risk of XSS vulnerabilities from untrusted input.
  • Use CSS classes rather than inline styles: Toggling a class with classList is cleaner and allows the browser to batch style updates more efficiently than setting individual style properties one at a time.

DOM Events

The DOM is also the system through which user interactions are handled. Events such as clicks, keyboard input, mouse movement, and form submissions are dispatched on DOM nodes, and JavaScript can listen for and respond to them using event listeners.

// Add an event listener
button.addEventListener('click', function(event) {
  console.log('Button clicked', event.target)
})

// Remove an event listener
button.removeEventListener('click', handler)

// Event delegation: listen on a parent for events from children
document.querySelector('ul').addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log('List item clicked:', event.target.textContent)
  }
})

Event delegation is a common pattern where you attach a single listener to a parent element rather than attaching individual listeners to each child. When a child element is clicked, the event bubbles up through the DOM tree to the parent, which handles it. This is more efficient for large lists or dynamically added elements and reduces memory usage from many individual listeners.

Frequently Asked Questions

  1. Is the DOM the same as HTML?
    No. HTML is the markup you write in your source file. The DOM is the live object tree the browser builds from that HTML when the page loads. They start out equivalent but diverge as soon as JavaScript makes any modifications. If you right-click and view page source, you see the original HTML. If you inspect an element in developer tools, you see the current state of the DOM, which reflects all changes JavaScript has made since the page loaded.
  2. What is the virtual DOM used by React?
    React maintains a lightweight in-memory copy of the DOM structure called the virtual DOM. When a component's state or props change, React re-renders the component into the virtual DOM first and then compares the new virtual DOM tree with the previous one through a process called diffing. Only the differences, meaning the minimum set of actual DOM changes needed, are then applied to the real DOM. This batching and diffing approach avoids unnecessary reflows and repaints, making frequent updates more efficient.
  3. Can CSS access the DOM directly?
    CSS cannot read or modify the DOM the way JavaScript can. However, the browser uses both the DOM and the CSSOM together to calculate which styles apply to which elements and to build the render tree. CSS selectors target elements in the DOM by their tag name, class, ID, attributes, and position in the tree, so there is an indirect relationship, but CSS has no programmatic access to DOM properties or values.
  4. What is the difference between innerHTML and textContent?
    innerHTML reads or writes the HTML markup inside an element, including tags. The browser parses any HTML you assign to it and creates corresponding DOM nodes. textContent reads or writes only the plain text content, stripping out all HTML tags. When inserting content that comes from user input or untrusted sources, always use textContent to prevent cross-site scripting (XSS) attacks, since innerHTML will execute any script tags or event handlers embedded in the string.
  5. What is the difference between childNodes and children?
    childNodes returns a NodeList of all child nodes including element nodes, text nodes, and comment nodes. children returns an HTMLCollection of only the child element nodes, excluding text and comment nodes. In most practical cases children is what you want because whitespace between tags creates text nodes that appear in childNodes and can cause unexpected results when iterating.

Conclusion

The DOM is what makes web pages interactive. It is the structured, live representation of your HTML that JavaScript can read and manipulate to update pages, respond to user input, and build dynamic interfaces without a full reload. Every modern web framework including React, Vue, and Angular ultimately interacts with the DOM to produce what users see on screen, whether directly or through an abstraction layer like the virtual DOM. Understanding how the DOM works, how to traverse and modify it efficiently, and how DOM operations affect browser performance gives you a foundation that applies across all of these frameworks and tools. Continue with CSSOM and browser rendering to see how the DOM and styles combine to produce the final display.