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:
- By ID
- By class (first and all)
- By tag
- Using complex selectors
Exercise 4.5.2: Content Modifier
Build a page that:
- Changes heading text on button click
- Updates paragraph content
- Modifies image src and alt
Exercise 4.5.3: Style Manipulator
Create buttons to:
- Change background color
- Toggle text size (large/small)
- Hide/show elements
- Apply multiple classes
Exercise 4.5.4: Dynamic List Builder
Build an interactive list where users can:
- Add items
- Remove items (with delete button)
- Mark items as complete (with checkbox/click)
- 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 textContentfor plain text (safer),innerHTMLfor HTML markup- Modify attributes with
setAttribute()or direct properties (img.src) - Use
classListmethods (add, remove, toggle) for CSS classes - Create elements with
createElement(), insert withappendChild()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 →