Chapter 8.1☕ 20 min read

DOM Architecture: Nodes, Rendering Pipeline & Reflow

JS akela kya karega? Screen pe dikhane ke liye DOM chahiye. Pipeline samjho — Reflow = mahanga, Composite = sasta.

01DOM ka Tree — Document Object Model Asli Hai Kya?

The DOM is a tree-structured representation of your HTML. The browser parses your HTML, builds a DOM tree, and JavaScript reads or modifies it — then the browser re-renders the screen.

Every HTML tag becomes an Element node, every piece of text becomes a Text node, even comments become Comment nodes.

Node types: Document (9), Element (1), Text (3), Comment (8), DocumentFragment (11), DocumentType (10).

Node vs Element: Node is generic — it includes text, comments, everything. Element is a specific Node with a tag name, attributes, and children.

Text nodes are real nodes — whitespace between tags creates text nodes!

// The DOM tree for this HTML:
// <p>Hello <strong>world</strong></p>
//
// <p> (Element node)
//   ├── "Hello " (Text node — note the space!)
//   ├── <strong> (Element node)
//   │     └── "world" (Text node)
//   └── "" (Text node — possible trailing whitespace)

// Check node types
const p = document.querySelector("p");
p.childNodes.forEach(node => {
  console.log(node.nodeType); // 3 for text, 1 for element
  console.log(node.nodeName); // "#text" or "STRONG"
});
DOM nodes are C++ objects in the browser's memory. Each node has ~100+ bytes of overhead: vtable pointer, parent/child/sibling pointers, node flags, etc. A page with 10,000 DOM nodes uses 1-2MB just for node objects — before any content.
02Node vs Element — Uncle aur Bhai Mein Farq

getElementsByClassName returns a LIVE HTMLCollection — it auto-updates when the DOM changes. querySelectorAll returns a STATIC NodeList — it's a snapshot that doesn't update.

Live collection danger: iterating and removing elements skips every other element because the collection shrinks!

NodeList from querySelectorAll has .forEach(). HTMLCollection does NOT have .forEach().

Always convert live collections to arrays before modifying: [...document.getElementsByClassName("item")]

// ❌ DANGER: Iterating live collection while modifying
const items = document.getElementsByClassName("item");
// Suppose there are 5 elements: [0,1,2,3,4]
items[0].remove(); // Collection is now [1,2,3,4] (shifted!)
// What was items[1] is now items[0]
// Your loop index moves to 1, but items[1] is now the ORIGINAL items[2]
// Result: you SKIP the original items[1]!

// ✅ FIX 1: Iterate backwards
for (let i = items.length - 1; i >= 0; i--) {
  items[i].remove(); // Safe! Removing from end doesn't shift
}

// ✅ FIX 2: Convert to static array first
const staticItems = [...document.getElementsByClassName("item")];
staticItems.forEach(item => item.remove()); // Safe!

// ✅ FIX 3: Use querySelectorAll (always static)
document.querySelectorAll(".item").forEach(item => item.remove());
Rule of thumb: If you're going to modify the DOM while iterating, always use querySelectorAll or convert to an array first. Live collections will bite you.
03Rendering Pipeline — DOM → CSSOM → Render Tree → Paint

DocumentFragment is a lightweight container — NOT part of the live DOM tree. Append children to the fragment, then append the fragment to the DOM — single reflow instead of N reflows.

When you append a fragment to the DOM, the fragment's children are moved — the fragment itself is NOT inserted.

This is the MOST important performance pattern for bulk DOM operations.

DocumentFragment is also used as a "build zone" — construct complex UI offline, then attach once.

// ❌ SLOW: 100 individual appends = up to 100 reflows
const list = document.getElementById("list");
for (let i = 0; i < 100; i++) {
  const li = document.createElement("li");
  li.textContent = "Item " + i;
  list.appendChild(li); // Each append MAY trigger reflow!
}

// ✅ FAST: Build in fragment, single append = 1 reflow
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const li = document.createElement("li");
  li.textContent = "Item " + i;
  fragment.appendChild(li); // No reflow — fragment isn't in DOM!
}
list.appendChild(fragment); // ONE reflow for 100 elements!

// Modern alternative: <template> element
const template = document.createElement("template");
template.innerHTML = "<li>Item 1</li><li>Item 2</li>";
list.appendChild(template.content); // template.content IS a DocumentFragment!
DocumentFragment.appendChild does NOT trigger reflow because the fragment isn't in the live DOM. Children are added to the fragment's internal C++ tree only. When you finally append the fragment to the DOM, the browser inserts all children in ONE operation — a single reflow.
04Reflow vs Repaint — Performance Ka Game

When JS modifies the DOM, the browser runs a 4-step pipeline to update the screen.

Step 1 — Style Calculation: Figure out which CSS rules apply to each element.

Step 2 — Layout (Reflow): Calculate geometry — position, size, margins for every affected element.

Step 3 — Paint: Fill pixels — colors, text, images, borders, shadows.

Step 4 — Composite: Combine layers — stack painted layers in correct order, apply transforms.

The pipeline is not always 4 steps — some changes skip steps.

Reading layout properties (offsetWidth, getBoundingClientRect) FORCES the browser to complete layout immediately (forced reflow).

// ❌ FORCED REFLOW: Reading layout property after DOM change
const box = document.getElementById("box");
box.style.width = "100px"; // Marks layout as dirty
console.log(box.offsetWidth); // FORCES layout calculation NOW!
box.style.height = "200px"; // Marks layout as dirty AGAIN
console.log(box.offsetHeight); // FORCES layout AGAIN!
// Two layout calculations instead of one. Expensive!

// ✅ BATCH READS, THEN BATCH WRITES
const box2 = document.getElementById("box2");
const width = box2.offsetWidth;  // Read 1
const height = box2.offsetHeight; // Read 2
box2.style.width = width + 10 + "px";   // Write 1
box2.style.height = height + 20 + "px"; // Write 2
// Only ONE layout calculation. Fast!
05Virtual DOM — React Ne Kya Invent Kiya?

Reflow (Layout) = MOST expensive. Triggers: changing width, height, margin, padding, display, position, font-size, adding/removing elements.

Repaint = Moderate cost. Triggers: changing color, background, visibility, box-shadow, border-color. Does NOT change geometry.

Composite = CHEAPEST. Triggers: changing transform (translate, rotate, scale), opacity. GPU-accelerated, separate layer.

CSS property cheat sheet: Use transform instead of top/left. Use opacity instead of visibility:hidden. Use will-change to hint the browser.

requestAnimationFrame: Runs BEFORE the render step — modify DOM in rAF, changes painted in same frame.

// ❌ SLOW: Animating with top/left triggers reflow every frame
// .element {
//   position: absolute;
//   transition: top 0.3s, left 0.3s;
// }
// .element.moved {
//   top: 100px;  // REFLOW on every frame!
//   left: 200px; // REFLOW on every frame!
// }

// ✅ FAST: Animating with transform triggers only composite
// .element {
//   transition: transform 0.3s;
//   will-change: transform; // Hint browser to create separate layer
// }
// .element.moved {
//   transform: translate(200px, 100px); // COMPOSITE only! GPU!
// }

// ✅ FAST: Opacity animations also composite-only
// .element {
//   transition: opacity 0.3s;
//   will-change: opacity;
// }
// .element.hidden {
//   opacity: 0; // COMPOSITE only! No repaint!
// }
Only two CSS properties are "composite-only": transform and opacity. For smooth 60fps animations, ONLY animate these two properties. Everything else triggers layout or paint, which can cause frame drops.

Lo kar liya — Key Points:

  • ✅ The DOM is a tree of nodes — Element nodes, Text nodes, Comment nodes — each with a nodeType and nodeName
  • ✅ Live HTMLCollections (getElementsBy*) auto-update when DOM changes; static NodeLists (querySelectorAll) are snapshots
  • ✅ Iterating a live collection while modifying the DOM skips elements — convert to array or iterate backwards
  • ✅ DocumentFragment allows building DOM structures offline and inserting in a single operation (one reflow)
  • ✅ The rendering pipeline: Style → Layout → Paint → Composite — each step is progressively cheaper
  • ✅ Reflow is most expensive (geometry change), Repaint is moderate (visual change), Composite is cheapest (transform/opacity only)
  • ✅ Only animate transform and opacity for 60fps — everything else triggers layout or paint
  • ✅ Reading layout properties (offsetWidth, getBoundingClientRect) forces immediate reflow — batch reads before writes
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase