JavaScript DOM Manipulation: The Practical Guide
Stop reaching for jQuery. Vanilla JS can do everything.
Selecting Elements
// Single element
const el = document.getElementById('myId');
const firstBtn = document.querySelector('.btn'); // CSS selector!
// Multiple elements
const allDivs = document.querySelectorAll('div'); // NodeList (not array!)
const allButtons = document.querySelectorAll('button.primary');
// Convert to real array (for map/filter/reduce)
const buttons = [...document.querySelectorAll('button')];
// or: Array.from(document.querySelectorAll('button'))
Creating Elements
// Create from scratch
const div = document.createElement('div');
div.className = 'card';
div.id = 'card-1';
div.innerHTML = '<h2>Title</h2><p>Content</p>';
div.dataset.userId = '123'; // data-user-id="123"
// Add to page
document.body.appendChild(div);
parentElement.insertBefore(div, referenceElement);
// Create with HTML string (faster for complex structures)
container.insertAdjacentHTML('beforeend', `
<div class="card" id="card-2">
<h2>Another Card</h2>
<p>Created via insertAdjacentHTML</p>
</div>
`);
Modifying Elements
const el = document.querySelector('#myEl');
// Text & HTML
el.textContent = 'Plain text'; // Escapes HTML
el.innerHTML = '<b>Rich text</b>'; // Parses HTML
// Classes
el.classList.add('active', 'visible');
el.classList.remove('hidden');
el.classList.toggle('open');
el.classList.contains('active'); // true/false
// Styles
el.style.color = 'red';
el.style.backgroundColor = '#333';
el.style.cssText = 'color: red; padding: 10px;'; // Set multiple at once
// Attributes
el.setAttribute('data-id', '456');
el.getAttribute('href');
el.removeAttribute('disabled');
// Data attributes
el.dataset.userId; // "123"
el.dataset.config; // JSON.parse if you stored JSON
Traversing the DOM
const card = document.querySelector('.card');
// Parent
card.parentElement;
card.closest('.container'); // Find ancestor matching selector
// Children
card.children; // Element nodes only
card.childNodes; // All nodes (including text, comments)
card.firstElementChild;
card.lastElementChild;
// Siblings
card.nextElementSibling;
card.previousElementSibling;
// Query within element
card.querySelector('.title');
card.querySelectorAll('.item');
Event Handling
// Basic click
document.querySelector('#btn').addEventListener('click', function(e) {
console.log('Clicked!', e.target);
});
// Event delegation (one listener for many children)
document.querySelector('#list').addEventListener('click', function(e) {
if (e.target.matches('li')) {
console.log('Clicked item:', e.target.textContent);
}
});
// Common events
element.addEventListener('input', handler); // Form input change
element.addEventListener('change', handler); // Blur after edit
element.addEventListener('submit', handler); // Form submit
element.addEventListener('keydown', handler); // Key press
element.addEventListener('scroll', handler); // Scroll position
element.addEventListener('resize', handler); // Window resize
// Event object properties
function handleClick(e) {
e.target; // The clicked element
e.currentTarget; // The element with the listener
e.preventDefault(); // Stop default behavior (form submit, link)
e.stopPropagation(); // Stop event from bubbling up
e.key; // Which key was pressed
e.code; // Physical key code ('KeyA', 'Enter')
e.ctrlKey; // Modifier keys
}
Animations
// CSS transitions (preferred — GPU accelerated)
el.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
el.style.transform = 'translateX(100px)';
el.style.opacity = '0';
// Web Animations API (no library needed!)
el.animate([
{ transform: 'translateX(0)', opacity: 1 },
{ transform: 'translateX(100px)', opacity: 0.5 },
{ transform: 'translateX(0)', opacity: 1 }
], {
duration: 500,
iterations: 1,
easing: 'ease-in-out'
});
// Intersection Observer (scroll animations)
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.animate-on-scroll').forEach(el => observer.observe(el));
Common Patterns
Dynamic List Rendering
const users = await fetch('/api/users').then(r => r.json());
const list = document.querySelector('#user-list');
list.innerHTML = users.map(user => `
<li class="user-item" data-id="${user.id}">
<strong>${user.name}</strong>
<span>${user.email}</span>
<button class="delete-btn">Delete</button>
</li>
`).join('');
// Event delegation for dynamic content
list.addEventListener('click', async (e) => {
if (e.target.matches('.delete-btn')) {
const li = e.target.closest('li');
const userId = li.dataset.id;
await fetch(`/api/users/${userId}`, { method: 'DELETE' });
li.remove();
}
});
Form Handling
const form = document.querySelector('#myForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
// Or include disabled fields:
const allData = new FormData(form).entries().reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
try {
const res = await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
showNotification('Saved successfully!', 'success');
form.reset();
} catch (err) {
showNotification(err.message, 'error');
}
});
Copy to Clipboard
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showCopiedFeedback();
} catch (err) {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
}
What's your most-used DOM manipulation pattern?
Follow @armorbreak for more JavaScript content.
Top comments (0)