JavaScript and the DOM
Learn how JavaScript and the DOM work together: select elements, change text and styles, respond to clicks with event listeners, and build a counter. Runnable code, a worked example and a quiz.
Key takeaways
- The DOM is the browser's live, tree-shaped model of your HTML page
- JavaScript selects elements with document.querySelector
- You change content with textContent and styling with the style or classList properties
- addEventListener runs your code in response to clicks and other events
- Together, JavaScript and the DOM turn a static page into an interactive app
From a static page to a living one
HTML describes the structure of a page and CSS styles it, but on their own they are static β the page just sits there. To make things happen when a user clicks, types, or scrolls, you need JavaScript, and JavaScript reaches the page through the DOM.
If you've written a first web page and tried some introduction to JavaScript, you're ready to connect the two and make pages react.
What the DOM is
When a browser loads your HTML, it doesn't just display the text β it builds a Document Object Model: a live, tree-shaped map of every element on the page. Each tag becomes an object (a node) with a parent and possibly children, mirroring how your tags nest.
Consider this HTML:
<body>
<h1>My Page</h1>
<p id="message">Hello</p>
</body>
The browser turns it into a tree: body is the parent, with h1 and p as its children. JavaScript can walk this tree, read any node, and change it β and the page updates the moment you do.
The special object document is your entry point to that tree. Everything starts from document.
Selecting elements
Before you can change an element, you have to find it. The workhorse is document.querySelector(), which takes a CSS selector (the same patterns you use in CSS) and returns the first matching element:
const heading = document.querySelector("h1"); // by tag name
const message = document.querySelector("#message"); // by id (# means id)
const box = document.querySelector(".box"); // by class (. means class)
"h1"matches the first<h1>tag."#message"matchesid="message"β a#always means an id.".box"matchesclass="box"β a.always means a class.
querySelector gives you back the element so you can store it in a variable and work with it. To grab all matches instead of just the first, use querySelectorAll.
Changing content and style
Once you hold an element, you can read or change it through its properties.
To change the text inside, set textContent:
const message = document.querySelector("#message");
message.textContent = "Goodbye!";
This instantly replaces Hello with Goodbye! on the page β no reload needed.
To change styling, you have two routes. For a quick one-off tweak, use the style property:
message.style.color = "red";
message.style.fontSize = "24px";
Note that CSS's font-size becomes fontSize in JavaScript β hyphens turn into camelCase.
The tidier approach for anything beyond a tiny change is to define a class in CSS and toggle it with classList:
message.classList.add("highlight"); // add a CSS class
message.classList.remove("highlight"); // take it away
message.classList.toggle("highlight"); // flip it on/off
This keeps your look in CSS (where it belongs) and lets JavaScript simply decide when to apply it.
Responding to events
The real magic is making code run when the user does something. An action like a click is called an event, and you react to it with addEventListener:
const button = document.querySelector("#myButton");
button.addEventListener("click", function () {
alert("You clicked me!");
});
Line by line:
button.addEventListener(...)attaches a listener to the button."click"is the event we care about. Others include"mouseover","keydown", and"submit".- The
function () { ... }is a callback β code the browser keeps on file and runs each time the event happens. Here it pops up an alert.
The function doesn't run when you write it; it waits, stored, until a click triggers it.
Why script placement matters
JavaScript runs the instant the browser reaches it. If your <script> sits in the <head>, it runs before the page's elements exist, so querySelector finds nothing and returns null. The simple fix is to put your <script> tag at the end of the <body>, after the elements:
<body>
<p id="message">Hello</p>
<script src="app.js"></script>
</body>
Now every element already exists by the time your code runs.
Worked example: a click counter
Let's combine selecting, events and updating into a complete, working mini-app. Save this as a single .html file and open it in a browser.
<!DOCTYPE html>
<html>
<body>
<h1 id="count">0</h1>
<button id="plus">Add one</button>
<button id="reset">Reset</button>
<script>
let total = 0;
const display = document.querySelector("#count");
const plusBtn = document.querySelector("#plus");
const resetBtn = document.querySelector("#reset");
plusBtn.addEventListener("click", function () {
total = total + 1;
display.textContent = total;
});
resetBtn.addEventListener("click", function () {
total = 0;
display.textContent = total;
});
</script>
</body>
</html>
How it works:
- The HTML shows a number (
<h1 id="count">) and two buttons. let total = 0;keeps track of the count in a variable.- We select the heading and both buttons with
querySelector, storing each in aconst. - The first listener fires on every click of "Add one": it increases
totalby 1, then writes the new value into the heading withtextContent. The browser repaints the number instantly. - The second listener resets
totalto 0 and updates the display.
You now have a live, interactive page β and not a single reload in sight. That loop of event β change a variable β update the DOM is the heart of nearly every interactive web feature.
Try it yourself
Extend the counter, or build something new:
- Add a "Subtract one" button that decreases the total.
- Stop the count going below zero by checking with an
ifbefore subtracting. - Use
classListto turn the number red when it goes above 10 and back to black when it drops below. - (Bigger challenge) Add a text box (
<input>) and a button that takes whatever the user typed (read it withinputElement.value) and shows it in a paragraph below.
When your page reacts the way you want, revisit styling web pages with CSS to make those changes look polished, since CSS classes and JavaScript work hand in hand.
Quick quiz
Test yourself and earn XP
What does DOM stand for?
DOM means Document Object Model: the browser's structured, tree-like representation of the page that JavaScript can read and change.
Which selects the first element with class "box"?
A dot means class, so ".box" matches class="box". A hash (#) is for ids, and a bare word matches a tag name.
How do you change the text inside an element stored in `el`?
Setting el.textContent replaces the text inside that element. There is no .text or .write property for this.
What does addEventListener do?
addEventListener attaches a function that the browser calls whenever the chosen event (such as 'click') occurs on that element.
Why put the <script> tag at the end of the <body>?
Scripts run as soon as they're reached. Placing the script after the elements ensures those elements already exist, so querySelector can find them.
FAQ
HTML is the text file you write. When the browser loads it, it builds the DOM: a live, in-memory tree of objects representing every element. HTML is the blueprint; the DOM is the running structure JavaScript reads and edits. Changing the DOM updates what you see instantly, without touching the original file.
No. That's the whole point of DOM manipulation. When your code updates the DOM, the browser repaints the affected part immediately. This is how live counters, form validation, and interactive menus work without ever reloading.
Keep exploring
More in Coding