DOM Structure and Selecting Elements
Objectives
By the end of this chapter, you should be able to:
- Explain what the DOM is and how it relates to your HTML
- Select a single element with
document.querySelector() - Select multiple elements with
document.querySelectorAll() - Explain the difference between a live and a static collection of elements
💡 Why this matters: Every DOM lesson after this one assumes you can find the element you want to work with. Selecting elements is step one for all of it.
What Is the DOM?
When a browser loads an HTML page, it doesn’t just display the text you wrote. It builds a live, in-memory tree of objects representing every element on the page, called the DOM (Document Object Model). JavaScript can read and change that tree directly, and the browser redraws the page to match.
Given this HTML:
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
The DOM represents it as a tree: a ul element, with two li elements nested inside it as children. Every tag becomes a node in that tree.
The entry point to this tree from JavaScript is the global document object.
Selecting One Element: document.querySelector()
document.querySelector(selector) returns the first element matching a CSS selector, or null if nothing matches. If you know CSS selectors (#id, .class, tagname), you already know how to use this.
Given this HTML:
<h1 id="title">Welcome</h1>
<p class="intro">First paragraph.</p>
<p class="intro">Second paragraph.</p>
const title = document.querySelector("#title");
console.log(title.textContent);
// Welcome
const firstIntro = document.querySelector(".intro");
console.log(firstIntro.textContent);
// First paragraph.
document.getElementById("title") does the same thing as document.querySelector("#title"), just faster and limited to IDs only. Both are common. This course mostly uses querySelector(), since one function works for any kind of selector.
Selecting Many Elements: document.querySelectorAll()
document.querySelectorAll(selector) returns every matching element, as a NodeList.
const allIntros = document.querySelectorAll(".intro");
console.log(allIntros.length);
// 2
A NodeList looks like an array, and supports .forEach(), but it isn’t actually an array, Array.isArray(allIntros) is false. Most array methods you learned in JavaScript Fundamentals, .map(), .filter(), .reduce(), don’t exist on it directly. .forEach() works, and that covers most cases:
allIntros.forEach((paragraph) => {
console.log(paragraph.textContent);
});
// First paragraph.
// Second paragraph.
Live vs. Static Collections
This is a real gotcha worth knowing early. document.querySelectorAll() returns a static snapshot, it doesn’t update if the page changes later. document.getElementsByClassName() (an older selection method) returns a live collection, it does.
Given this HTML:
<ul id="list">
<li class="item">One</li>
<li class="item">Two</li>
</ul>
const staticList = document.querySelectorAll(".item");
const liveList = document.getElementsByClassName("item");
console.log(staticList.length, liveList.length);
// 2 2
const newItem = document.createElement("li");
newItem.className = "item";
document.getElementById("list").appendChild(newItem);
console.log(staticList.length, liveList.length);
// 2 3
Both started at 2. After adding a third .item to the page, liveList.length updated to 3 automatically, staticList.length stayed at 2, frozen at the moment querySelectorAll() was called. If you need the collection to always reflect the current page, use getElementsByClassName() or re-run querySelectorAll(). Most of the time, the static behavior of querySelectorAll() is exactly what you want, since it doesn’t change out from under you unexpectedly.
Try It
- Given a page with
<h1 id="heading">Hello</h1>, select it withquerySelector()and log its.textContent. - Given three elements with
class="card", usequerySelectorAll()to select all of them and log how many there are. - Loop over the result from exercise 2 with
.forEach()and log each element’s.textContent.
Recap
- The DOM is a live, in-memory tree the browser builds from your HTML. JavaScript reads and changes it through the
documentobject. document.querySelector()selects the first matching element.document.querySelectorAll()selects all of them, as a NodeList.- A NodeList supports
.forEach()but isn’t a true array. querySelectorAll()returns a static snapshot.getElementsByClassName()returns a live collection that updates as the page changes.
Next lesson: creating brand new elements and modifying the ones already on the page.