JavaScript

DOM 1.1: Structure & Manipulation — selecting, traversing, content editing, attributes, classList, styles, and high-performance node manipulation.

1. What is the DOM?

The Document Object Model (DOM) is the browser's live, in-memory representation of your HTML webpage. When the browser loads an HTML file, it parses the tags and constructs a tree of objects that JavaScript can inspect and modify.

Whenever JavaScript changes the DOM, the browser updates the visual display immediately.

💡 The Blueprint vs The Living Building Analogy

Think of HTML as an architect's blueprint on paper, and the DOM as the actual living building standing in real life:

  • HTML file: The static text document stored on your hard drive.
  • DOM: The live, interactive object tree constructed in the browser's RAM memory.
  • JavaScript: The interior decorator who rearranges walls, repaints doors, and turns on lights in the living building without ever touching the paper blueprint.

The DOM Tree Hierarchy

document
 └── html
      ├── head
      │    └── title → "My Web App"
      └── body
           ├── h1   → "Hello World"
           ├── p    → "Welcome to JS"
           └── button → "Click Me"

Node Types Cheat Sheet

Node Type Example nodeType Number
Element Node <div>, <p>, <button> 1 (Most common)
Text Node The raw text inside an element ("Hello") 3
Comment Node <!-- Developer note --> 8
Document Node The root document object itself 9
01

window (The Browser Environment)

The global container representing the browser tab itself: handles timers (setTimeout), local storage (localStorage), and URL location (window.location).

02

document (The HTML Page Inside)

A property of window representing the active webpage inside the viewport: handles element selection, node creation, and page layout (document.body, document.title).

2. Selecting Elements

Every DOM operation starts with finding the right element. Modern JavaScript provides fast, flexible selector methods:

// 1. By ID (Fastest single element lookup, returns Element or null)
const mainTitle = document.getElementById("header-title");

// 2. By CSS Selector (Returns the FIRST matching element, or null)
const primaryBtn = document.querySelector(".btn-primary");
const emailField = document.querySelector("form input[type='email']");

// 3. All Matches (Returns a static NodeList)
const allCards = document.querySelectorAll(".card-item");

// 4. Legacy methods (Return LIVE HTMLCollections)
const tagList = document.getElementsByTagName("p");
const classList = document.getElementsByClassName("active");

⚠️ Always Guard Against null!

If an element does not exist in the HTML, querySelector returns null. Attempting to set properties on null will crash your code with a TypeError:

const el = document.querySelector(".missing-box");
// ❌ Crashes: TypeError: Cannot set properties of null (setting 'textContent')
// el.textContent = "Hello";

// ✅ Safe: Optional chaining or null check
if (el) el.textContent = "Found!";
el?.classList.add("highlight");

Static NodeList vs Live HTMLCollection

Method Return Type Live or Static? Recommended?
document.getElementById('id') Single Element / null Static reference ✅ Yes (for known unique IDs)
document.querySelector('sel') First Element / null Static reference 🌟 Highly Recommended (Universal)
document.querySelectorAll('sel') NodeList Static Snapshot 🌟 Highly Recommended (Safe for loops)
document.getElementsByClassName() HTMLCollection Live Collection ⚠️ Caution (Auto-mutates during loops)
💡 Live Demo: Static vs Live Collections in Action
// 1. Create a container with 2 items:
const container = document.createElement("div");
for (let i = 0; i < 2; i++) {
    const item = document.createElement("p");
    item.className = "item";
    container.append(item);
}

const staticSnapshot = container.querySelectorAll(".item");       // NodeList snapshot
const liveCollection = container.getElementsByClassName("item");  // Live HTMLCollection

console.log("Before adding item:", staticSnapshot.length, liveCollection.length); // 2 2

// 2. Add a third item to the container:
const newItem = document.createElement("p");
newItem.className = "item";
container.append(newItem);

console.log("After adding item:", staticSnapshot.length, liveCollection.length);  // 2 3
▶ Live Lab 1: Selecting & Modifying Styles & Content
Interactive Sandbox
JavaScript Editor (Edit code & click Run) Ctrl+Enter
Hello DOM
🎨 Color:
🔲 Shape:
✏️ Text:

3. Traversing the DOM Tree

Once you have a reference to one element, you can navigate up, down, or sideways through the hierarchy without re-querying the whole document:

const card = document.querySelector(".card");

// 1. Upwards traversal
card.parentElement;                 // The direct parent node
card.closest(".dashboard-section"); // Nearest ancestor matching CSS selector (Crucial for delegation!)

// 2. Downwards traversal (Elements only)
card.children;                      // HTMLCollection of child elements
card.firstElementChild;             // First child element
card.lastElementChild;              // Last child element

// 3. Sideways sibling traversal
card.nextElementSibling;            // Next sibling element
card.previousElementSibling;        // Previous sibling element

// 4. Utility checks
card.matches(".active");            // Does this element match the selector? (returns true/false)
card.contains(subElement);          // Is subElement nested inside card? (returns true/false)

💡 Tip: Always Use 'Element' Properties

Avoid childNodes, firstChild, and nextSibling. Those older properties include whitespace text nodes and HTML comments, which causes unexpected bugs. Always use children, firstElementChild, and nextElementSibling.

4. Reading & Changing Content (Security & XSS)

JavaScript provides multiple properties to read and update an element's content:

Property Behavior Security with User Input
textContent Gets or sets plain text. Strips all HTML tags and renders characters literally. ✅ 100% Safe (Prevents XSS attacks)
innerText Gets or sets visible rendered text (respects CSS display: none). Triggers reflow. ✅ Safe
innerHTML Gets or sets HTML markup. Parses tags and builds child DOM nodes. ❌ Dangerous (Vulnerable to XSS if untrusted)
outerHTML Includes the element itself along with its inner HTML markup. ❌ Dangerous (Vulnerable to XSS)

🚨 The #1 DOM Security Trap: Cross-Site Scripting (XSS)

const userInput = '<img src="invalid" onerror="alert(\'Hacked!\')">';

// ❌ DANGEROUS: Executes malicious injected script!
// card.innerHTML = `<h3>Welcome ${userInput}</h3>`;

// ✅ SECURE: Characters like < and > are treated strictly as text!
card.textContent = `Welcome ${userInput}`;

5. Attributes, classList, dataset & Styles

Manipulating Classes with classList

Never overwrite element.className = "..." directly because it erases existing utility classes. Always use the built-in classList API:

const btn = document.createElement("button");

btn.classList.add("btn", "btn-primary"); // Add one or more classes
btn.classList.remove("btn-primary");     // Remove a class
btn.classList.toggle("active");          // Adds if absent, removes if present
btn.classList.toggle("dark", true);      // Force add (if 2nd arg is true)
btn.classList.contains("btn");           // Returns true
btn.classList.replace("btn", "badge");   // Replaces 'btn' with 'badge'

Custom Data Attributes (dataset)

HTML5 allows embedding custom data with data-* attributes. JavaScript reads them as camelCase properties on the dataset object:

// HTML: <div id="userCard" data-user-id="4590" data-account-type="premium"></div>
const userCard = document.getElementById("userCard");

console.log(userCard.dataset.userId);       // "4590" (Always a string!)
console.log(userCard.dataset.accountType);  // "premium"

// Update attribute dynamically:
userCard.dataset.accountType = "admin";    // Sets data-account-type="admin"

Inline Styles vs getComputedStyle()

const box = document.createElement("div");
// 1. Setting styles (camelCase properties):
box.style.backgroundColor = "#6C4CE8";
box.style.fontSize = "16px";
box.style.setProperty("--custom-theme", "#10B981");

// 2. Reading REAL styles (from external CSS stylesheets):
const computed = window.getComputedStyle(box);
console.log("Calculated background:", computed.backgroundColor);
▶ Live Lab 2: Manipulating Classes with classList
Interactive Sandbox
JavaScript Editor Ctrl+Enter

Developer Profile

Classes change appearance dynamically via JavaScript.

⚡ Toggle:

6. Creating, Inserting & Batching DOM Elements

Modern DOM Insertion Methods

const newCard = document.createElement("div");
newCard.className = "card";
newCard.textContent = "Fresh Card";

// Modern insertion methods:
parentEl.append(newCard);         // Inserts at the end of parent (can accept strings & nodes)
parentEl.prepend(newCard);        // Inserts at the very start of parent
referenceEl.before(newCard);      // Inserts right before referenceEl
referenceEl.after(newCard);       // Inserts right after referenceEl
referenceEl.replaceWith(newCard);  // Swaps referenceEl with newCard
newCard.remove();                 // Removes element from the DOM directly!

High Performance Batching with DocumentFragment

Inserting 1,000 elements one by one into the live DOM causes 1,000 costly browser layout recalculations (reflows). A DocumentFragment is an invisible, lightweight off-screen container that lets you prepare elements in memory and inject them with a single reflow:

const userList = document.createElement("ul");
const fragment = document.createDocumentFragment(); // Lightweight off-screen memory bucket

const users = ["Anshuman", "Priya", "Rahul", "Sara", "Vikram"];

users.forEach(name => {
    const li = document.createElement("li");
    li.textContent = name;
    fragment.append(li); // Adding to off-screen fragment does NOT trigger reflow
});

userList.append(fragment); // 🚀 ONE single paint & reflow to the real DOM!
console.log("Generated HTML:", userList.outerHTML);
▶ Live Lab 3: Creating & Removing DOM Elements
Interactive Sandbox
JavaScript Editor Ctrl+Enter
HTML5 CSS3 JavaScript
Placement MCQ Quiz: DOM Structure & Manipulation
Question 1 of 5
Static vs Live Collection TrapQ1 of 5
const div = document.createElement("div");
div.innerHTML = '<span class="tag">A</span><span class="tag">B</span>';

const staticList = div.querySelectorAll(".tag");
const liveList = div.getElementsByClassName("tag");

const extra = document.createElement("span");
extra.className = "tag";
div.append(extra);

console.log(staticList.length, liveList.length);
What will be printed to the console?
textContent vs innerHTMLQ2 of 5
const p = document.createElement("p");
p.textContent = "<b>Alex</b>";

console.log(p.innerHTML);
What does p.innerHTML output?
closest() Selector NavigationQ3 of 5
const card = document.createElement("article");
card.className = "card";

const btn = document.createElement("button");
card.append(btn);

console.log(btn.closest(".card") === card);
What will be printed to the console?
classList.toggle() LogicQ4 of 5
const el = document.createElement("div");

const firstToggle = el.classList.toggle("active");
const secondToggle = el.classList.toggle("active");

console.log(firstToggle, secondToggle);
What are the return values of firstToggle and secondToggle?
DocumentFragment PerformanceQ5 of 5
const frag = document.createDocumentFragment();
for (let i = 0; i < 50; i++) {
    const li = document.createElement("li");
    frag.append(li);
}

const ul = document.createElement("ul");
ul.append(frag);
How many live DOM reflow calculations occur when appending frag to ul?

7. Hands-On Practice: Build a Dynamic Profile Card

Write a function createProfileCard({ name, role, skills }) that creates and returns an <article class="profile-card"> element with an <h2> for the name, a <p> for the role, and an <ul> containing <li> items for each skill.