Skip to content
JavaScript js dom 5 min read

The DOM Explained

When a browser loads an HTML page, it doesn’t keep your markup as a plain string of text. Instead it parses that markup into an in-memory data structure called the Document Object Model, or DOM. The DOM is a tree of objects that represents every part of the page, and it is the bridge that lets JavaScript read, change, add, and remove content while the page is running. Understanding this model is the foundation for everything else in DOM manipulation.

From HTML text to a tree of nodes

The browser reads your HTML top to bottom and builds a tree where each tag becomes a node. Nested tags become child nodes of the tag that contains them, so the structure of your markup is mirrored exactly by the structure of the tree.

Consider this small document:

<!DOCTYPE html>
<html>
  <head>
    <title>Hi</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>World</p>
  </body>
</html>

The browser turns it into a tree that looks like this:

document
└── html
    ├── head
    │   └── title
    │       └── "Hi"          (text node)
    └── body
        ├── h1
        │   └── "Hello"       (text node)
        └── p
            └── "World"       (text node)

Each box is a node. The lines show parent/child relationships: html is the parent of head and body, body is the parent of h1 and p, and the visible words are themselves separate text nodes living inside their elements.

The document object

At the very top of the tree sits the global document object. It is the entry point for all DOM work — every query, every new element, and every change starts from document. It is available automatically in any script that runs in a browser page.

console.log(document.title);           // the contents of <title>
console.log(document.documentElement); // the <html> element
console.log(document.body);            // the <body> element
console.log(document.nodeType);        // 9 (Node.DOCUMENT_NODE)

Output:

Hi
<html>…</html>
<body>…</body>
9

Node vs element vs text node

People often use “node” and “element” interchangeably, but they are not the same thing. Node is the general base type for everything in the tree. Element is a specific kind of node that corresponds to an HTML tag (like <p> or <div>). A text node holds the actual characters between tags. Comments are nodes too.

TermWhat it isnodeTypeExample
DocumentThe root of the whole tree9document
ElementA node created from a tag1<p>, <div>
Text nodeThe characters inside an element3"World"
CommentAn HTML comment node8<!-- note -->

This distinction matters when you walk the tree. The phrase “Hello” inside <h1>Hello</h1> is not the h1 element — it is a separate text node that is a child of the h1 element.

const heading = document.querySelector('h1');
console.log(heading.nodeType);        // 1  (an element)
console.log(heading.firstChild.nodeType); // 3  (its text node)
console.log(heading.firstChild.nodeValue); // "Hello"

Because whitespace and line breaks in your HTML also become text nodes, properties like firstChild can return an empty text node. When you only care about elements, prefer the element-aware navigation properties such as firstElementChild and children.

The DOM is live

The most important idea about the DOM is that it is live. It is not a one-time snapshot of your HTML — it is the running representation of the page. When JavaScript changes a node, the browser re-renders and the user sees the change immediately. Equally, when the user types into an input or a script adds an element, the tree updates to match.

This live, two-way relationship is what makes interactive pages possible. The example below changes the tree in response to a click, and you can watch the page update in real time:

<!DOCTYPE html>
<html>
  <body>
    <h1 id="title">The DOM is live</h1>
    <p id="count">Clicks: 0</p>
    <button id="btn">Add a paragraph</button>
    <div id="output"></div>

    <script>
      let clicks = 0;
      const btn = document.getElementById('btn');
      const count = document.getElementById('count');
      const output = document.getElementById('output');

      btn.addEventListener('click', () => {
        clicks += 1;
        count.textContent = `Clicks: ${clicks}`;

        const p = document.createElement('p');
        p.textContent = `New node #${clicks} added to the tree`;
        output.appendChild(p);
      });
    </script>
  </body>
</html>

Notice that nothing reloaded. The text node inside <p id="count"> was replaced and brand-new <p> elements were grafted onto the tree, and the browser reflected both changes instantly.

Inspecting the tree yourself

You don’t need a special tool to see the structure — every node exposes properties that describe its place in the tree. This demo reads the live document and prints a small outline of the elements it finds:

<!DOCTYPE html>
<html>
  <body>
    <main>
      <h2>Recipe</h2>
      <ul>
        <li>Flour</li>
        <li>Sugar</li>
      </ul>
    </main>
    <pre id="log"></pre>

    <script>
      const lines = [];

      function walk(node, depth) {
        if (node.nodeType === Node.ELEMENT_NODE) {
          lines.push(`${'  '.repeat(depth)}<${node.tagName.toLowerCase()}>`);
        }
        for (const child of node.childNodes) {
          walk(child, depth + 1);
        }
      }

      walk(document.querySelector('main'), 0);
      document.getElementById('log').textContent = lines.join('\n');
    </script>
  </body>
</html>

The same document object and the same node properties are what every DOM API is built on top of, whether you are selecting elements, changing content, or handling events.

Best Practices

  • Think in terms of the tree: locate the right node first, then read or change it — most DOM bugs come from targeting the wrong node.
  • Remember the difference between an element and its text node; use textContent to read or set the text rather than poking at child text nodes directly.
  • Prefer element-aware properties (children, firstElementChild, nextElementSibling) so stray whitespace text nodes don’t trip you up.
  • Run DOM code after the document has parsed — place scripts at the end of <body>, use defer, or wait for the DOMContentLoaded event.
  • Treat the DOM as live: a single change re-renders the page, so batch related updates to keep things smooth.
  • Use your browser’s DevTools “Elements” panel to inspect the real tree while you learn — it shows the same nodes your JavaScript sees.
Last updated June 1, 2026
Was this helpful?