The DOM: the live page your script can change
Your HTML is a file. Once a browser loads it, it becomes something alive: a tree of objects a script can search and change. That living tree is the DOM.
The big idea
The DOM is the browser's live model of the page; a script finds an element in it, then changes that element's text, classes, or attributes.
See it in code
document.querySelector finds the first element matching a selector. #mission-count means "the element whose id is mission-count." Setting textContent replaces its text:
<section class="p-6">
<h2 class="text-2xl font-bold">Missions</h2>
<p id="mission-count" class="text-sm font-semibold text-violet-300"></p>
</section>
<script>
const count = document.querySelector("#mission-count");
count.textContent = "5 missions and counting";
</script>The paragraph is empty in the HTML. The text you see was written by the script after the page loaded. Find, then change: that's the whole pattern.
Styling lives in classes, so changing how something looks means changing its class list. classList.add and classList.remove do exactly that:
<section class="p-6">
<h1 id="title" class="text-5xl font-extrabold">COSMOS</h1>
<p id="credit" class="mt-3 hidden text-sm text-slate-400">Images and data courtesy of NASA</p>
</section>
<script>
const title = document.querySelector("#title");
const credit = document.querySelector("#credit");
title.classList.add("text-sky-300");
credit.classList.remove("hidden");
</script>The note starts with hidden in its class list. Removing that one class is all it takes to show it. No element was created or deleted.
querySelectorAll returns every match. forEach then runs the same function on each one, receiving the element and its position:
<section class="p-6">
<p id="summary" class="text-sm font-semibold text-violet-300"></p>
<ul id="mission-list" class="mt-3 space-y-2">
<li class="flex gap-3"><span class="font-bold text-violet-300"></span> Perseverance</li>
<li class="flex gap-3"><span class="font-bold text-violet-300"></span> Webb</li>
<li class="flex gap-3"><span class="font-bold text-violet-300"></span> Juno</li>
</ul>
</section>
<script>
const items = document.querySelectorAll("#mission-list li");
items.forEach((item, index) => {
item.querySelector("span").textContent = index + 1;
});
document.querySelector("#summary").textContent = items.length + " missions on this page";
</script>Add a fourth li and it gets its number, and the summary updates, with no change to the script. Code written against "all matches" keeps working as the page grows.
A tree of objects you can query and modify is a pattern well beyond browsers. A file system, a JSON document, and a game's scene are all walked and edited the same way.
Try it yourself
Use classList.toggle("hidden") on the credit note in place of remove, and reload a few times. Then make every list item's text sky blue with one forEach.
The common mistake
Running a script before the element exists. A script can only find elements that come before it in the file, so a <script> at the top returns null. Put scripts at the end.
What it unlocks
Changing the page once is only the start. To change it when the visitor does something, listen for events. To generate whole sections, see building pages from data.