JavaScript Interview Prep
DOM & Browser APIs

DOM Manipulation

Reaching Into the Living Tree

LinkedIn Hook

You can write flawless JavaScript logic and still freeze the moment the interviewer says: "Render these 1,000 items without locking the browser."

That's not an algorithm question. That's a DOM question.

The DOM is the browser's live in-memory tree of your HTML. Every query, every append, every class toggle either runs cheaply or forces the browser to rebuild layout — and the difference between a fast UI and a janky one usually comes down to understanding that tree.

In this lesson you'll learn how to select, create, and modify DOM nodes; why textContent beats innerText; and why one DocumentFragment can be ten times faster than a naive loop.

If you've ever wondered why appending 1,000 <li>s kills your page — this lesson fixes that.

Read the full lesson -> [link]

#JavaScript #DOM #Frontend #WebPerformance #InterviewPrep #CodingInterview #WebDevelopment


DOM Manipulation thumbnail


What You'll Learn

  • How to select, create, append, and remove DOM nodes with modern APIs
  • The real differences between innerHTML, textContent, and innerText (including XSS and reflow implications)
  • Why DocumentFragment slashes reflow cost for batch inserts

The Living Tree

The DOM (Document Object Model) is the browser's in-memory representation of your HTML. Think of it as a living tree — every element is a node, and JavaScript can reach into that tree to read, add, remove, or modify any node at any time.

Selecting Elements

// By ID — returns single element or null
const header = document.getElementById("main-header");

// querySelector — returns FIRST match (CSS selector)
const firstBtn = document.querySelector(".btn");
const nav = document.querySelector("nav > ul");

// querySelectorAll — returns ALL matches (NodeList, NOT live)
const allButtons = document.querySelectorAll(".btn");

// Older methods (still valid, return live HTMLCollections)
const divs = document.getElementsByTagName("div");
const items = document.getElementsByClassName("item");

Key difference: querySelectorAll returns a static NodeList (a snapshot). getElementsByClassName and getElementsByTagName return live HTMLCollections that automatically update when the DOM changes.

Creating, Appending, and Removing Elements

// Create
const card = document.createElement("div");
card.className = "card";
card.textContent = "New Card";

// Append
document.getElementById("container").appendChild(card);

// Modern append (accepts strings too)
container.append(card, " some text");

// Insert before a specific element
const ref = document.querySelector(".reference");
container.insertBefore(card, ref);

// Remove
card.remove(); // Modern
// OR
card.parentNode.removeChild(card); // Older approach

Modifying Attributes, Classes, and Styles

const el = document.querySelector(".box");

// Attributes
el.setAttribute("data-id", "42");
el.getAttribute("data-id"); // "42"
el.removeAttribute("data-id");

// Classes
el.classList.add("active", "highlighted");
el.classList.remove("highlighted");
el.classList.toggle("active"); // add if absent, remove if present
el.classList.contains("active"); // true or false

// Inline styles
el.style.backgroundColor = "coral";
el.style.transform = "translateX(100px)";

innerHTML vs textContent vs innerText

const div = document.querySelector("#example");

// innerHTML — parses HTML (XSS risk if used with user input!)
div.innerHTML = "<strong>Bold</strong> text";

// textContent — raw text, ignores HTML, includes hidden elements
div.textContent = "<strong>Bold</strong> text";
// Renders literally: "<strong>Bold</strong> text"

// innerText — "visible" text only, triggers reflow to compute
console.log(div.innerText); // only what's visually rendered

Performance note: innerText is slower because it triggers a reflow to compute CSS visibility. Prefer textContent when you don't need visual awareness.

DocumentFragment for Batch Operations

This is a critical performance pattern. Every time you append to the live DOM, the browser may trigger a reflow/repaint. A DocumentFragment lets you build a subtree off-screen, then insert it all at once.

// BAD — triggers reflow on every iteration
function addItemsSlow(count) {
  const list = document.getElementById("list");
  for (let i = 0; i < count; i++) {
    const li = document.createElement("li");
    li.textContent = `Item ${i}`;
    list.appendChild(li); // reflow each time!
  }
}

// GOOD — single reflow with DocumentFragment
function addItemsFast(count) {
  const list = document.getElementById("list");
  const fragment = document.createDocumentFragment();

  for (let i = 0; i < count; i++) {
    const li = document.createElement("li");
    li.textContent = `Item ${i}`;
    fragment.appendChild(li); // off-screen, no reflow
  }

  list.appendChild(fragment); // ONE reflow
}

// Performance test
console.time("slow");
addItemsSlow(1000); // ~50ms+ depending on complexity
console.timeEnd("slow");

console.time("fast");
addItemsFast(1000); // ~5-10ms
console.timeEnd("fast");

Reflow vs Repaint

  • Reflow (Layout): The browser recalculates positions and dimensions of elements. Triggered by changes to width, height, margin, padding, adding/removing elements, reading offsetHeight, etc.
  • Repaint: The browser redraws pixels (color, shadow, visibility changes) without recalculating layout. Cheaper than reflow.

Every reflow triggers a repaint, but not every repaint triggers a reflow.

DOM Manipulation visual 1


Common Mistakes

  • Using innerHTML with untrusted strings — any user-supplied value becomes executable markup and opens an XSS hole. Use textContent for text or sanitize before injecting HTML.
  • Appending nodes inside a loop to the live DOM — each append can trigger a reflow. Build inside a DocumentFragment and insert once.
  • Assuming querySelectorAll returns a live collection — it returns a static snapshot, so DOM changes after the call won't show up. Live collections come from getElementsByClassName / getElementsByTagName.

Interview Questions

Q: What is the difference between querySelector and getElementById?

getElementById only selects by ID and returns a single element. querySelector accepts any CSS selector and returns the first match. getElementById is slightly faster for ID lookups, but querySelector is more versatile.

Q: Why use DocumentFragment?

DocumentFragment is an off-screen container that doesn't trigger reflow when you append to it. You build your subtree inside the fragment, then insert it into the live DOM in one operation — causing only a single reflow instead of one per element.

Q: What is the difference between innerHTML and textContent?

innerHTML parses and renders HTML markup (potential XSS risk with user input). textContent treats everything as raw text, is faster, and is safe from injection. innerText is similar to textContent but only returns visible text and triggers reflow.

Q: What is the DOM? How does JavaScript interact with it?

The DOM is a tree-structured, in-memory representation of the parsed HTML document that the browser builds. JavaScript interacts with it through the document object and its APIs (querySelector, createElement, appendChild, etc.), reading and mutating nodes to change what the user sees.

Q: What is the difference between querySelector and querySelectorAll?

querySelector returns the first matching element (or null). querySelectorAll returns a static NodeList of every match; it's iterable with forEach but is not live.

Q: Why is innerText slower than textContent?

innerText needs to know what's visually rendered, so it forces the browser to flush pending layout work (a reflow) before returning. textContent reads the raw node text without consulting styles, so it's cheap.

Q: What triggers a reflow? What triggers only a repaint?

Reflow is triggered by anything that changes geometry: adding/removing nodes, changing width/height/margin/padding, changing fonts, or reading layout properties like offsetHeight. Repaint-only changes don't affect layout — color, visibility, box-shadow, or background-image swaps.


Quick Reference — Cheat Sheet

DOM MANIPULATION — QUICK MAP

Selection:
  getElementById("id")        -> single element or null
  querySelector(".css")       -> first match (CSS selector)
  querySelectorAll(".css")    -> static NodeList
  getElementsByClassName(...) -> LIVE HTMLCollection

Create / Insert / Remove:
  createElement("div")
  parent.appendChild(node)  |  parent.append(...nodes, ...strings)
  parent.insertBefore(node, refNode)
  node.remove()

Attributes / Classes / Styles:
  el.setAttribute / getAttribute / removeAttribute
  el.classList.add / remove / toggle / contains
  el.style.<camelCaseProp> = "..."

Text vs HTML:
  textContent -> raw text, fast, safe
  innerText   -> visible text, triggers reflow
  innerHTML   -> parses HTML, XSS risk with user input

Batch inserts:
  const f = document.createDocumentFragment();
  // append many nodes to f (no reflow)
  parent.appendChild(f);        // ONE reflow

Previous: Error Handling in Promises Next: Event Handling & Event Delegation


This is Lesson 11.1 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page