DOM manipulation is basically how JavaScript talks to the HTML page. We can select elements, change them, add new ones, or remove existing ones — all through JavaScript. Let’s go through each part.
Selecting Elements
These are the methods we use to grab elements from the page.
// By ID — returns a single element
const header = document.getElementById('header');
// By CSS selector — returns the FIRST match
const btn = document.querySelector('.btn-primary');
// By CSS selector — returns ALL matches (NodeList)
const items = document.querySelectorAll('.list-item');
// Loop through all matches
items.forEach(item => console.log(item.textContent));
In simple language, querySelector is like a Swiss army knife — it takes any CSS selector. Use getElementById when you have an ID, and querySelectorAll when you need multiple elements.
Creating Elements
We can create brand new elements from scratch and then add them to the page.
// Create a new element
const div = document.createElement('div');
// Create a text node
const text = document.createTextNode('Hello there!');
// Add the text to the div
div.appendChild(text);
Modifying Elements
Once we have an element, we can change pretty much anything about it.
const card = document.querySelector('.card');
// Change text (safe, no HTML parsing)
card.textContent = 'Updated text';
// Change HTML inside (parses HTML — be careful with user input)
card.innerHTML = '<strong>Bold text</strong>';
// Set attributes
card.setAttribute('data-id', '42');
card.setAttribute('role', 'article');
// classList — add, remove, toggle classes
card.classList.add('active');
card.classList.remove('hidden');
card.classList.toggle('dark-mode'); // adds if missing, removes if present
A quick note — prefer textContent over innerHTML when you’re just setting text. innerHTML parses HTML, which is slower and can be a security risk (XSS) if you’re inserting user input.
Inserting Elements
There are several ways to add elements to the page. The newer methods (append, prepend) are more flexible.
const list = document.querySelector('#todo-list');
const newItem = document.createElement('li');
newItem.textContent = 'New task';
// appendChild — adds at the end (returns the appended node)
list.appendChild(newItem);
// insertBefore — adds before a specific child
const firstItem = list.firstElementChild;
list.insertBefore(newItem, firstItem);
// append — adds at the end (can take strings too)
list.append('Some text', newItem);
// prepend — adds at the beginning
list.prepend(newItem);
The difference between appendChild and append is that append can take multiple arguments and can take plain strings, while appendChild only takes one Node.
Removing Elements
Two ways to remove an element — the modern way is much cleaner.
const item = document.querySelector('.old-item');
// Modern way — call remove() on the element itself
item.remove();
// Old way — ask the parent to remove the child
item.parentNode.removeChild(item);
remove() is supported in all modern browsers. The removeChild approach is the old-school way you’ll see in legacy code, but it does the same thing.