05 - DOM Manipulation

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";

// 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
const element = document.querySelector(".card");
element.dataset.userId = "123";  // <div data-user-id="123">
console.log(element.dataset.userId);  // "123"

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

Handle events on dynamically added elements:

// ❌ Won't work for dynamically added buttons
const buttons = document.querySelectorAll("button");
buttons.forEach((btn) => {
  btn.addEventListener("click", () => console.log("Clicked"));
});

// ✅ Works for all buttons, even future ones
document.body.addEventListener("click", (e) => {
  if (e.target.matches("button")) {
    console.log("Button clicked:", e.target.textContent);
  }
});

// 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)
  • Add/remove CSS classes with classList instead of inline styles
  • Create elements with createElement and add them with appendChild
  • Use addEventListener to respond to user interactions
  • Event delegation handles events on dynamically added elements
  • Always preventDefault() on form submit to stop page reload
  • Traverse the DOM with parentElement, children, closest(), etc.