The DOM
Learn how the browser turns your HTML into a live tree of objects that JavaScript can read, update, and style on the fly.
You have written HTML. You have styled it with CSS. But so far your pages sit still — same text, same colours, every single load. JavaScript changes that. The moment a browser parses your HTML, it converts every element, every piece of text, and every attribute into a live object in memory. That living data structure is called the Document Object Model, or the DOM. Once you can reach into the DOM with JavaScript, your pages can respond to users, update content without reloading, and come alive in ways that plain HTML never could.
Think of a family tree
Your HTML is a family tree. <html> is the great-grandparent at the top. Inside it are two children: <head> and <body>. Inside <body> there might be an <h1>, a <p>, a <ul> with several <li> children, and so on. Every element is a node — a box in that tree — and JavaScript can walk up, down, or sideways through the whole family. When you write document in JavaScript, you hold a reference to the root of this tree — the entire page.
#Finding Elements: querySelector and querySelectorAll
// querySelector returns the FIRST match
const title = document.querySelector('#title');
console.log(title.tagName); // "H1"
// querySelectorAll returns ALL matches as a NodeList
const items = document.querySelectorAll('.item');
console.log(items.length); // 3#Reading and Changing Text
const title = document.querySelector('#title');
console.log(title.textContent); // "Hello, world"
title.textContent = 'Hello, DOM!';
console.log(title.textContent); // "Hello, DOM!"innerHTML: powerful, but handle with care
innerHTML lets you set HTML markup as a string — great for inserting <strong>bold</strong> or entire chunks of structure. But never put user-supplied text into innerHTML without sanitising it first. A malicious string could inject a <script> tag and run arbitrary code. When you only need text, always prefer textContent. Use innerHTML only when you need real HTML and you fully control the content.
#Toggling Classes with classList
const card = document.querySelector('.card');
card.classList.add('highlight'); // add a class
card.classList.remove('highlight'); // remove it
card.classList.toggle('highlight'); // add if absent, remove if present
console.log(card.classList.contains('highlight')); // true or false#Reading and Changing Attributes
const link = document.querySelector('a');
console.log(link.getAttribute('href')); // "https://example.com"
link.setAttribute('href', 'https://mozilla.org');
link.setAttribute('target', '_blank'); // open in new tab#Looping Over Multiple Elements
const items = document.querySelectorAll('.item');
// NodeList supports forEach just like an array
items.forEach((item, index) => {
item.textContent = `${index + 1}. ${item.textContent}`;
});
// Produces: "1. Apples", "2. Bananas", "3. Cherries"You want to add the CSS class 'active' to a button if it does not already have it, and remove it if it does — in a single call. Which method does this?
Key takeaways
- The DOM is the browser's live, in-memory representation of your HTML as a tree of nodes — JavaScript can read and change it at any time.
- document.querySelector(selector) finds the first match; querySelectorAll finds all matches, using the same syntax as CSS selectors.
- Use textContent to safely read or replace text; use innerHTML only when you need real HTML markup and you control the content.
- classList.add / remove / toggle keeps visual logic in CSS — JavaScript just switches classes on and off.
- getAttribute and setAttribute let you read and update any HTML attribute, from href and src to disabled and custom data attributes.
The lesson shows that querySelectorAll returns a NodeList of every match, and that forEach visits each one in order. Given this list, what gets logged?
// HTML: <ul>
// <li class="item">Apples</li>
// <li class="item">Bananas</li>
// <li class="item">Cherries</li>
// </ul>
const items = document.querySelectorAll('.item');
console.log(items.length);
console.log(items[0].textContent);This code is meant to add a 'highlight' class to the card without disturbing its existing classes. It has a bug — what's wrong?
// HTML: <div class="card rounded"></div>
const card = document.querySelector('.card');
card.setAttribute('class', 'highlight');
console.log(card.className); // wanted: "card rounded highlight"Complete this snippet. Select the single element whose id is 'title', then safely replace its plain text with 'Hello, DOM!'. Fill in the method that finds the first match and the property that reads or replaces text.
const title = document.('#title'); title. = 'Hello, DOM!';
Put these lines in order to select an anchor, swap its href to a new URL, and then read the new value back. The steps should run in a sensible top-to-bottom sequence.
link.setAttribute('href', 'https://mozilla.org');console.log(link.getAttribute('href'));link.setAttribute('target', '_blank');console.log(link.getAttribute('href'));const link = document.querySelector('a');You have a profile card with a name heading, a bio paragraph, and an avatar image. Write JavaScript that: (1) changes the heading text to your own name, (2) adds the CSS class 'featured' to the card element, (3) updates the image's alt attribute to describe the avatar, and (4) loops over all paragraph elements inside the card and prepends the text 'Bio: ' to each one.
Try it live — edit the code and hit Run to see the output: