Selecting & Manipulating DOM Elements

Introduction

Now you understand that the DOM converts HTML into JavaScript objects. But knowing this conceptually is different from actually finding and manipulating those objects in your code.

This lesson is entirely practical - we're going to master the essential skills of hunting down HTML elements and changing them. Think of it as learning to be a DOM detective and surgeon rolled into one.

Learning Objectives

By the end of this lesson, you'll be able to:

  • Use CSS selectors to find any element on a page
  • Understand when to use querySelector vs getElementById vs querySelectorAll
  • Navigate between elements using parent/child relationships
  • Change text content, HTML content, and styling
  • Add and remove CSS classes (especially with Tailwind)
  • Create new elements and add them to the page
  • Debug when elements aren't found (null handling)

The Three Ways to Find Elements

Before you can manipulate HTML elements, you need to find them. JavaScript gives you three main methods:

1. getElementById() - Find by ID

Best for: Finding a specific, unique element

<button id="save-button">Save</button> <input id="user-name" type="text" />
const saveBtn = document.getElementById("save-button");
const nameInput = document.getElementById("user-name");

Key points:

  • IDs should be unique on the page
  • No # symbol needed (it's implied)
  • Returns null if not found

2. querySelector() - Find First Match

Best for: Using CSS selectors, finding first match

<div class="card">
  <h2 class="title">First Card</h2>
  <p class="description">Some text</p>
</div>
<div class="card">
  <h2 class="title">Second Card</h2>
</div>
// CSS selectors work just like in CSS
const firstCard = document.querySelector(".card"); // First .card element
const firstTitle = document.querySelector(".title"); // First .title element
const cardTitle = document.querySelector(".card .title"); // .title inside .card
const specificCard = document.querySelector("#special-card"); // By ID

3. querySelectorAll() - Find All Matches

Best for: Working with multiple elements

// Returns a NodeList (like an array) of ALL matches
const allCards = document.querySelectorAll(".card");
const allTitles = document.querySelectorAll(".title");
const allButtons = document.querySelectorAll("button");

// Use forEach to work with each element
allCards.forEach((card) => {
  card.style.border = "1px solid blue";
});

CSS Selector Cheat Sheet

Common selectors you'll use:

// By element type
document.querySelector("button"); // First <button>
document.querySelector("p"); // First <p>

// By class (most common with Tailwind)
document.querySelector(".btn"); // First element with class="btn"
document.querySelector(".text-red-500"); // First element with Tailwind class

// By ID
document.querySelector("#header"); // Element with id="header"

// Combinations
document.querySelector("button.primary"); // <button class="primary">
document.querySelector(".card h2"); // <h2> inside element with class="card"
document.querySelector("#nav .menu"); // .menu inside element with id="nav"

// Attributes
document.querySelector('[type="email"]'); // Element with type="email"
document.querySelector('[data-id="123"]'); // Element with data-id="123"

Changing Element Content

Once you've found an element, you can change what's inside it:

textContent - Change Text Only

const heading = document.querySelector("h1");
heading.textContent = "New heading text";

// Safe from XSS attacks - HTML tags become literal text
heading.textContent = '<script>alert("hack")</script>'; // Shows as text, not code

innerHTML - Change HTML Content

const container = document.querySelector(".content");
container.innerHTML = "<p>New <strong>paragraph</strong> with HTML</p>";

// ⚠️ Be careful - can be dangerous with user input
// Only use with content you control

Practical Example

<div id="user-info">
  <h2>Loading...</h2>
  <p>Please wait</p>
</div>
// Update when data loads
const userInfo = document.getElementById("user-info");
userInfo.innerHTML = `
  <h2>Welcome, ${userName}!</h2>
  <p>Last login: ${lastLogin}</p>
`;

Working with CSS Classes (Tailwind Focus)

Since you're using Tailwind, you'll frequently add/remove CSS classes:

classList Methods

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

// Add classes
button.classList.add("bg-red-500", "text-white", "px-4");

// Remove classes
button.classList.remove("bg-blue-500");

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

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

Practical Tailwind Examples

<div class="card rounded bg-white p-4 shadow">
  <h3>Card Title</h3>
  <button class="btn rounded bg-blue-500 px-4 py-2 text-white">Click me</button>
</div>
const card = document.querySelector(".card");
const button = document.querySelector(".btn");

// Highlight card on hover
card.addEventListener("mouseenter", () => {
  card.classList.add("ring-2", "ring-blue-500");
});

card.addEventListener("mouseleave", () => {
  card.classList.remove("ring-2", "ring-blue-500");
});

// Change button state
button.addEventListener("click", () => {
  button.classList.remove("bg-blue-500");
  button.classList.add("bg-green-500");
  button.textContent = "Clicked!";
});

Creating New Elements

Sometimes you need to create elements dynamically with JavaScript:

Basic Element Creation

// Create element
const newDiv = document.createElement("div");
newDiv.textContent = "Hello from JavaScript!";
newDiv.classList.add("bg-blue-100", "p-4", "rounded");

// Add to page
const container = document.querySelector("#container");
container.appendChild(newDiv);

Building Complex Elements

// Create a card component
function createUserCard(user) {
  const card = document.createElement("div");
  card.classList.add("bg-white", "rounded", "shadow", "p-6");

  card.innerHTML = `
    <h3 class="text-xl font-bold">${user.name}</h3>
    <p class="text-gray-600">${user.email}</p>
    <button class="bg-blue-500 text-white px-4 py-2 rounded mt-4">
      Contact ${user.name}
    </button>
  `;

  return card;
}

// Use it
const users = [{ name: "Sarah", email: "sarah@email.com" }];
const userList = document.querySelector("#user-list");

users.forEach((user) => {
  const card = createUserCard(user);
  userList.appendChild(card);
});

Debugging: When Elements Aren't Found

Common issue: document.querySelector() returns null

Safe Element Selection

// ❌ Dangerous - will crash if element doesn't exist
const button = document.querySelector(".my-button");
button.addEventListener("click", handleClick); // Error if button is null

// ✅ Safe - check before using
const button = document.querySelector(".my-button");
if (button) {
  button.addEventListener("click", handleClick);
} else {
  console.log("Button not found!");
}

// ✅ Alternative - optional chaining (modern browsers)
button?.addEventListener("click", handleClick);

Debugging Tips

// Check if element exists
const element = document.querySelector(".mystery-class");
console.log("Element found:", element); // null if not found

// Count how many match
const elements = document.querySelectorAll(".mystery-class");
console.log("Found", elements.length, "elements");

// Check what classes an element has
const div = document.querySelector("div");
if (div) {
  console.log("Classes:", Array.from(div.classList));
}

Inspecting the DOM with Developer Tools

Viewing the Live DOM

  1. Open any webpage
  2. Press F12 to open Developer Tools
  3. Click the Elements tab

What you see is not your original HTML - it's the live DOM that the browser created.

Key Differences Between Source HTML and DOM

Your HTML Source Code:

<div>
  <p>Hello world</p>
</div>

The Browser's DOM:

<div>
  <p>Hello world</p>
  <!-- Browser auto-closes the tag -->
</div>

The browser fixes errors and fills in missing pieces!

Interactive DOM Exploration

Try this experiment:

  1. Open any webpage
  2. Open Developer Tools (F12)
  3. In the Console tab, type:
    document.body.style.backgroundColor = "lightblue";
    
  4. Watch the page change color
  5. Look at the Elements tab - you'll see the style attribute was added

You just saw JavaScript modify the DOM, and the browser updated the display immediately!

DOM Objects Have Properties and Methods

Each DOM element is a JavaScript object with useful properties and methods:

const heading = document.querySelector("h1");

// Properties (data about the element)
console.log(heading.textContent); // The text inside
console.log(heading.id); // The id attribute
console.log(heading.className); // The class attribute
console.log(heading.tagName); // "H1"

// Methods (things you can do with the element)
heading.addEventListener("click", function () {
  console.log("Heading was clicked!");
});

Common Properties

  • element.textContent - Text inside the element
  • element.innerHTML - HTML inside the element
  • element.id - The id attribute
  • element.className - The class attribute
  • element.style - Inline CSS styles
  • element.children - Child elements
  • element.parentElement - Parent element

Common Methods

  • element.addEventListener() - Listen for events
  • element.appendChild() - Add a child element
  • element.removeChild() - Remove a child element
  • element.querySelector() - Find elements inside this element

Practical DOM Exploration

Let's explore the DOM of a real example:

HTML File (index.html)

JavaScript File (main.js)

What This Demonstrates

  1. JavaScript can inspect DOM elements and read their properties
  2. Elements have relationships (parents, children, siblings)
  3. DOM methods work together to create dynamic content
  4. The DOM is live - changes appear immediately

Homework Assignments

Assignment 1: DOM Detective (Console Practice)

Goal: Practice DOM inspection and manipulation using browser dev tools on real websites.

Instructions:

  1. Visit 3 different websites of your choice (news sites, social media, etc.)
  2. Open Developer Tools (F12) on each site
  3. Complete these tasks using the Console tab:

For each website, do the following:

a) Find and modify text content:

// Find a heading and change its text
document.querySelector("h1").textContent = "I changed this!";

b) Change styling:

// Change the background color of the page
document.body.style.backgroundColor = "lightblue";

c) Inspect element relationships:

// Find an element and explore its family
const element = document.querySelector("p"); // or any element
console.log("Tag name:", element.tagName);
console.log("Parent:", element.parentElement?.tagName);
console.log("Children count:", element.children.length);

Deliverable: Take screenshots showing your console commands and their effects on each website.

Assignment 2: Template Repository Work

Goal: Set up your development environment and create your first DOM manipulation project.

Instructions:

  1. Clone the template repository provided by your instructor
  2. Run npm install to install dependencies (cd into the project folder first)
  3. Start the development server with npm run dev
  4. In a separate terminal instance/window, run code . to open the project in VSCode. Be sure to install any missing dependencies if prompted (see bottom right corner).
  5. Modify the template to create a simple "About Me" page with:
    • Your name in an <h1> tag
    • A paragraph about yourself
    • A button that changes the page background color when clicked
    • A counter that shows how many times the button has been clicked

Requirements:

  • Use external JavaScript file (main.js)
  • Include proper semantic HTML structure
  • Use at least 3 DOM manipulation techniques:
    • getElementById() or querySelector()
    • Changing textContent or innerHTML
    • Adding event listeners
    • Modifying styles with element.style

Commit early, commit often, and push your changes to your GitHub repository so that your instructor can review your work.

Create a short video (3-4 minutes) demonstrating your page and explaining your code. Separate out what AI helped with vs. what you actually learned and coded yourself.

Assignment 3: DOM Reflection

Goal: Personal (not AI generated regurgitation 🤮 of the facts from the lesson) reflection on understanding the DOM and its role in web development.

Instructions: Create a dom-reflection.md file and respond to these prompts:

  1. In your own words, explain the DOM like you're teaching it to someone who's never heard of it.

  2. What was the biggest "aha moment" you had while learning about the DOM?

  3. Think about a website you use frequently - describe what you think is happening "under the hood" with the DOM when you interact with it.

  4. What's the weirdest thing you discovered about how browsers handle HTML vs. the DOM?

  5. What questions do you still have about the DOM that you're curious to explore?

  6. How has learning about the DOM changed how you think about websites and web development?

Deliverable: Submit dom-reflection.md file.

Looking Ahead

Now that you understand what the DOM is and how it works, you're ready to learn how to find and select DOM elements with JavaScript. In our next lesson, we'll master the essential skills of:

  • Using CSS selectors to find elements. Yes, CSS 💄 knowledge is not displaced by Tailwind.
  • Understanding the difference between single elements and NodeLists
  • Navigating element relationships (parents, children, siblings)
  • Writing robust selectors that won't break

Key Takeaways

  1. DOM is not HTML - it's a JavaScript representation of your HTML
  2. Everything is an object - each element has properties and methods
  3. The DOM is live - changes appear immediately on the page
  4. Timing matters - elements must exist before JavaScript can find them
  5. Browser fixes errors - the DOM might look different from your HTML
  6. Developer Tools show the DOM - not your original HTML source

Understanding the DOM is fundamental to all browser JavaScript development. Every time you see document.something, you're working with the DOM!