When a browser loads your HTML, it builds a live tree of objects called the DOM, short for Document Object Model. Every element on the page becomes an object that JavaScript can read and change.
This is where JavaScript meets the page. Everything interactive you'll build, from menus to form checks to to-do lists, starts by finding the right element in the DOM. The good news is that you already know the tool for finding elements: the CSS selectors from the CSS Selectors lesson.
The DOM tree
<body>
<h1>Shop</h1>
<ul id="list">
<li>Apples</li>
<li>Pears</li>
</ul>
</body>To JavaScript, this is a family tree. body has two children, h1 and ul, and the ul has two li children. The whole tree hangs from a single object called document.
The DOM is live. When JavaScript changes it, the page on the screen updates straight away, and when the page changes, the DOM reflects that too.
querySelector and querySelectorAll
document.querySelector(selector)returns the first matching element, ornullif there isn't one.document.querySelectorAll(selector)returns all matching elements as a NodeList, which is empty if nothing matches.
Any CSS selector works: "#cart", ".card", "nav a", "input[type=email]" or "li:last-child". If you can style it, you can select it.
Other ways to select and move around
document.getElementById("cart")selects by id. Note that you don't write the#.element.querySelector(…)searches inside one element instead of the whole page.element.closest(".card")walks up the tree to the nearest matching ancestor, which is very useful with events.element.parentElement,element.childrenandelement.nextElementSiblingmove around the tree from an element you already have.
NodeLists vs arrays
A NodeList looks like an array. It has .length and .forEach(), but not map or filter. Convert it when you need those methods: [...nodeList] or Array.from(nodeList).
When the result is null
If querySelector finds nothing, it returns null, and trying to use the result throws an error such as Cannot read properties of null. The usual causes are a typo in the selector, a missing # or ., or a script that runs before the HTML has loaded (see Where to Put JavaScript). Logging the result straight after selecting is a quick way to check.
DOM Selecting Syntax
Selecting elements
const title = document.querySelector("h1");
const items = document.querySelectorAll(".item");
const cart = document.getElementById("cart");
const card = button.closest(".card");
const first = list.querySelector("li");DOM Selecting Summary
- The DOM is a live tree of objects that represents the page.
querySelectorfinds the first match;querySelectorAllfinds every match.- Any CSS selector can be used to find elements.
closest,parentElementandchildrenmove around the tree.- A
nullresult usually means a selector typo or a script that ran too early.
DOM Selecting Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Finding things on a page
<h1 class="title">Plant Shop</h1>
<ul id="plants">
<li class="plant">Fern</li>
<li class="plant sale">Cactus</li>
<li class="plant">Monstera</li>
</ul>
<p class="note">Free delivery over £30</p>const heading = document.querySelector(".title");
const firstPlant = document.querySelector(".plant");
const allPlants = document.querySelectorAll(".plant");
const onSale = document.querySelector(".plant.sale");
const list = document.getElementById("plants");
const missing = document.querySelector(".nope");
console.log(heading.textContent);
console.log(firstPlant.textContent);
console.log(allPlants.length, "plants");
console.log(onSale.textContent);
console.log(list.children.length);
console.log(missing);Line-by-line explanation
- Line 1The first element with class
title. - Line 2Only the first match.
- Line 3Every match, as a NodeList of 3.
- Line 4Combined class selector, just like CSS.
- Line 5By id. Note that there's no
#. - Line 6No match returns
null.
Looping over many elements
<ul>
<li>Bread: £1.20</li>
<li>Cheese: £3.50</li>
<li>Grapes: £2.10</li>
</ul>const items = document.querySelectorAll("li");
items.forEach((li, i) => {
console.log(i + 1, li.textContent);
});
const texts = [...items].map(li => li.textContent.split(": ")[0]);
console.log(texts);Line-by-line explanation
- Lines 3 to 5NodeLists have
forEach. - Line 7Spread into a real array to use
map.
Walking the tree with closest
<article class="card" data-id="42">
<h3>Wireless Headphones</h3>
<button class="buy">Buy</button>
</article>const button = document.querySelector(".buy");
const card = button.closest(".card");
const title = card.querySelector("h3");
console.log(card.dataset.id);
console.log(title.textContent);
console.log(button.parentElement === card);Line-by-line explanation
- Line 3From the button, walk up to the card that contains it.
- Line 4Then search inside just that card.
- Line 6
data-idattributes are available asdataset.id. - Line 8The button's parent is the card.
Common DOM Selecting Mistakes
Forgetting the # or . in querySelector
document.querySelector("cart"); // looks for a <cart> element
document.getElementById("#cart"); // looks for id="#cart"document.querySelector("#cart");
document.getElementById("cart");querySelector needs a full CSS selector; getElementById needs just the id.
Using a NodeList as if it were one element
document.querySelectorAll(".tab").classList.add("active"); // TypeErrordocument.querySelectorAll(".tab").forEach(tab => tab.classList.add("active"));Selecting before the element exists
// script in <head> without defer
document.querySelector("#app").textContent = "Hi"; // null!<script src="app.js" defer></script>DOM Selecting in Real Projects
Selecting is step one of everything
Every interactive feature starts the same way: find the button, the form, the list or the modal. Testing tools (like Playwright) and browser extensions use these exact selectors to find elements too.
DOM Selecting Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Select the first .card and all .card elements.
const one = document.(".card");
const all = document.(".card");
querySelector for one, querySelectorAll for all.What does document.querySelector(".missing") return when nothing matches?
You're inside a click handler with the clicked button. How do you get the .product that contains it?
closest walks up the tree.The code should log the number of .task items (3) and the text of the item with id urgent. Fix the selectors.
<ul>
<li class="task">Email Sam</li>
<li class="task" id="urgent">Pay rent</li>
<li class="task">Water plants</li>
</ul>const tasks = document.querySelector("task");
const urgent = document.getElementById("#urgent");
console.log(tasks.length);
console.log(urgent.textContent);- Logs 3
- Logs "Pay rent"
- No errors
DOM Selecting Quiz
3 questions to check what you learned.
What does DOM stand for?
- Document Object Model
- Data Object Manager
- Digital Output Mode
- Document Order Map
Which method do NodeLists not have?
- forEach
- map
- length
- item
How do you search only inside a specific element
panel?document.querySelector(panel, "p")panel.find("p")panel.querySelector("p")document.panel("p")
DOM Selecting Challenge
Inventory report
Using DOM selection only:
- Count all
.itemelements and logItems: 4 - Count the ones that also have
.lowand logLow stock: 2 - Log the text of the last item using a
:last-childselector
<ul id="stock">
<li class="item">Flour</li>
<li class="item low">Sugar</li>
<li class="item">Eggs</li>
<li class="item low">Butter</li>
</ul>- Logs "Items: 4"
- Logs "Low stock: 2"
- Logs "Butter"
- Uses querySelectorAll