What you'll be able to do
By the end of this page you should be able to grab an element from the page, change its text, toggle a CSS class, and place your <script> so the HTML exists before your code runs.
Previous post: Set and Map basics — this closes Phase 6. We now work in the browser with the live page.
Who this is for
- People who wrote console-only JS and want the page to react
- Beginners who got
nullfromdocument.querySelectorand did not know why - Anyone who put
<script>in<head>and wondered why the button was “missing”
You can skip this if you already select nodes and update textContent / classList confidently. Come back when script order breaks your first DOM demo.
The DOM in one sentence
The DOM is the browser’s live tree of everything on the page — each tag becomes a node you can reach from JavaScript via document.
Scenario A — change a greeting. HTML shows “Hello.” JS finds the paragraph and replaces the text.
Scenario B — highlight a row. JS adds a class; CSS already knows what .active looks like.
Select by id: getElementById
<p id="status">Waiting…</p>
<script>
const status = document.getElementById("status");
status.textContent = "Ready";
</script>
Scenario A — one unique element. Ids should be unique on the page.
Scenario B — script after the element. The <p> exists before the script runs. If the script were above the <p>, getElementById would return null.
querySelector: CSS-style picks
const firstButton = document.querySelector("button");
const hero = document.querySelector(".hero");
const email = document.querySelector("#email");
Scenario A — first match only. querySelector returns one node or null.
Scenario B — many items. Use querySelectorAll (returns a NodeList) — we use it more in the next post on traversal.
Change text: textContent
const title = document.querySelector("h1");
title.textContent = "New title";
Scenario A — plain text. Safe default; user-visible words only.
Scenario B — need HTML inside? innerHTML exists but treats strings as markup — easy to misuse with user input. Prefer textContent until you know why you need HTML.
Classes: classList
const box = document.querySelector(".box");
box.classList.add("open");
box.classList.remove("hidden");
box.classList.toggle("active");
Scenario A — CSS does the look. JS toggles class names; styles live in CSS.
Scenario B — avoid box.className = "a b c" when you only want to add one class. classList keeps the rest.
Script before HTML: the classic null
<script>
const btn = document.querySelector("#go"); // null if #go is below
</script>
<button id="go">Go</button>
Fix A — move <script> to the end of <body>.
Fix B — wait for DOM ready (later post #48). For now, end-of-body scripts are the honest beginner habit.
A tiny practice file
Save as dom-select.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>DOM select</title>
<style>
.highlight { background: #ffe082; }
</style>
</head>
<body>
<h1 id="title">Hello</h1>
<button id="rename">Rename</button>
<button id="toggle">Toggle highlight</button>
<script>
const title = document.getElementById("title");
document.getElementById("rename").addEventListener("click", function () {
title.textContent = "Hello from JS";
});
document.getElementById("toggle").addEventListener("click", function () {
title.classList.toggle("highlight");
});
</script>
</body>
</html>
Mistakes I see a lot
1. Script in <head> before elements exist.
2. Typo in selector → null → crash on .textContent. Log the node once.
3. Using getElementById("#id") — id form has no** #.
4. Replacing entire className and wiping unrelated classes.
5. innerHTML with user strings — XSS risk; use textContent first.
6. Expecting querySelectorAll to be an array. It is array-like; spread or Array.from when you need array methods.
What to try before the next post
- Change an
h1withtextContent. - Toggle a class that CSS styles.
- Break script order on purpose; fix by moving the script.
- Build
dom-select.html.
Next in this series: DOM traversal — parent, children, and siblings without brittle selectors only.
Try this next outside the series
Browser JavaScript stops feeling isolated once you connect it to forms and component-oriented UI work.
- React forms without pain — see modern form handling after you understand the DOM version first
- HTML to JSX Converter — move tiny HTML snippets into React-style markup when you're ready