Every interactive page you have ever used works the same way: the browser parses your HTML into a tree of objects, and JavaScript changes that tree after the page has loaded. That tree is the Document Object Model. This lesson is where JavaScript stops being console exercises and starts changing what people see.
Learning objectives
- Select elements with
getElementById,querySelectorandquerySelectorAll. - Read and write content safely with
textContentinstead ofinnerHTML. - Create, insert and remove elements.
- Toggle styling with
classListand store data on elements withdataset.
1. Selecting elements
const title = document.getElementById("title"); // one element by id
const firstCard = document.querySelector(".card"); // first match, CSS selector
const allCards = document.querySelectorAll(".card"); // static NodeList
const input = document.querySelector("form input[type='text']");
querySelectorAll returns a static NodeList: it does not update when the page later changes. Convert it when you need array methods:
const cards = [...document.querySelectorAll(".card")];
cards.forEach((card) => card.classList.add("is-visible"));
2. textContent vs innerHTML
const output = document.querySelector("#output");
output.textContent = "Saved"; // safe: text only
output.innerHTML = "<strong>Saved</strong>"; // parses HTML
innerHTML executes whatever markup it receives. If any part of that string came from a user — a comment, a name, a search query — you have created an XSS hole. Use textContent by default and build markup with createElement.
3. Creating, inserting and removing elements
const list = document.querySelector("#list");
const item = document.createElement("li");
item.textContent = "New task";
item.classList.add("task");
list.append(item); // add at the end
list.prepend(item); // add at the start
item.remove(); // remove from the page
append accepts strings and multiple nodes; appendChild accepts a single node. Prefer append.
4. classList
const box = document.querySelector(".box");
box.classList.add("selected");
box.classList.remove("selected");
box.classList.toggle("selected"); // add if missing, remove if present
box.classList.contains("selected"); // true / false
Never write element.className = "selected" unless you intend to delete every other class on the element.
5. dataset — data on the element itself
<button class="swatch" data-color="#FF5733" data-id="42">#FF5733</button>
button.addEventListener("click", () => {
console.log(button.dataset.color); // "#FF5733"
console.log(button.dataset.id); // "42"
});
data-* attributes let you keep per-element state in the markup instead of in parallel arrays. Values are always strings.
6. A complete pattern: render from data
const people = [
{ name: "Aarav", role: "Frontend" },
{ name: "Meera", role: "Backend" },
];
const list = document.querySelector("#people");
list.replaceChildren(); // clear without innerHTML
for (const person of people) {
const card = document.createElement("article");
card.className = "person";
const name = document.createElement("h3");
name.textContent = person.name;
const role = document.createElement("p");
role.textContent = person.role;
card.append(name, role);
list.append(card);
}
This is the same loop React will generate for you later — it is worth doing once by hand.
Common mistakes
- Forgetting that the script runs before the element exists. Place the script at the end of
<body>or usedefer. - Using
innerHTMLwith anything a user typed. - Re-querying the DOM inside a long loop. Query once, reuse the reference.
Practice
Build the colour palette generator described in the assignment below. It combines every idea in this lesson: creating elements, storing the HEX value in dataset, toggling a selected class and removing nodes.