What the DOM Actually Is
When your browser loads an HTML file, it does not just display the text of that file, it builds a living object in memory called the DOM, the Document Object Model. The DOM is a tree structure representing every element on your page as an object that JavaScript can read and change. This is the critical idea to internalize: HTML is what you write, the DOM is what the browser builds from it, and JavaScript talks to the DOM, not directly to your HTML file.
That distinction is why JavaScript can change what you see on screen instantly, without reloading the page. It is not editing a text file, it is manipulating a live object graph that the browser is continuously rendering to the screen.
Selecting Elements
Before you can change anything, you need to grab a reference to it. The modern, most flexible way to do
this is document.querySelector(), which accepts any CSS selector, exactly
the kind you already know from styling.
<h1 id="title">Welcome</h1>
<button class="cta-button">Click Me</button>
const heading = document.querySelector('#title');
const button = document.querySelector('.cta-button');
console.log(heading.textContent); // "Welcome"
Notice the selectors are identical to CSS syntax, #title for an ID,
.cta-button for a class. If you need every matching element rather than
just the first, use document.querySelectorAll(), which returns a list you
can loop through.
const allButtons = document.querySelectorAll('button');
allButtons.forEach(btn => {
console.log(btn.textContent);
});
Changing Content
Once you have a reference to an element, you can change what is inside it. Two properties handle this, and choosing the right one matters.
const heading = document.querySelector('#title');
heading.textContent = 'Hello, Developer!'; // safe, treats input as plain text
heading.innerHTML = 'Hello, <em>Developer</em>!'; // parses HTML tags
Prefer textContent when you can. innerHTML parses
whatever string you give it as real HTML, which is dangerous if that string ever comes from user input,
it opens the door to injected scripts. Use textContent for plain text and
reach for innerHTML only when you deliberately need to insert markup.
Changing Styles and Classes
You can set inline styles directly, but the better practice is to toggle CSS classes and let your stylesheet own the actual styling.
.highlight {
background: #fff3cd;
border-left: 4px solid #ffc107;
}
const heading = document.querySelector('#title');
heading.classList.add('highlight'); // add a class
heading.classList.remove('highlight'); // remove a class
heading.classList.toggle('highlight'); // add if missing, remove if present
classList.toggle() is what powers the vast majority of dropdown menus,
dark mode switches, and mobile hamburger navigation you interact with daily, including the hamburger menu
on this very site.
Responding to Events
Selecting and changing elements is only half the story, the real power comes from responding to what the
user does. addEventListener() is how you attach behavior to an event, a
click, a key press, a form submission.
<button id="counterBtn">Clicked 0 times</button>
const button = document.querySelector('#counterBtn');
let count = 0;
button.addEventListener('click', () => {
count++;
button.textContent = `Clicked ${count} times`;
});
Every time the button is clicked, the function you passed as the second argument runs. Inside it, the
count variable increases and the button's own text updates to reflect it,
a small but complete example of state changing on screen in response to user action.
Creating New Elements
Beyond editing what already exists, you can build entirely new elements with JavaScript and insert them into the page, exactly how dynamic to-do lists, comment sections, and live search results work.
<ul id="taskList"></ul>
<input id="taskInput" type="text" placeholder="New task">
<button id="addTaskBtn">Add Task</button>
const list = document.querySelector('#taskList');
const input = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addTaskBtn');
addBtn.addEventListener('click', () => {
if (input.value.trim() === '') return;
const item = document.createElement('li');
item.textContent = input.value;
list.appendChild(item);
input.value = ''; // clear the input for the next task
});
This is the complete pattern behind almost every interactive list you have ever used online:
createElement() builds a brand-new element in memory,
textContent fills it with data, and
appendChild() attaches it to the visible page.
A Complete Mini Project: Show and Hide a Panel
Let us combine selection, class toggling, and events into one working feature.
<button id="toggleBtn">Show Details</button>
<div id="panel" class="hidden">
<p>Here are the details you were looking for.</p>
</div>
.hidden { display: none; }
const toggleBtn = document.querySelector('#toggleBtn');
const panel = document.querySelector('#panel');
toggleBtn.addEventListener('click', () => {
panel.classList.toggle('hidden');
toggleBtn.textContent = panel.classList.contains('hidden')
? 'Show Details'
: 'Hide Details';
});
Each click toggles the hidden class, which CSS uses to show or hide the
panel, while the button text updates to match the current state. Nothing here uses a framework, this is
plain JavaScript doing exactly what libraries like React ultimately compile down to under the hood.
Where to Go From Here
You can now select elements, read and change their content, respond to user events, and build new elements dynamically. The next natural step is pulling in real data from outside your page. Read our Fetch API tutorial to learn how to load live data from an API and display it using these same DOM skills.
Ready to go beyond one tutorial?
This lesson is one piece of the full CodeVent Digital frontend curriculum. Start free and build real projects step by step.
Start Learning Free