4.5: DOM Manipulation

Master the Document Object Model (DOM) to dynamically manipulate HTML and CSS with JavaScript. Learn how to select elements, modify content and styles, create new elements, and respond to user interactions.

1. What is the DOM?

The Document Object Model (DOM) is a programming interface that represents HTML as a tree structure, allowing JavaScript to interact with the page.

DOM Tree Structure

HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>My Page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>World</p>
  </body>
</html>

DOM Tree:

document
  └── html
      ├── head
      │   └── title
      │       └── "My Page"
      └── body
          ├── h1
          │   └── "Hello"
          └── p
              └── "World"

DOM is NOT HTML

  • HTML is the source code (text)
  • DOM is the live representation in memory (JavaScript can modify it)

2. Selecting Elements

document.getElementById()

Select element by id attribute:

<div id="header">Welcome</div>
const header = document.getElementById("header");
console.log(header);  // <div id="header">Welcome</div>
console.log(header.textContent);  // "Welcome"

document.querySelector()

Select first element matching CSS selector:

<div class="box">Box 1</div>
<div class="box">Box 2</div>
<p id="intro">Intro</p>
// By class
const box = document.querySelector(".box");
console.log(box.textContent);  // "Box 1" (first match)

// By ID
const intro = document.querySelector("#intro");

// By tag
const p = document.querySelector("p");

// Complex selector
const firstBoxP = document.querySelector("div.box p");

document.querySelectorAll()

Select all elements matching CSS selector:

const boxes = document.querySelectorAll(".box");
console.log(boxes);  // NodeList [div.box, div.box]

// Iterate with forEach
boxes.forEach(box => {
  console.log(box.textContent);
});

// Convert to array
const boxArray = Array.from(boxes);
const boxArray2 = [...boxes];  // Spread operator

Other Selection Methods (Legacy)

// By tag name
const paragraphs = document.getElementsByTagName("p");

// By class name
const boxes = document.getElementsByClassName("box");

// Note: These return HTMLCollection (not NodeList)
// HTMLCollection is "live" (updates automatically)

Best practice: Use querySelector() and querySelectorAll() for consistency and power.


3. Reading Element Content

textContent vs innerHTML

textContent:

<div id="box">Hello <strong>World</strong></div>
const box = document.querySelector("#box");
console.log(box.textContent);  // "Hello World" (plain text)

innerHTML:

console.log(box.innerHTML);  // "Hello <strong>World</strong>" (includes HTML)

Difference:

  • textContent: Plain text only, safer (no HTML parsing)
  • innerHTML: HTML markup included, can execute scripts (security risk)

value (for form inputs)

<input type="text" id="username" value="Alice">
const input = document.querySelector("#username");
console.log(input.value);  // "Alice"

4. Modifying Element Content

Setting textContent

<div id="message">Old message</div>
const message = document.querySelector("#message");
message.textContent = "New message!";
// <div id="message">New message!</div>

Setting innerHTML

const container = document.querySelector("#container");
container.innerHTML = "<p>Hello <strong>World</strong></p>";

Security warning: Never use innerHTML with user input (XSS vulnerability):

// DANGEROUS!
const userInput = prompt("Enter your name");
element.innerHTML = `<p>Hello ${userInput}</p>`;  // Can inject scripts!

// SAFE alternative:
element.textContent = `Hello ${userInput}`;

Setting input values

<input type="text" id="username">
const input = document.querySelector("#username");
input.value = "Alice";

5. Modifying Attributes

getAttribute() and setAttribute()

<img id="photo" src="old.jpg" alt="Old photo">
const img = document.querySelector("#photo");

// Get attribute
console.log(img.getAttribute("src"));  // "old.jpg"

// Set attribute
img.setAttribute("src", "new.jpg");
img.setAttribute("alt", "New photo");

// Remove attribute
img.removeAttribute("alt");

// Check if attribute exists
console.log(img.hasAttribute("src"));  // true

Direct property access

const img = document.querySelector("#photo");

// Direct access (preferred for common attributes)
img.src = "new.jpg";
img.alt = "New photo";

console.log(img.src);  // Full URL: "http://example.com/new.jpg"
console.log(img.getAttribute("src"));  // "new.jpg" (as written in HTML)

Data attributes

<div id="user" data-user-id="123" data-role="admin">User</div>
const user = document.querySelector("#user");

// Access via dataset
console.log(user.dataset.userId);  // "123" (camelCase!)
console.log(user.dataset.role);    // "admin"

// Set data attribute
user.dataset.status = "active";
// <div ... data-status="active">User</div>

6. Modifying Styles

Inline styles (style property)

<div id="box">Box</div>
const box = document.querySelector("#box");

// Set individual styles (camelCase, not kebab-case)
box.style.color = "blue";
box.style.backgroundColor = "yellow";  // background-color → backgroundColor
box.style.fontSize = "20px";
box.style.display = "none";  // Hide element

// CSS property with dashes needs camelCase
// CSS: border-bottom-width → JS: borderBottomWidth

Note: This sets inline styles (high specificity, overrides CSS):

<!-- After JavaScript -->
<div id="box" style="color: blue; background-color: yellow;">Box</div>

CSS classes (better approach)

CSS:

.highlight {
  background-color: yellow;
  color: blue;
  font-weight: bold;
}

.hidden {
  display: none;
}

JavaScript:

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

// Add class
box.classList.add("highlight");

// Remove class
box.classList.remove("highlight");

// Toggle class
box.classList.toggle("hidden");  // Adds if not present, removes if present

// Check if has class
if (box.classList.contains("highlight")) {
  console.log("Box is highlighted");
}

// Replace class
box.classList.replace("highlight", "normal");

Best practice: Use CSS classes for styling, not inline styles.


7. Creating Elements

createElement()

// Create element
const div = document.createElement("div");

// Set content
div.textContent = "Hello, I'm new!";

// Set attributes
div.id = "new-box";
div.className = "box highlight";  // className, not class

// Set styles
div.style.padding = "10px";

// Element exists in memory, but not in DOM yet!
console.log(div);  // <div id="new-box" class="box highlight">Hello, I'm new!</div>

Inserting elements

appendChild() - Add to end:

const container = document.querySelector("#container");
const newDiv = document.createElement("div");
newDiv.textContent = "New item";

container.appendChild(newDiv);  // Adds to end of container

insertBefore() - Add before specific element:

const container = document.querySelector("#container");
const referenceNode = document.querySelector("#item-2");
const newDiv = document.createElement("div");

container.insertBefore(newDiv, referenceNode);  // Inserts before item-2

Modern methods (ES6+):

const element = document.querySelector("#target");
const newDiv = document.createElement("div");

element.prepend(newDiv);    // Add to beginning
element.append(newDiv);     // Add to end
element.before(newDiv);     // Add before element
element.after(newDiv);      // Add after element

Creating text nodes

const textNode = document.createTextNode("Hello");
element.appendChild(textNode);

// Or just use textContent (easier)
element.textContent = "Hello";

8. Removing Elements

remove() (modern)

const element = document.querySelector("#to-remove");
element.remove();  // Removes itself from DOM

removeChild() (older)

const parent = document.querySelector("#container");
const child = document.querySelector("#to-remove");
parent.removeChild(child);

Replacing elements

const oldElement = document.querySelector("#old");
const newElement = document.createElement("div");
newElement.textContent = "I'm new!";

oldElement.replaceWith(newElement);  // Modern
// Or
parent.replaceChild(newElement, oldElement);  // Older

9. DOM Traversal

Parent, children, siblings

HTML:

<div id="container">
  <div id="first">First</div>
  <div id="second">Second</div>
  <div id="third">Third</div>
</div>

Traversal:

const second = document.querySelector("#second");

// Parent
console.log(second.parentElement);  // <div id="container">

// Children (of container)
const container = document.querySelector("#container");
console.log(container.children);  // HTMLCollection [div#first, div#second, div#third]
console.log(container.firstElementChild);  // <div id="first">
console.log(container.lastElementChild);   // <div id="third">
console.log(container.childElementCount);  // 3

// Siblings
console.log(second.previousElementSibling);  // <div id="first">
console.log(second.nextElementSibling);      // <div id="third">

Note: Use Element variants (parentElement, nextElementSibling) to ignore text nodes.

Finding elements relative to current

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

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

// Find descendants
const items = element.querySelectorAll(".item");

10. Practical Examples

Example 1: Dynamic List

HTML:

<ul id="todo-list"></ul>
<input type="text" id="todo-input">
<button id="add-btn">Add</button>

JavaScript:

const list = document.querySelector("#todo-list");
const input = document.querySelector("#todo-input");
const btn = document.querySelector("#add-btn");

btn.addEventListener("click", () => {
  const text = input.value.trim();

  if (text) {
    const li = document.createElement("li");
    li.textContent = text;
    list.appendChild(li);
    input.value = "";  // Clear input
  }
});

Example 2: Toggle Visibility

HTML:

<button id="toggle-btn">Toggle</button>
<div id="content">This content can be hidden</div>

CSS:

.hidden {
  display: none;
}

JavaScript:

const btn = document.querySelector("#toggle-btn");
const content = document.querySelector("#content");

btn.addEventListener("click", () => {
  content.classList.toggle("hidden");
});

Example 3: Change Theme

HTML:

<button id="theme-btn">Toggle Dark Mode</button>

CSS:

body.dark-mode {
  background-color: #222;
  color: #fff;
}

JavaScript:

const btn = document.querySelector("#theme-btn");

btn.addEventListener("click", () => {
  document.body.classList.toggle("dark-mode");
});

Example 4: Form Validation

HTML:

<form id="login-form">
  <input type="text" id="username" required>
  <span id="error" style="color: red;"></span>
  <button type="submit">Login</button>
</form>

JavaScript:

const form = document.querySelector("#login-form");
const usernameInput = document.querySelector("#username");
const error = document.querySelector("#error");

form.addEventListener("submit", (e) => {
  e.preventDefault();  // Prevent form submission

  const username = usernameInput.value.trim();

  if (username.length < 3) {
    error.textContent = "Username must be at least 3 characters";
    usernameInput.classList.add("invalid");
  } else {
    error.textContent = "";
    usernameInput.classList.remove("invalid");
    console.log("Form submitted:", username);
  }
});

11. Practical Exercises

Exercise 4.5.1: Element Selector

Create a page with various elements and practice selecting:

  1. By ID
  2. By class (first and all)
  3. By tag
  4. Using complex selectors

Exercise 4.5.2: Content Modifier

Build a page that:

  1. Changes heading text on button click
  2. Updates paragraph content
  3. Modifies image src and alt

Exercise 4.5.3: Style Manipulator

Create buttons to:

  1. Change background color
  2. Toggle text size (large/small)
  3. Hide/show elements
  4. Apply multiple classes

Exercise 4.5.4: Dynamic List Builder

Build an interactive list where users can:

  1. Add items
  2. Remove items (with delete button)
  3. Mark items as complete (with checkbox/click)
  4. Clear all items

12. Knowledge Check

Question 1: What's the difference between textContent and innerHTML?

Show answer textContent gets/sets plain text. innerHTML gets/sets HTML markup. Use textContent for security (prevents XSS) unless you need to insert HTML.

Question 2: How do you add a CSS class to an element?

Show answer Use `element.classList.add("className")`. To toggle: `classList.toggle()`, to remove: `classList.remove()`.

Question 3: What's the difference between querySelector and querySelectorAll?

Show answer querySelector returns first matching element (or null). querySelectorAll returns NodeList of all matching elements.

Question 4: How do you create and insert a new element?

Show answer Create with `document.createElement("tagName")`, set properties, then insert with `appendChild()`, `append()`, `prepend()`, `before()`, or `after()`.

Question 5: What's the safest way to display user input?

Show answer Use `textContent` instead of `innerHTML` to prevent XSS attacks. textContent treats everything as plain text.

13. Key Takeaways

  • DOM is a tree representation of HTML that JavaScript can manipulate
  • Use querySelector() for single element, querySelectorAll() for multiple
  • textContent for plain text (safer), innerHTML for HTML markup
  • Modify attributes with setAttribute() or direct properties (img.src)
  • Use classList methods (add, remove, toggle) for CSS classes
  • Create elements with createElement(), insert with appendChild() or modern methods
  • Remove with element.remove()
  • Traverse DOM with parentElement, children, nextElementSibling, etc.
  • Always validate and sanitize user input
  • Prefer CSS classes over inline styles

14. Further Resources

Documentation:


Next Steps

Great work! You can now manipulate the DOM to create dynamic web pages.

In Lesson 4.6: Events & Event Handling, you'll learn to respond to user interactions like clicks, keyboard input, and form submissions.

Next: Lesson 4.6 - Events & Event Handling →