7.3 — Reading & changing the DOM
You can find any element (7.2). Now you get to touch them — and this is the moment JavaScript stops being a console language: every mutation you make to the tree appears on screen, instantly. Change a text node — the page changes. Add a class — the styling flips. Append a node — content EXISTS where there was none.
Four verbs run the show: read/write text, juggle classes, set attributes, create/remove nodes.
Plus one security landmine (innerHTML) that every beginner must meet before production meets them.
const title = document.querySelector("h1");
console.log(title.textContent); // read
title.textContent = "Shopping"; // write (safe)
title.classList.add("big");
title.classList.toggle("dark");
title.setAttribute("data-test", "hdr");
const li = document.createElement("li");
li.textContent = "buy tea";
document.querySelector("ul").append(li);
li.remove();title.textContent reads the words inside the element — a plain string.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Precision on the pair everyone confuses: textContent gets/sets the raw text (fast, safe, no parsing); innerHTML gets/sets markup — reading it serializes the subtree to a string, writing it runs the parser and REPLACES all children.
The security rule is one sentence: if a user typed it, it goes in textContent. XSS — script smuggled through markup — is one of the top web vulnerabilities, and test suites (yours, someday) include cases that try exactly that smuggle.
Every mutation also nudges the rendering pipeline — the tree changed, so layout and paint re-run (lesson 7.8 walks that machinery).
Batches of small edits are why real apps repaint smoothly and also why "the element exists but isn't PAINTED yet" moments happen — the root of flaky-test folklore, and of Playwright's auto-waiting cure (11.5).
And a live reference reminder (7.1 + 4.6): hold const el = querySelector(…) once and mutate it all day — every change lands on the real tree through the same arrow. Playwright's locators behave like these references with retry logic bolted on.
⌨️ implement classList.toggle
The best way to trust a tool is to build its core once. Model an element’s classes as an array and implement the toggle verb — the exact logic behind every dark-mode switch on the web.
requirements:
- An object
elwithclasses=["todo"]. - A function
toggle(el, name): ifnameis present inel.classes, REMOVE it (4.9's filter builds the new array); if absent, ADD it (push). Return nothing — mutate the object (4.6, knowingly!). - Toggle
"done"(→ added), print the classes joined by a space; toggle"done"again (→ removed), print again.
when you press RUN, the console must show exactly:
✏️ Quick check 1
A username typed by a user must be shown on the page. Which property do you assign — textContent or innerHTML?
✏️ Quick check 2
el has class "dark". After el.classList.toggle("dark") — is "dark" present? Type yes or no.
✏️ Quick check 3
createElement("li") makes a node. Is it visible on the page before you append it? Type yes or no.
🗣️ Now teach it back
Explain to a friend the four DOM mutation verbs (text, classes, attributes, create/remove) — and give them the one-sentence security rule about textContent vs innerHTML, with the reason behind it.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.