The DOM: a page that can be changed
Why:A browser keeps the page as a tree, and JavaScript can search it, read it and change it. Afterwards you’ll find elements with selectors, create new ones from data, and understand why other people’s text must never go in through innerHTML.
25 minutes
Read by speech synthesis (Qwen3-TTS); the voice is not a person’s.
The DOM is the document object model: the tree of nodes a browser builds from HTML. JavaScript finds elements in it, changes them and creates new ones.
A page you can touch
In the lesson on rendering you saw that the browser reads HTML and builds a DOM (Document Object Model) from it: a tree in which every tag, every piece of text and every attribute becomes a node. The page on screen is drawn from this tree. What matters for us: the browser hands the tree to JavaScript. In a script it’s available under the name document, and whatever the script changes in it, the browser redraws at once.
Every kind of interactivity works this way: a menu opens because a script removed the hidden attribute from an element; a counter grows because a script wrote a new number into an element; a list appears because a script created an element for each item and put it on the page. Three actions: find, read or change, create and insert.
Finding an element
The language has two commands for searching, and they take the same selectors you wrote in CSS: h3, .card, #offer, nav a.
const first = document.querySelector('.card'); // the first match, or null
const all = document.querySelectorAll('#offer .card'); // every match querySelector returns one element, or null if nothing was found. querySelectorAll returns a list of nodes (a NodeList), possibly empty. You can walk it with forEach or for...of, and to use map and filter you turn it into an array: Array.from(all).
From a found element you can search further: card.querySelector('h3') looks only inside that card. That lets you write shorter selectors, and the script comes out tidier.
Try selectors on a small page, and what you find is highlighted in both the markup and the tree:
Nothing found. querySelector would return null, and querySelectorAll an empty list.
Check yourself: what does querySelector return if there’s no such element?
null. So when an element may be missing, check the result, or the next line, such as el.textContent = ..., will fail with an error.
Reading and changing
Once an element is found, it has properties and methods:
textContentreads and writes the element’s text:title.textContent = 'New title';getAttribute('href')andsetAttribute('href', '#top')work with attributes. Many attributes also have short properties:link.href,input.value,el.id,el.hidden;classList.add('selected'),classList.remove('selected')andclassList.toggle('selected')manage classes. This is the best way to change how something looks: the script attaches a class, and CSS knows what to do with it. The styling stays in CSS and the logic in JS;datasetgives access to attributes likedata-price="1200":card.dataset.price. That’s how markup passes its own data to a script.
Creating and inserting
To add something new, you first create an element in memory, set it up, and only then put it in the tree:
const row = document.createElement('li'); // not on the page yet
row.textContent = 'A new item';
list.append(row); // now it is Other commands you’ll need: prepend (to the start), remove() (take an element away) and replaceChildren(...) (replace all the contents). If you insert many elements in a row, build them in memory and insert them with a single append with several arguments: the browser then doesn’t have to redraw the page after each one.
Why other people’s text must not go in through innerHTML
There’s a shorter way too: the innerHTML property. You write a string of markup into it, and the browser parses it:
box.innerHTML = '<p>Hello, ' + name + '</p>'; It looks convenient, but if name came from a visitor (from a form field, a comment, a server’s answer, the page’s address), it may not be a name at all, but markup with an event handler. The browser sees no difference between “your” markup and “someone else’s”: it parses everything and runs the stranger’s code in the name of your site. This vulnerability is called XSS (cross-site scripting). It’s how sessions are stolen and pages are replaced.
The rule is simple: other people’s text goes in through textContent. It writes the string as text, the characters < and > stay ordinary letters, and nothing runs. Keep innerHTML for markup that you wrote in full yourself.
Try the same string both ways:
text “<img src=x onerror=alert(1)>Anna”
Safe. One string of text; on the page it appears as plain letters.
No tags here. But it is not the string that protects you, it is the habit: other people’s text goes through textContent.
None of this is run: the string is parsed in an isolated document that is never inserted anywhere.
When a script runs
A mistake everyone trips over: a script looks for an element, and it isn’t there yet. An ordinary <script> stops the parsing of the page and runs at once, so everything lower in the document is still invisible to it. There are three ways out: put the script at the very end of the page, give it the defer attribute, or make it a module, as in our editor (type="module"). A module runs after the page is parsed, and you can search for anything in it.
Practice
A 2014 site. You already have a list of cards, but on a long page they can’t all be seen on the first screen. Let a script build a quick jump from the cards: a link to every offer at the top of the section, plus a counter.
The script invents nothing, it reads what is in the markup: it finds the cards, takes the heading of each and makes a link to it. Add one more card to the markup, and a link appears by itself. And if there are no cards, the visitor sees a clear message rather than an empty space.
Notice what is missing from the task: the word innerHTML. A card’s heading can contain anything, so everything goes through textContent.
What was wrong with the old DOM
There was a time when the DOM differed from browser to browser: Internet Explorer had some methods, Netscape had others, and document.all and document.layers had to be guessed at. A standard appeared in the late 1990s, and the browsers were slow to follow it. That confusion is why the jQuery library was born in 2006: one function, $('.card'), that worked the same in every browser. Millions of sites were built on it, and many still run on it, so if you read someone else’s code and see a dollar sign, you’re looking at jQuery.
Today browsers behave alike, the standard is called the DOM Standard (WHATWG), and querySelector, classList and append do the jobs for which a library used to be added. New sites don’t need jQuery: it weighs tens of kilobytes and solves problems that no longer exist. What stayed right is something else: find elements with selectors, change classes rather than styles, and never trust other people’s text.
Practice
Preparing your page…
Test
Question 1 of 7
Cards
These cards go into your review once you finish the lesson.
What is the DOM?
Answer
The tree of objects a browser builds from HTML. JavaScript gets it as document and can search, read and change the nodes. The page on screen is redrawn to follow.
How does querySelector differ from querySelectorAll?
Answer
querySelector finds the first matching element, or null. querySelectorAll returns a list of all the matches (possibly empty). The selectors are the same as in CSS.
How do you add a new element to a page?
Answer
Create it with createElement, set it up (textContent, attributes, classList) and put it in the tree with append. Until it is inserted, it isn’t on the page.
Why must other people’s text not go in through innerHTML?
Answer
innerHTML parses a string as markup. If it holds a tag with a handler, a stranger’s code runs on your page (XSS). textContent shows text as text.
What does querySelectorAll return, and what do you do with it?
Answer
A NodeList: a list you can walk with forEach or for...of. To use map and filter, turn it into an array first: Array.from(list).
Sources
- Document Object Model (MDN) CC BY-SA, in English
- Document.querySelector (MDN) CC BY-SA, in English
- DOM Standard (WHATWG) CC BY, in English
- Document (The Modern JavaScript Tutorial) link only, in English
- OWASP: DOM based XSS Prevention Cheat Sheet CC BY-SA, in English
Videos
JavaScript DOM Manipulation: full course for beginners (freeCodeCamp.org)
The video loads from youtube-nocookie.com only when you press play.
Next
You now know: the DOM.