What is the DOM?

The Document Object Model (DOM) is a programming interface for web pages. When a browser loads an HTML page, it creates a tree-like structure of objects representing every element on the page. JavaScript can interact with this tree to read, modify, add, or remove elements in real time.

Think of the DOM as a live blueprint of your webpage. Every HTML tag becomes a "node" in the tree, and JavaScript gives you the tools to manipulate any node you want.

<!DOCTYPE html>
<html>
  <head>
    <title>My Page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to my site.</p>
  </body>
</html>

// DOM tree structure:
// document
//   └── html
//       ├── head
//       │   └── title ("My Page")
//       └── body
//           ├── h1 ("Hello")
//           └── p ("Welcome to my site.")

The document object is the entry point to the DOM. Every method you use to find or modify elements starts from document.

Selecting Elements

Before you can change anything on a page, you need to select the element you want to work with. JavaScript provides several methods for finding elements in the DOM.

getElementById

Selects a single element by its id attribute. Returns null if no match is found.

// HTML: <h1 id="main-title">Welcome</h1>

const title = document.getElementById("main-title");
console.log(title.textContent); // "Welcome"

querySelector

Selects the first element that matches a CSS selector. This is the most versatile selection method.

// Select by ID
const title = document.querySelector("#main-title");

// Select by class
const firstCard = document.querySelector(".card");

// Select by tag
const firstParagraph = document.querySelector("p");

// Complex selectors work too
const navLink = document.querySelector("nav a.active");
const input = document.querySelector('input[type="email"]');

querySelectorAll

Selects all elements matching a CSS selector. Returns a NodeList (similar to an array).

const allCards = document.querySelectorAll(".card");
console.log(allCards.length); // Number of matching elements

// Loop through results
allCards.forEach(card => {
    console.log(card.textContent);
});

// You can also use index access
const firstCard = allCards[0];
const lastCard = allCards[allCards.length - 1];
💡
querySelector vs getElementById

querySelector is more flexible because it accepts any CSS selector, while getElementById only works with IDs. However, getElementById is slightly faster in performance-critical situations. For most use cases, querySelector is the preferred modern approach.

Other Selection Methods

// Select by class name (returns HTMLCollection)
const items = document.getElementsByClassName("item");

// Select by tag name (returns HTMLCollection)
const paragraphs = document.getElementsByTagName("p");

// Note: HTMLCollection is live — it updates automatically
// when the DOM changes. NodeList from querySelectorAll is static.

Modifying Content

Once you have selected an element, you can read or change its content.

textContent

Gets or sets the text content of an element, ignoring any HTML tags:

// HTML: <p id="info">Hello <strong>world</strong></p>

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

// Reading
console.log(info.textContent); // "Hello world"

// Writing — replaces everything with plain text
info.textContent = "Goodbye world";
// Result: <p id="info">Goodbye world</p>

innerHTML

Gets or sets the HTML content of an element, including tags:

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

// Reading
console.log(info.innerHTML); // "Hello <strong>world</strong>"

// Writing — parses HTML tags
info.innerHTML = "Goodbye <em>world</em>";
// Result: <p id="info">Goodbye <em>world</em></p>
⚠️
innerHTML and XSS Security Risk

Never insert user-provided data directly into innerHTML. If a user submits text containing <script> tags or other malicious HTML, it will be executed on the page. This is called a Cross-Site Scripting (XSS) attack. Use textContent for user input, or sanitize the HTML first.

Modifying Attributes

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

// Read an attribute
console.log(link.getAttribute("href"));

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

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

// Check if an attribute exists
console.log(link.hasAttribute("href")); // true

// Common shorthand properties
const img = document.querySelector("img");
img.src = "/images/photo.jpg";
img.alt = "A beautiful photo";

Changing Styles and Classes

Inline Styles

Use the style property to set inline CSS on an element:

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

// Set individual styles (use camelCase for CSS properties)
box.style.backgroundColor = "#1a1a2e";
box.style.color = "#00ff88";
box.style.padding = "20px";
box.style.borderRadius = "8px";
box.style.fontSize = "18px";

// Read a style
console.log(box.style.color); // "#00ff88"

CSS Classes (Preferred Approach)

Instead of setting styles directly, it is better to add or remove CSS classes. This keeps your styling in CSS files and your logic in JavaScript.

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

// Add a class
button.classList.add("active");

// Remove a class
button.classList.remove("active");

// Toggle a class (add if missing, remove if present)
button.classList.toggle("active");

// Check if a class exists
if (button.classList.contains("active")) {
    console.log("Button is active");
}

// Replace one class with another
button.classList.replace("btn-primary", "btn-secondary");

// Add multiple classes
button.classList.add("rounded", "shadow", "animated");

Creating and Removing Elements

Creating Elements

Use document.createElement() to build new elements from scratch:

// Create a new paragraph
const newParagraph = document.createElement("p");
newParagraph.textContent = "This paragraph was created by JavaScript!";
newParagraph.classList.add("dynamic-text");

// Create a new link
const newLink = document.createElement("a");
newLink.href = "https://example.com";
newLink.textContent = "Visit Example";
newLink.setAttribute("target", "_blank");

Adding Elements to the Page

const container = document.querySelector("#content");

// Add as the last child
container.appendChild(newParagraph);

// Add as the first child
container.prepend(newLink);

// Insert before a specific element
const reference = document.querySelector("#reference-element");
container.insertBefore(newParagraph, reference);

// Modern methods with more flexibility
container.append(newParagraph, newLink);       // Add multiple at end
container.before(newParagraph);                // Add before the container itself
container.after(newParagraph);                 // Add after the container itself

// Insert HTML at specific positions
container.insertAdjacentHTML("beforebegin", "<p>Before container</p>");
container.insertAdjacentHTML("afterbegin", "<p>First child</p>");
container.insertAdjacentHTML("beforeend", "<p>Last child</p>");
container.insertAdjacentHTML("afterend", "<p>After container</p>");

Removing Elements

// Modern way: element removes itself
const oldElement = document.querySelector("#old-item");
oldElement.remove();

// Classic way: parent removes child
const parent = document.querySelector("#list");
const child = document.querySelector("#list-item-3");
parent.removeChild(child);

// Remove all children from an element
const container = document.querySelector("#content");
container.innerHTML = ""; // Quick but destroys event listeners

// Safer way to remove all children
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

Cloning Elements

const original = document.querySelector(".card");

// Shallow clone (element only, no children)
const shallowCopy = original.cloneNode(false);

// Deep clone (element + all children)
const deepCopy = original.cloneNode(true);

// Add the clone to the page
document.querySelector("#card-list").appendChild(deepCopy);

Traversing the DOM

DOM traversal means navigating between elements using their relationships — parent, child, and sibling connections.

// Given this HTML:
// <ul id="menu">
//   <li>Home</li>
//   <li class="active">About</li>
//   <li>Contact</li>
// </ul>

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

// Parent
console.log(active.parentElement);          // <ul id="menu">

// Children (from the parent)
const menu = document.querySelector("#menu");
console.log(menu.children);                 // HTMLCollection of 3 <li> elements
console.log(menu.children[0]);              // First <li> (Home)
console.log(menu.firstElementChild);        // First <li> (Home)
console.log(menu.lastElementChild);         // Last <li> (Contact)

// Siblings
console.log(active.previousElementSibling); // <li>Home</li>
console.log(active.nextElementSibling);     // <li>Contact</li>

// Closest ancestor matching a selector
const listItem = document.querySelector("li");
const closestList = listItem.closest("ul"); // Finds the nearest <ul> ancestor

Practical Example: Dynamic List

Let us build a complete interactive list application that combines all the DOM techniques covered in this tutorial.

// HTML structure:
// <div id="app">
//   <input type="text" id="item-input" placeholder="Add item...">
//   <button id="add-btn">Add</button>
//   <ul id="item-list"></ul>
//   <p id="count">Items: 0</p>
// </div>

// Select elements
const input = document.querySelector("#item-input");
const addBtn = document.querySelector("#add-btn");
const list = document.querySelector("#item-list");
const count = document.querySelector("#count");

// Function to update the item count
function updateCount() {
    const items = list.querySelectorAll("li");
    count.textContent = `Items: ${items.length}`;
}

// Function to add a new item
function addItem() {
    const text = input.value.trim();
    if (text === "") return;

    // Create the list item
    const li = document.createElement("li");
    li.classList.add("list-item");

    // Create the text span
    const span = document.createElement("span");
    span.textContent = text;

    // Create a delete button
    const deleteBtn = document.createElement("button");
    deleteBtn.textContent = "Delete";
    deleteBtn.classList.add("delete-btn");

    // When delete is clicked, remove the item
    deleteBtn.addEventListener("click", function() {
        li.remove();
        updateCount();
    });

    // Assemble and add to list
    li.appendChild(span);
    li.appendChild(deleteBtn);
    list.appendChild(li);

    // Clear input and update count
    input.value = "";
    input.focus();
    updateCount();
}

// Add item on button click
addBtn.addEventListener("click", addItem);

// Also add item when Enter is pressed
input.addEventListener("keydown", function(event) {
    if (event.key === "Enter") {
        addItem();
    }
});

This example demonstrates element selection, creation, modification, event handling, and removal — the core building blocks of any interactive web application.

Summary

  • The DOM is a tree-like representation of an HTML page that JavaScript can manipulate
  • Use querySelector and querySelectorAll to find elements using CSS selectors
  • Modify text with textContent (safe) or innerHTML (renders HTML but has XSS risks)
  • Manage CSS classes with classList.add(), .remove(), and .toggle()
  • Create new elements with document.createElement() and add them with appendChild()
  • Remove elements with element.remove()
  • Traverse the DOM using parentElement, children, and sibling properties
🎉
DOM mastery unlocked!

You can now read, modify, create, and remove any element on a web page. Next up: Event Handling — learn how to make your pages respond to user interactions like clicks, keypresses, and form submissions.