Browser & DOMEasy

Explain the DOM tree structure

01

The Short Answer

The DOM (Document Object Model) is a tree data structure that represents an HTML document. Every HTML element becomes a node in the tree, with the document object as the root. Parent-child relationships in the tree mirror the nesting of HTML tags. The browser parses HTML into this tree, and JavaScript uses it to read, modify, add, or remove elements on the page.

02

From HTML to Tree

When the browser receives an HTML file, it parses the markup character by character and builds the DOM tree. Each opening tag creates a node, nesting creates parent-child relationships, and text between tags becomes text nodes. The resulting tree is what JavaScript interacts with — not the HTML source text.

example.htmlhtml
<!DOCTYPE html>
<html>
  <head>
    <title>My Page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to <a href="/about">my site</a></p>
  </body>
</html>

This HTML produces a tree where document is the root, html is its child, head and body are children of html, and so on. The <a> tag is a child of <p>, which is a child of <body>. Text like "Hello" and "Welcome to" are text nodes — leaf nodes that can't have children.

  • document
    • html → head → title → "My Page" (text node)
    • html → body → h1 → "Hello" (text node)
    • html → body → p → a (nested element)
03

Node Types

Not everything in the DOM tree is an element. The tree contains different types of nodes, each with a numeric nodeType value. Understanding these types matters because methods like childNodes return all node types (including whitespace text nodes), while children returns only element nodes.

Node TypenodeType valueExampleDescription
Element1<div>, <p>, <a>HTML tags — the most common type
Text3"Hello world"Text content between tags (includes whitespace)
Comment8<!-- note -->HTML comments
Document9documentThe root node of the entire tree
DocumentFragment11createDocumentFragment()Lightweight container for batch operations
04

Navigating the Tree

Every node in the DOM tree has properties that let you traverse to related nodes — parent, children, and siblings. There are two sets of navigation properties: one that includes all node types (including text and comment nodes), and one that only includes element nodes. In practice, you almost always want the element-only versions.

tree-navigation.tstypescript
const paragraph = document.querySelector('p')!;

// Parent
paragraph.parentNode;        // Any node type
paragraph.parentElement;     // Element only (null if parent is document)

// Children
paragraph.childNodes;        // All nodes (includes text, comments, whitespace)
paragraph.children;          // Elements only (HTMLCollection)
paragraph.firstChild;        // First node (might be whitespace text)
paragraph.firstElementChild; // First element child
paragraph.lastElementChild;  // Last element child

// Siblings
paragraph.nextSibling;          // Next node (might be text)
paragraph.nextElementSibling;   // Next element
paragraph.previousElementSibling; // Previous element

// Common gotcha: childNodes includes whitespace text nodes
// <div>\n  <p>Hi</p>\n</div>
// div.childNodes.length === 3 (text, p, text)
// div.children.length === 1 (just p)

The whitespace gotcha trips up many developers. Newlines and spaces between tags create text nodes, so childNodes often has more items than you expect. Use children, firstElementChild, and nextElementSibling to skip text nodes.

05

The Tree Is Live

The DOM tree is a live representation — when you modify it with JavaScript, the page updates immediately (after the browser's next render). And some collections returned by DOM methods are live too, meaning they automatically reflect changes to the document without you needing to re-query.

live-collections.tstypescript
// HTMLCollection (from getElementsByClassName) is LIVE
const items = document.getElementsByClassName('item');
console.log(items.length); // 3

document.querySelector('.item')!.classList.remove('item');
console.log(items.length); // 2 — automatically updated!

// NodeList (from querySelectorAll) is STATIC
const staticItems = document.querySelectorAll('.item');
console.log(staticItems.length); // 2

document.querySelector('.item')!.classList.remove('item');
console.log(staticItems.length); // 2 — still 2, it's a snapshot

Live vs static collections

getElementsByClassName and getElementsByTagName return live HTMLCollections. querySelectorAll returns a static NodeList (a snapshot). Live collections can cause bugs in loops if you're modifying the DOM while iterating.

06

DOM vs HTML vs Render Tree

It's important to distinguish the DOM tree from the HTML source and the render tree. The HTML is just text — the parser converts it into the DOM tree. The render tree is a separate structure the browser builds for painting — it excludes invisible elements (like display: none) and includes pseudo-elements that aren't in the DOM.

ConceptWhat it isIncludes hidden elements?Includes pseudo-elements?
HTML sourceText markup — input to the parserYesNo (they're CSS)
DOM treeIn-memory tree of nodes — what JS interacts withYes (display:none is in DOM)No
Render treeLayout tree for painting — what the user seesNo (skips display:none)Yes (::before, ::after)
07

Why Interviewers Ask This

This question tests foundational web knowledge. Interviewers want to see that you understand the DOM is a tree (not a flat list or the HTML itself), know the different node types and how to navigate between them, understand the difference between live and static collections, and can distinguish the DOM from the render tree. It's the foundation for understanding how frameworks like React work (virtual DOM diffing) and why DOM manipulation has performance implications.

Quick Revision Cheat Sheet

Data structure: Tree — nodes with parent/child/sibling relationships

Root node: document (nodeType 9)

Common node types: Element (1), Text (3), Comment (8), Document (9)

Element-only navigation: children, firstElementChild, nextElementSibling

All-node navigation: childNodes, firstChild, nextSibling (includes text/comments)

Live vs static: getElementsBy* → live | querySelectorAll → static snapshot