JavaScript 61 🧬 DOM manipulation
const heading = document.querySelector('h1');
const boxes = document.querySelectorAll('.box');
const newEl = document.createElement('div');
newEl.textContent = 'Hello';
document.body.appendChild(newEl);
heading.textContent = 'Updated';
heading.innerHTML = '<span>Rich</span>';
heading.classList.add('active');
heading.classList.remove('hidden');
heading.classList.toggle('highlight');
heading.setAttribute('data-id', '42');
console.log(heading.getAttribute('data-id'));
heading.style.color = 'red';
heading.style.fontSize = '24px';
heading.addEventListener('click', () => {
console.log('clicked');
});
const parent = document.querySelector('#container');
parent.removeChild(newEl);
newEl.remove();
const clone = heading.cloneNode(true);
parent.appendChild(clone);
The DOM (Document Object Model) is a tree representation of an HTML page. JavaScript can read it, modify it, add to it, and remove from it — which is how every interactive web page works. document.querySelector, createElement, appendChild, addEventListener — these are the tools.
Key point: The DOM is a live tree. When you change it, the page changes. Every update — even one character — triggers the browser to re-render. Efficient DOM manipulation means batching updates and touching the DOM as little as possible.
a – Selecting elements
Before you can change the DOM, you have to find the elements you want.
The four main methods:
| Method | Returns | Selector |
|---|---|---|
getElementById(id) | One element | ID |
querySelector(sel) | First match | CSS selector |
querySelectorAll(sel) | NodeList | CSS selector |
getElementsByClassName | Live HTMLCollection | Class |
querySelector — the universal picker:
const heading = document.querySelector('h1');
const firstBox = document.querySelector('.box');
const byId = document.querySelector('#main');
const nested = document.querySelector('.card .title');
Takes any valid CSS selector — tags, classes, IDs, attributes, pseudo-classes.
document.querySelector('input[type="email"]');
document.querySelector('ul > li:first-child');
document.querySelector('a[href^="https"]');
querySelectorAll — every match:
const boxes = document.querySelectorAll('.box');
console.log(boxes.length);
// [ 3 ]
boxes.forEach(box => console.log(box.textContent));
Returns a NodeList — not an array, but iterable. Use forEach, spread it, or Array.from:
const arr = [...boxes];
const arr2 = Array.from(boxes);
getElementById — fastest for IDs:
const main = document.getElementById('main');
Slightly faster than querySelector('#main') because it doesn’t parse a selector.
Live vs static collections:
| Method | Type | Live? |
|---|---|---|
querySelectorAll | NodeList | Static |
getElementsByClassName | HTMLCollection | Live |
getElementsByTagName | HTMLCollection | Live |
children | HTMLCollection | Live |
const staticList = document.querySelectorAll('.item');
const liveList = document.getElementsByClassName('item');
// Add a new element
document.body.appendChild(makeItem());
console.log(staticList.length); // original count — unchanged
console.log(liveList.length); // updated — includes new element
Live collections update automatically — useful sometimes, surprising often.
Selecting within an element:
const card = document.querySelector('.card');
const title = card.querySelector('.title');
const buttons = card.querySelectorAll('button');
You can call querySelector on any element to scope the search.
Selecting forms and inputs:
const form = document.querySelector('form');
console.log(form.elements); // all inputs, buttons, selects
console.log(form.name.value); // named input access
Selecting special elements:
document.documentElement; // <html>
document.head; // <head>
document.body; // <body>
document.title; // <title> text
document.forms; // all <form>
document.images; // all <img>
document.links; // all <a href>
Common patterns:
// Get all buttons in a nav
const navButtons = document.querySelectorAll('nav button');
// Find the currently active tab
const activeTab = document.querySelector('.tab.active');
// Find inputs that aren't disabled
const inputs = document.querySelectorAll('input:not([disabled])');
Handling missing elements:
const el = document.querySelector('.missing');
if (el) {
el.textContent = 'found';
}
// Or with optional chaining
el?.classList.add('active');
querySelector returns null if nothing matches. Always guard.
Selecting multiple kinds:
const elements = document.querySelectorAll('h1, h2, h3');
CSS selectors let you match multiple patterns at once.
b – Modifying elements
Once you’ve selected an element, you can change its content, attributes, styles, and classes.
Text content:
const el = document.querySelector('.title');
el.textContent = 'New title';
el.textContent = ''; // clear
textContent sets or gets the plain text — safe from HTML injection.
HTML content:
el.innerHTML = '<strong>Bold</strong>';
innerHTML parses HTML. Powerful but dangerous with user input — XSS injection is real.
// ❌ Never do this with user input
el.innerHTML = userComment;
// ✅ Use textContent for plain text
el.textContent = userComment;
// ✅ Or sanitize
el.innerHTML = DOMPurify.sanitize(userComment);
innerText vs textContent:
| Feature | innerText | textContent |
|---|---|---|
| Reads rendered text | ✅ | ❌ |
| Includes hidden text | ❌ | ✅ |
| Respects CSS | ✅ | ❌ |
| Performance | Slower | Faster |
| Use for | Visible text | Raw content |
el.innerText = 'Visible text';
Prefer textContent for reads and writes unless you specifically need rendered text.
Attributes:
el.setAttribute('data-id', '42');
el.getAttribute('data-id');
el.hasAttribute('data-id');
el.removeAttribute('data-id');
Direct property access:
img.src = '/new.png';
input.value = 'hello';
a.href = 'https://example.com';
checkbox.checked = true;
Most HTML attributes are exposed as properties.
dataset for data-* attributes:
// <div data-user-id="42" data-role="admin">
el.dataset.userId; // "42"
el.dataset.role; // "admin"
el.dataset.userId = '99';
data-user-id becomes dataset.userId — kebab-case to camelCase.
Classes:
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');
el.classList.contains('active');
el.classList.replace('old', 'new');
Full class control:
el.className = 'box active'; // replace all
console.log(el.className); // 'box active'
Styles:
el.style.color = 'red';
el.style.fontSize = '24px';
el.style.backgroundColor = '#f0f0f0';
CSS property names use camelCase for hyphenated ones.
el.style.cssText = 'color: red; font-size: 24px;';
Or use the CSS text format — replaces all inline styles.
Reading computed styles:
const styles = getComputedStyle(el);
console.log(styles.color);
console.log(styles.fontSize);
getComputedStyle returns the final computed value — including from CSS files.
Creating elements:
const div = document.createElement('div');
div.className = 'card';
div.textContent = 'Hello';
const text = document.createTextNode('plain text');
Cloning elements:
const clone = el.cloneNode(true); // deep clone — includes children
const shallow = el.cloneNode(false); // shallow — element only
Removing elements:
el.remove(); // modern
parent.removeChild(el); // older
Replacing elements:
parent.replaceChild(newEl, oldEl);
Inserting elements:
parent.appendChild(child); // at end
parent.insertBefore(child, reference); // before reference
parent.prepend(child); // at start (modern)
parent.append(child); // at end (modern)
reference.before(child); // before reference
reference.after(child); // after reference
Modern insertion methods:
el.append('text', otherEl, anotherEl); // multiple
el.prepend('first');
el.before('before');
el.after('after');
el.replaceWith(newEl);
Bulk DOM updates:
const fragment = document.createDocumentFragment();
for (const item of items) {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
}
ul.appendChild(fragment);
DocumentFragment batches updates — one DOM insertion instead of many.
Templates:
<template id="row">
<tr>
<td class="name"></td>
<td class="email"></td>
</tr>
</template>
const template = document.getElementById('row');
const clone = template.content.cloneNode(true);
clone.querySelector('.name').textContent = 'Alice';
The correct way to update text:
// ❌ Slow — one DOM write per call
el.textContent = 'a';
el.textContent = 'b';
el.textContent = 'c';
// ✅ Fast — single write
el.textContent = 'c';
Batch updates with a fragment:
function renderList(items) {
const frag = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
frag.appendChild(li);
});
list.innerHTML = '';
list.appendChild(frag);
}
c – Events and DOM interaction
Events let your page respond to user actions — clicks, keypresses, scrolling, input.
addEventListener — the standard:
el.addEventListener('click', () => {
console.log('clicked');
});
The event object:
el.addEventListener('click', (event) => {
console.log(event.type); // 'click'
console.log(event.target); // the clicked element
console.log(event.currentTarget);// the element with listener
console.log(event.clientX); // mouse coordinates
});
Common events:
| Category | Events |
|---|---|
| Mouse | click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout |
| Keyboard | keydown, keyup, keypress (deprecated) |
| Form | submit, change, input, focus, blur |
| Document | DOMContentLoaded, load, beforeunload |
| Touch | touchstart, touchend, touchmove |
| Scroll | scroll, wheel |
| Drag | dragstart, drag, dragend, drop |
Removing listeners:
function handleClick() { ... }
el.addEventListener('click', handleClick);
el.removeEventListener('click', handleClick);
You must pass the same function reference to remove.
Once option:
el.addEventListener('click', handler, { once: true });
Fires once, then auto-removes.
Event delegation:
Instead of attaching to each child, attach to a parent:
list.addEventListener('click', (event) => {
if (event.target.matches('.delete-btn')) {
const id = event.target.dataset.id;
deleteItem(id);
}
});
One listener handles all children — efficient and dynamic-proof.
Preventing default:
form.addEventListener('submit', (event) => {
event.preventDefault();
// handle manually
});
Stopping propagation:
el.addEventListener('click', (event) => {
event.stopPropagation();
});
Prevents the event from bubbling to ancestors.
Event phases:
┌──────────────────────────────────────────────┐
│ Capturing (top → down) │
│ │ │
│ ▼ │
│ Target │
│ │ │
│ ▼ │
│ Bubbling (bottom → top) │
└──────────────────────────────────────────────┘
el.addEventListener('click', handler); // bubble (default)
el.addEventListener('click', handler, true); // capture
el.addEventListener('click', handler, { capture: true });
Ready state:
document.addEventListener('DOMContentLoaded', () => {
// DOM ready — safe to query
});
Or use defer on script tags:
<script src="app.js" defer></script>
MutationObserver — watch DOM changes:
const observer = new MutationObserver((mutations) => {
mutations.forEach(m => console.log(m.type));
});
observer.observe(target, {
childList: true,
attributes: true,
subtree: true
});
observer.disconnect();
IntersectionObserver — visibility detection:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
});
observer.observe(el);
ResizeObserver — element size changes:
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
console.log(entry.contentRect.width);
}
});
observer.observe(el);
Common event pattern — form handling:
form.addEventListener('submit', async (event) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(form));
try {
await fetch('/api', {
method: 'POST',
body: JSON.stringify(data)
});
} catch (err) {
console.error(err);
}
});
Common event pattern — input validation:
input.addEventListener('input', (event) => {
const value = event.target.value;
isValid = value.length >= 3;
feedback.textContent = isValid ? '✓' : 'Too short';
});
Common event pattern — keyboard shortcuts:
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') closeModal();
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
save();
}
});
Complete Example Session
// ============================================
// PART 1: SELECT
// ============================================
const heading = document.querySelector('h1');
console.log(heading?.tagName);
// [ 'H1' ]
const boxes = document.querySelectorAll('.box');
console.log(boxes.length);
// [ N ]
// ============================================
// PART 2: CREATE
// ============================================
const newEl = document.createElement('div');
newEl.textContent = 'Hello';
document.body.appendChild(newEl);
// ============================================
// PART 3: TEXT
// ============================================
heading.textContent = 'Updated';
// [ heading now shows "Updated" ]
// ============================================
// PART 4: HTML
// ============================================
heading.innerHTML = '<span>Rich</span>';
// [ heading contains a <span> ]
// ============================================
// PART 5: CLASSES
// ============================================
heading.classList.add('active');
heading.classList.remove('hidden');
heading.classList.toggle('highlight');
// ============================================
// PART 6: ATTRIBUTES
// ============================================
heading.setAttribute('data-id', '42');
console.log(heading.getAttribute('data-id'));
// [ '42' ]
// ============================================
// PART 7: STYLE
// ============================================
heading.style.color = 'red';
heading.style.fontSize = '24px';
// ============================================
// PART 8: EVENTS
// ============================================
heading.addEventListener('click', () => {
console.log('clicked');
});
// ============================================
// PART 9: REMOVE
// ============================================
const parent = document.querySelector('#container');
parent.removeChild(newEl);
newEl.remove();
// ============================================
// PART 10: CLONE
// ============================================
const clone = heading.cloneNode(true);
parent.appendChild(clone);
// ============================================
// PART 11: DATASET
// ============================================
// <div data-user-id="42" data-role="admin">
el.dataset.userId = '99';
console.log(el.dataset.role);
// [ 'admin' ]
// ============================================
// PART 12: QUERY WITHIN
// ============================================
const card = document.querySelector('.card');
const title = card.querySelector('.title');
// ============================================
// PART 13: FRAGMENT
// ============================================
const frag = document.createDocumentFragment();
for (let i = 0; i < 3; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
frag.appendChild(li);
}
document.querySelector('ul').appendChild(frag);
// ============================================
// PART 14: EVENT DELEGATION
// ============================================
list.addEventListener('click', (event) => {
if (event.target.matches('.delete')) {
event.target.closest('li').remove();
}
});
// ============================================
// PART 15: PREVENT DEFAULT
// ============================================
form.addEventListener('submit', (event) => {
event.preventDefault();
});
// ============================================
// PART 16: DOMContentLoaded
// ============================================
document.addEventListener('DOMContentLoaded', () => {
console.log('ready');
});
// ============================================
// PART 17: GETCOMPUTEDSTYLE
// ============================================
const styles = getComputedStyle(heading);
console.log(styles.color);
// [ 'rgb(255, 0, 0)' ]
// ============================================
// PART 18: MUTATIONOBSERVER
// ============================================
const observer = new MutationObserver((mutations) => {
console.log('DOM changed');
});
observer.observe(document.body, { childList: true, subtree: true });
// ============================================
// PART 19: INSERT MODERN
// ============================================
el.before('before');
el.after('after');
el.replaceWith(otherEl);
// ============================================
// PART 20: FULL SCRIPT
// ============================================
const heading61 = document.querySelector('h1');
const boxes61 = document.querySelectorAll('.box');
const newEl61 = document.createElement('div');
newEl61.textContent = 'Hello';
document.body.appendChild(newEl61);
heading61.textContent = 'Updated';
heading61.innerHTML = '<span>Rich</span>';
heading61.classList.add('active');
heading61.classList.remove('hidden');
heading61.classList.toggle('highlight');
heading61.setAttribute('data-id', '42');
console.log(heading61.getAttribute('data-id'));
heading61.style.color = 'red';
heading61.style.fontSize = '24px';
heading61.addEventListener('click', () => {
console.log('clicked');
});
const parent61 = document.querySelector('#container');
parent61.removeChild(newEl61);
newEl61.remove();
const clone61 = heading61.cloneNode(true);
parent61.appendChild(clone61);
Quick Reference
Selecting
| Method | Returns |
|---|---|
getElementById(id) | One element |
querySelector(sel) | First match |
querySelectorAll(sel) | NodeList |
getElementsByClassName(cls) | Live HTMLCollection |
getElementsByTagName(tag) | Live HTMLCollection |
el.closest(sel) | Nearest ancestor |
el.matches(sel) | Boolean |
Content
| Property | Purpose |
|---|---|
textContent | Plain text (safe) |
innerText | Visible text |
innerHTML | HTML (dangerous) |
outerHTML | Element + content |
value | Form input value |
Classes
| Method | Purpose |
|---|---|
classList.add(...) | Add classes |
classList.remove(...) | Remove classes |
classList.toggle(cls) | Toggle |
classList.contains(cls) | Check |
classList.replace(a, b) | Replace |
className | All classes as string |
Attributes
| Method | Purpose |
|---|---|
setAttribute(name, val) | Set |
getAttribute(name) | Get |
hasAttribute(name) | Check |
removeAttribute(name) | Remove |
dataset.* | data-* access |
Styles
| Method | Purpose |
|---|---|
style.prop = val | Inline style |
style.cssText = '...' | Replace all |
getComputedStyle(el) | Final computed |
Creating and Inserting
| Method | Purpose |
|---|---|
createElement(tag) | Create element |
createTextNode(str) | Create text |
createDocumentFragment() | Batch container |
cloneNode(deep) | Clone |
appendChild(node) | Add at end |
prepend(node) | Add at start |
append(...) | Add multiple |
insertBefore(new, ref) | Before reference |
before(node) | Before element |
after(node) | After element |
replaceWith(node) | Replace |
Removing
| Method | Purpose |
|---|---|
el.remove() | Modern |
parent.removeChild(el) | Older |
Events
| Method | Purpose |
|---|---|
addEventListener(type, fn) | Attach |
removeEventListener(type, fn) | Detach |
event.preventDefault() | Stop default |
event.stopPropagation() | Stop bubbling |
event.target | Triggered element |
event.currentTarget | Listener element |
Common Events
| Category | Events |
|---|---|
| Mouse | click, mouseenter, mouseleave |
| Keyboard | keydown, keyup |
| Form | submit, input, change |
| Document | DOMContentLoaded, load |
Observers
| Observer | Purpose |
|---|---|
MutationObserver | DOM changes |
IntersectionObserver | Visibility |
ResizeObserver | Size changes |
Best Practices
✅ Do This:
// Use querySelector for flexibility
const el = document.querySelector('.card'); // ✅
// Use textContent for user data
el.textContent = userInput; // ✅
// Batch DOM updates with fragments
const frag = document.createDocumentFragment(); // ✅
// Use event delegation for lists
list.addEventListener('click', handler); // ✅
// Guard against missing elements
el?.classList.add('active'); // ✅
// Use DOMContentLoaded
document.addEventListener('DOMContentLoaded', fn); // ✅
// Remove listeners to prevent leaks
el.removeEventListener('click', handler); // ✅
// Use classList instead of className
el.classList.add('active'); // ✅
❌ Don’t Do This:
// Don't use innerHTML with user input
el.innerHTML = userInput; // ❌ XSS
// Don't query in a loop
for (let i = 0; i < 1000; i++) {
document.querySelector('.item'); // ❌ slow
}
// Don't attach listeners to every list item
items.forEach(item => item.addEventListener(...)); // ⚠️ use delegation
// Don't update DOM in a loop
for (const x of arr) {
el.textContent += x; // ❌ many reflows
}
// Don't use document.write
document.write('<p>Hello</p>'); // ❌ obsolete
// Don't forget to remove observers
const obs = new MutationObserver(fn);
// obs.disconnect() when done // ✅
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
querySelectorAll in loop | Slow | Cache result |
innerHTML with user input | XSS | Use textContent |
Missing null check | TypeError | Use ?. or if |
| Attaching listeners per item | Many listeners | Event delegation |
| Updating DOM in loop | Reflow storm | Batch with fragment |
| Live vs static confusion | Wrong .length | Know which is which |
| Forgetting to remove | Memory leak | Remove on cleanup |
innerText performance | Slow reads | Use textContent |
Real-World Examples
1. Select and Modify
const h = document.querySelector('h1');
h.textContent = 'Hello';
2. Create and Add
const div = document.createElement('div');
div.textContent = 'New';
document.body.appendChild(div);
3. Add Class
el.classList.add('active');
4. Toggle Class
el.classList.toggle('open');
5. Set Attribute
el.setAttribute('data-id', '42');
6. Set Style
el.style.color = 'red';
7. Add Event Listener
el.addEventListener('click', () => console.log('clicked'));
8. Remove Element
el.remove();
9. Clone
const clone = el.cloneNode(true);
10. Data Attribute
el.dataset.userId = '99';
console.log(el.dataset.userId);
// [ '99' ]
11. Query Within
const card = document.querySelector('.card');
const title = card.querySelector('.title');
12. Document Fragment
const frag = document.createDocumentFragment();
for (const x of items) {
const li = document.createElement('li');
li.textContent = x;
frag.appendChild(li);
}
list.appendChild(frag);
13. Event Delegation
list.addEventListener('click', (e) => {
if (e.target.matches('.delete')) {
e.target.closest('li').remove();
}
});
14. Prevent Default
form.addEventListener('submit', (e) => {
e.preventDefault();
});
15. DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
// ready
});
16. Get Computed Style
const color = getComputedStyle(el).color;
17. Mutation Observer
const obs = new MutationObserver(list => {
console.log(list.length);
});
obs.observe(target, { childList: true });
18. Insert with Modern API
el.before(otherEl);
el.after(otherEl);
19. Replace
oldEl.replaceWith(newEl);
20. Full Script
const heading61 = document.querySelector('h1');
const boxes61 = document.querySelectorAll('.box');
const newEl61 = document.createElement('div');
newEl61.textContent = 'Hello';
document.body.appendChild(newEl61);
heading61.textContent = 'Updated';
heading61.innerHTML = '<span>Rich</span>';
heading61.classList.add('active');
heading61.classList.remove('hidden');
heading61.classList.toggle('highlight');
heading61.setAttribute('data-id', '42');
console.log(heading61.getAttribute('data-id'));
heading61.style.color = 'red';
heading61.style.fontSize = '24px';
heading61.addEventListener('click', () => {
console.log('clicked');
});
const parent61 = document.querySelector('#container');
parent61.removeChild(newEl61);
newEl61.remove();
const clone61 = heading61.cloneNode(true);
parent61.appendChild(clone61);
Visual: DOM Tree
┌──────────────────────────────────────────────┐
│ document │
│ │ │
│ ▼ │
│ html │
│ / \ │
│ head body │
│ │ │ │
│ title div │
│ / \ │
│ h1 ul │
│ / | \ │
│ li li li │
│ │
└──────────────────────────────────────────────┘
Visual: Live vs Static Collections
┌──────────────────────────────────────────────┐
│ querySelectorAll → NodeList (static) │
│ │
│ Captures current matches only │
│ Later changes don't affect it │
│ │
│ const list = querySelectorAll('.x'); │
│ list.length → N │
│ addMore(); │
│ list.length → N (unchanged) │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ getElementsByClassName → HTMLCollection │
│ (live) │
│ │
│ Reflects current DOM │
│ const list = getElementsByClassName('x'); │
│ list.length → N │
│ addMore(); │
│ list.length → N+M (updated) │
│ │
└──────────────────────────────────────────────┘
Visual: Event Flow
┌──────────────────────────────────────────────┐
│ html │
│ │ ↑ │
│ ▼ │ │
│ body ← capture: top → down │
│ │ ↑ │
│ ▼ │ │
│ div │
│ │ ↑ │
│ ▼ │ │
│ button ← target │
│ │ ↑ │
│ ▼ │ │
│ ────── ← bubble: bottom → top │
│ │
│ event.stopPropagation() stops at that point │
│ event.preventDefault() stops default action │
│ │
└──────────────────────────────────────────────┘
Visual: Event Delegation
┌──────────────────────────────────────────────┐
│ Instead of: │
│ │
│ <ul> │
│ <li> a <button>X</button></li> │
│ <li> b <button>X</button></li> │
│ <li> c <button>X</button></li> │
│ ... 100 more │
│ │
│ Attaching 100 listeners ❌ │
│ │
│ Do this: │
│ │
│ ul.addEventListener('click', e => { │
│ if (e.target.matches('button')) { │
│ e.target.closest('li').remove(); │
│ } │
│ }); │
│ │
│ One listener handles all ✅ │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Method | Example |
|---|---|---|
| Select one | querySelector | querySelector('h1') |
| Select many | querySelectorAll | querySelectorAll('.box') |
| By ID | getElementById | getElementById('main') |
| Text | textContent | el.textContent = 'x' |
| HTML | innerHTML | el.innerHTML = '<b>x</b>' |
| Classes | classList | el.classList.add('x') |
| Attributes | setAttribute | el.setAttribute('id', '1') |
| Data attrs | dataset | el.dataset.userId |
| Style | style.prop | el.style.color = 'red' |
| Create | createElement | createElement('div') |
| Append | appendChild | parent.appendChild(el) |
| Modern insert | append, before, after | el.before(x) |
| Remove | remove | el.remove() |
| Clone | cloneNode | el.cloneNode(true) |
| Fragment | createDocumentFragment | Batch inserts |
| Listen | addEventListener | el.addEventListener(...) |
| Delegate | Parent listener | list.addEventListener(...) |
| Observe | MutationObserver | Watch changes |
Key takeaways:
querySelectorandquerySelectorAllaccept any CSS selector — they’re the modern standardgetElementByIdis fastest for IDstextContentis safe;innerHTMLis dangerous with user inputclassListis cleaner than manipulatingclassNamestringsdatasetprovides clean access todata-*attributescreateDocumentFragmentbatches DOM inserts into one reflow- Use event delegation — one listener on a parent beats many on children
preventDefaultstops default behavior;stopPropagationstops bubbling- Live collections (
getElementsByClassName) update automatically; static (querySelectorAll) don’t addEventListeneris the correct way to bind events — neveronclick = ...DOMContentLoadedensures the DOM is ready before you query it- Observe changes with
MutationObserver,IntersectionObserver,ResizeObserver - Cache queries — don’t re-query in a loop
Remember: DOM manipulation is how you make pages interactive. Select with querySelector, change with textContent or classList, create with createElement, insert with appendChild or fragments, listen with addEventListener. Batch updates, delegate events, and never trust user input in innerHTML. Master the DOM, and every web page becomes your playground.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!