05 - DOM Manipulation

📋 Jump to Takeaways

What is the DOM?

The Document Object Model (DOM) is a tree representation of your HTML that JavaScript can interact with. It lets you read and modify web page content, structure, and styling.

<!DOCTYPE html>
<html>
  <body>
    <h1 id="title">Hello World</h1>
    <p class="text">This is a paragraph</p>
    <script>
      // JavaScript can access and modify these elements
      const title = document.getElementById("title");
      console.log(title.textContent);  // "Hello World"
    </script>
  </body>
</html>

Selecting Elements

// By ID — returns single element
const title = document.getElementById("title");

// By class name — returns HTMLCollection (array-like)
const paragraphs = document.getElementsByClassName("text");

// By tag name — returns HTMLCollection
const allDivs = document.getElementsByTagName("div");

// Query selector — returns first match (CSS selector syntax)
const first = document.querySelector(".text");
const byId = document.querySelector("#title");
const complex = document.querySelector("div.container > p");

// Query selector all — returns NodeList (array-like)
const all = document.querySelectorAll(".text");

// Modern approach — use querySelector/querySelectorAll

Modifying Content

const element = document.querySelector("#title");

// Text content — safe, doesn't parse HTML
element.textContent = "New Title";

// Inner HTML — can include HTML tags (be careful with user input)
element.innerHTML = "<strong>Bold Title</strong>";

// Inner text — respects CSS visibility
element.innerText = "Visible Text";

// textContent vs innerText
// Given: <p>Hello <span style="display:none">World</span></p>
element.textContent; // "Hello World" — includes hidden text
element.innerText;   // "Hello "     — only visible text

// Example
const container = document.querySelector(".container");
container.innerHTML = `
  <h2>Dynamic Content</h2>
  <p>Created with JavaScript</p>
`;

Modifying Styles

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

// Direct style modification
box.style.color = "red";
box.style.backgroundColor = "blue";
box.style.fontSize = "20px";

// Multiple styles
Object.assign(box.style, {
  color: "white",
  backgroundColor: "black",
  padding: "20px",
  borderRadius: "5px"
});

// Better approach — use CSS classes
box.classList.add("highlighted");
box.classList.remove("hidden");
box.classList.toggle("active");  // add if not present, remove if present

// Check if class exists
if (box.classList.contains("active")) {
  console.log("Box is active");
}

Modifying Attributes

const link = document.querySelector("a");

// Get attribute
const href = link.getAttribute("href");

// Set attribute
link.setAttribute("href", "https://example.com");
link.setAttribute("target", "_blank");

// Remove attribute
link.removeAttribute("target");

// Check if attribute exists
if (link.hasAttribute("href")) {
  console.log("Link has href");
}

// Direct property access (for common attributes)
link.href = "https://example.com";
link.className = "active";
link.id = "main-link";

// Data attributes — store custom data on any HTML element
// In HTML: <div class="card" data-user-id="123" data-role="admin">
// In JS: access via element.dataset (camelCase, not kebab-case)
const element = document.querySelector(".card");
element.dataset.userId = "123";         // sets data-user-id="123"
element.dataset.role = "admin";         // sets data-role="admin"
console.log(element.dataset.userId);    // "123"
console.log(element.dataset.role);      // "admin"

Creating Elements

// Create element
const div = document.createElement("div");
div.textContent = "New element";
div.className = "box";

// Add to page
document.body.appendChild(div);

// Insert at specific position
const container = document.querySelector(".container");
container.appendChild(div);  // add to end
container.prepend(div);      // add to beginning

// Insert before/after another element
const reference = document.querySelector(".reference");
reference.before(div);   // insert before
reference.after(div);    // insert after

// More complex example
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
  <h3>Card Title</h3>
  <p>Card description</p>
  <button>Click Me</button>
`;
document.body.appendChild(card);

Removing Elements

const element = document.querySelector(".remove-me");

// Modern way
element.remove();

// Old way (still works)
element.parentNode.removeChild(element);

// Remove all children
const container = document.querySelector(".container");
container.innerHTML = "";  // quick but loses event listeners

// Better — remove one by one
while (container.firstChild) {
  container.firstChild.remove();
}

Event Listeners

Make your page interactive by responding to user actions:

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

// Add click event
button.addEventListener("click", () => {
  console.log("Button clicked!");
});

// Event with parameter
button.addEventListener("click", (event) => {
  console.log(event.target);  // the button element
  console.log(event.type);    // "click"
});

// Multiple event types
const input = document.querySelector("input");

input.addEventListener("focus", () => {
  console.log("Input focused");
});

input.addEventListener("blur", () => {
  console.log("Input lost focus");
});

input.addEventListener("input", (e) => {
  console.log("Current value:", e.target.value);
});

// Form submission
const form = document.querySelector("form");
form.addEventListener("submit", (e) => {
  e.preventDefault();  // stop page reload
  console.log("Form submitted");
});

Common Events

// Mouse events
element.addEventListener("click", handler);
element.addEventListener("dblclick", handler);
element.addEventListener("mouseenter", handler);
element.addEventListener("mouseleave", handler);
element.addEventListener("mousemove", handler);

// Keyboard events
document.addEventListener("keydown", (e) => {
  console.log(`Key pressed: ${e.key}`);
});

document.addEventListener("keyup", handler);

// Form events
input.addEventListener("input", handler);    // any change
input.addEventListener("change", handler);   // after blur
input.addEventListener("focus", handler);
input.addEventListener("blur", handler);

// Window events
window.addEventListener("load", handler);    // page fully loaded
window.addEventListener("resize", handler);  // window resized
window.addEventListener("scroll", handler);  // page scrolled

Event Delegation

Event delegation is when you attach a single event listener to a parent element instead of adding individual listeners to each child. It works because of event bubbling — when a child element is clicked, the event bubbles up to the parent.

// ❌ Without delegation — listener on every button
document.querySelectorAll(".btn").forEach(btn => {
  btn.addEventListener("click", handleClick);
});

// ✅ With delegation — one listener on the parent
document.querySelector(".button-list").addEventListener("click", (e) => {
  if (e.target.matches(".btn")) {
    handleClick(e);
  }
});

Why it matters:

  • Works for dynamically added elements (new children automatically handled)
  • Better performance with many elements (one listener vs hundreds)
  • Less memory usage

The e.target tells you which specific child was clicked, and you use matches() or closest() to filter for the elements you care about.

// Real-world example — todo list
const list = document.querySelector("#todo-list");

list.addEventListener("click", (e) => {
  if (e.target.matches(".delete-btn")) {
    e.target.closest("li").remove();
  }

  if (e.target.matches(".todo-item")) {
    e.target.classList.toggle("completed");
  }
});

Traversing the DOM

Navigate between elements:

const element = document.querySelector(".current");

// Parent
const parent = element.parentElement;

// Children
const children = element.children;        // HTMLCollection
const firstChild = element.firstElementChild;
const lastChild = element.lastElementChild;

// Siblings
const next = element.nextElementSibling;
const prev = element.previousElementSibling;

// Find closest ancestor matching selector
const container = element.closest(".container");

// Example — find parent card
const button = document.querySelector(".delete-btn");
const card = button.closest(".card");
card.remove();

Practical Example — Counter

<!DOCTYPE html>
<html>
<head>
  <style>
    .counter {
      text-align: center;
      margin: 50px;
    }
    .count {
      font-size: 48px;
      margin: 20px;
    }
    button {
      padding: 10px 20px;
      margin: 5px;
      font-size: 16px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="counter">
    <h1>Counter App</h1>
    <div class="count">0</div>
    <button id="decrement">-</button>
    <button id="reset">Reset</button>
    <button id="increment">+</button>
  </div>

  <script>
    let count = 0;
    const display = document.querySelector(".count");

    document.getElementById("increment").addEventListener("click", () => {
      count++;
      display.textContent = count;
    });

    document.getElementById("decrement").addEventListener("click", () => {
      count--;
      display.textContent = count;
    });

    document.getElementById("reset").addEventListener("click", () => {
      count = 0;
      display.textContent = count;
    });
  </script>
</body>
</html>

Key Takeaways

  • Use querySelector and querySelectorAll for selecting elements
  • Modify content with textContent (safe) or innerHTML (for HTML)
  • textContent includes hidden text, innerText only shows visible text
  • Add/remove CSS classes with classList instead of inline styles
  • Store custom data on elements with data-* attributes, access via dataset
  • Create elements with createElement, add with appendChild or prepend
  • Remove elements with element.remove()
  • Use addEventListener to respond to user interactions
  • Event delegation: listen on the parent instead of each child, check e.target to know which element was clicked
  • Always preventDefault() on form submit to stop page reload
  • Traverse the DOM with parentElement, children, closest()

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

© 2026 ByteLearn.dev. Free courses for developers. · Privacy