Toolmingo
Guides7 min read

JavaScript DOM Manipulation Cheat Sheet: Complete Reference

Every DOM method you need — selecting, creating, modifying, and removing elements — with examples, event handling, and a quick-reference table.

The Document Object Model (DOM) is the live tree-representation of your HTML page. JavaScript manipulates it to make pages interactive — adding elements, changing styles, reacting to clicks. This cheat sheet covers every key DOM method with copy-paste examples.


Quick-reference table

Task Method / Property
Select one element document.querySelector(css)
Select all matching document.querySelectorAll(css)
Select by id document.getElementById(id)
Select by class document.getElementsByClassName(cls)
Create element document.createElement(tag)
Set text content el.textContent = 'text'
Set HTML content el.innerHTML = '<b>bold</b>'
Get/set attribute el.getAttribute(name) / el.setAttribute(name, val)
Remove attribute el.removeAttribute(name)
Data attributes el.dataset.myKey
Add class el.classList.add('name')
Remove class el.classList.remove('name')
Toggle class el.classList.toggle('name')
Check class el.classList.contains('name')
Set style el.style.color = 'red'
Append child parent.appendChild(child)
Insert before parent.insertBefore(newEl, refEl)
Insert HTML el.insertAdjacentHTML(pos, html)
Clone element el.cloneNode(deep)
Remove element el.remove()
Add event listener el.addEventListener(event, handler)
Remove event listener el.removeEventListener(event, handler)
Parent element el.parentElement
Children el.children / el.childNodes
Next sibling el.nextElementSibling
Previous sibling el.previousElementSibling
First/last child el.firstElementChild / el.lastElementChild

Selecting elements

querySelector and querySelectorAll accept any valid CSS selector.

// Single element (first match, or null)
const btn = document.querySelector('#submit-btn');
const first = document.querySelector('.card');

// All matches — returns a static NodeList
const cards = document.querySelectorAll('.card');
cards.forEach(card => card.classList.add('visible'));

// Legacy selectors (still fast, ID-based is fastest)
const el = document.getElementById('main');
const items = document.getElementsByClassName('item'); // live HTMLCollection
const paras = document.getElementsByTagName('p');      // live HTMLCollection

NodeList vs HTMLCollection: querySelectorAll returns a static NodeList (won't update if DOM changes). getElementsBy* returns a live HTMLCollection that auto-updates. Convert either to an array when you need .map() / .filter():

const arr = Array.from(document.querySelectorAll('.item'));
// or
const arr2 = [...document.querySelectorAll('.item')];

Creating and inserting elements

// Create
const div = document.createElement('div');
div.className = 'card';
div.textContent = 'Hello world';

// Append to end of parent
document.body.appendChild(div);

// Insert before a reference element
const ref = document.querySelector('#footer');
document.body.insertBefore(div, ref);

// Modern: append / prepend (accepts multiple nodes and strings)
const ul = document.querySelector('ul');
ul.append(li1, li2, 'text node');   // end
ul.prepend(li0);                     // start

// insertAdjacentHTML — four positions
// 'beforebegin' — before the element itself
// 'afterbegin'  — inside, before first child
// 'beforeend'   — inside, after last child
// 'afterend'    — after the element itself
el.insertAdjacentHTML('beforeend', '<li>New item</li>');

// insertAdjacentElement / insertAdjacentText for nodes/text
el.insertAdjacentElement('afterend', newEl);

// Clone an element (true = deep clone including children)
const copy = card.cloneNode(true);
document.body.appendChild(copy);

Modifying content

// textContent — plain text, safe from XSS
el.textContent = 'Hello <world>';   // renders literal angle brackets

// innerHTML — parses HTML (XSS risk with user input!)
el.innerHTML = '<strong>Bold</strong>';

// innerText — respects CSS visibility, slower than textContent
el.innerText = 'Visible text only';

// Replace entire children safely (no XSS)
el.replaceChildren(document.createTextNode('Safe text'));

Security: Never use el.innerHTML = userInput. Always sanitise user-provided HTML with a library like DOMPurify, or use textContent instead.


Attributes and data

// Standard attributes
el.getAttribute('href');           // get
el.setAttribute('href', '/new');   // set
el.removeAttribute('disabled');    // remove
el.hasAttribute('hidden');         // boolean check

// Boolean attributes: presence = true
el.setAttribute('disabled', '');   // disable a button
el.removeAttribute('disabled');    // re-enable

// data-* attributes via dataset (camelCase ↔ kebab-case)
// <div data-user-id="42" data-role="admin">
el.dataset.userId;       // "42"
el.dataset.role;         // "admin"
el.dataset.newKey = 'v'; // sets data-new-key="v"
delete el.dataset.role;  // removes data-role

Classes and styles

// classList — preferred for class manipulation
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');                     // add if absent, remove if present
el.classList.toggle('open', condition);          // force add/remove
el.classList.contains('active');                 // boolean
el.classList.replace('old-class', 'new-class');

// Multiple classes at once
el.classList.add('foo', 'bar', 'baz');

// Read all classes
el.className;               // string: "card active"
[...el.classList];          // array: ["card", "active"]

// Inline styles (camelCase property names)
el.style.color = 'red';
el.style.backgroundColor = '#333';
el.style.fontSize = '1.5rem';
el.style.cssText = 'color: red; font-size: 1.5rem;'; // overwrite all inline styles

// Remove an inline style
el.style.color = '';

// CSS custom properties (variables)
el.style.setProperty('--primary', '#3b82f6');
el.style.getPropertyValue('--primary');

// Read computed style (including stylesheets, not just inline)
const computed = getComputedStyle(el);
computed.color;         // e.g. "rgb(0, 0, 0)"
computed.fontSize;      // e.g. "16px"

Removing elements

// Modern (IE not supported)
el.remove();

// Legacy (still needed for IE11)
el.parentElement.removeChild(el);

// Remove all children
el.replaceChildren();   // modern
// or
while (el.firstChild) el.removeChild(el.firstChild);   // legacy

Traversal

// Up
el.parentElement;
el.closest('.card');      // walk up until CSS selector matches (or null)

// Down
el.children;              // live HTMLCollection, elements only
el.childNodes;            // live NodeList, includes text and comment nodes
el.firstElementChild;
el.lastElementChild;
el.querySelector('.item'); // deep search

// Sideways
el.nextElementSibling;
el.previousElementSibling;

// Check relationship
parent.contains(child);   // true/false

Events

// Add a listener
btn.addEventListener('click', handleClick);

// Named function so it can be removed later
function handleClick(event) {
  event.preventDefault();    // stop default (form submit, link follow)
  event.stopPropagation();   // stop bubbling up to parent
  console.log(event.target); // element that was clicked
  console.log(event.currentTarget); // element the listener is on
}

btn.removeEventListener('click', handleClick); // same reference required

// Common event types
// Mouse:    click, dblclick, mousedown, mouseup, mouseover, mouseout, mousemove
// Keyboard: keydown, keyup, keypress (deprecated)
// Form:     submit, change, input, focus, blur, reset
// Window:   load, DOMContentLoaded, resize, scroll, beforeunload

// Once option — auto-removes after first fire
el.addEventListener('click', handler, { once: true });

// Capture phase (fires before bubble)
el.addEventListener('click', handler, { capture: true });

// Event delegation — one listener handles many children
document.querySelector('ul').addEventListener('click', (e) => {
  const li = e.target.closest('li'); // find the li even if a child was clicked
  if (!li) return;
  li.classList.toggle('done');
});

Custom events

// Create and dispatch
const evt = new CustomEvent('cart:updated', {
  detail: { count: 3 },
  bubbles: true,
});
document.dispatchEvent(evt);

// Listen
document.addEventListener('cart:updated', (e) => {
  console.log(e.detail.count); // 3
});

Practical patterns

Toggle show/hide

function toggle(el) {
  el.hidden = !el.hidden;   // uses the hidden attribute
  // or with a class:
  el.classList.toggle('is-visible');
}

Build a list from data

const fruits = ['Apple', 'Banana', 'Cherry'];
const ul = document.querySelector('#fruit-list');

// Fragment batches DOM updates into a single paint
const frag = document.createDocumentFragment();
fruits.forEach(name => {
  const li = document.createElement('li');
  li.textContent = name;
  frag.appendChild(li);
});
ul.appendChild(frag);

Wait for DOM ready

// Inline script in <body> (no listener needed)
// Or:
document.addEventListener('DOMContentLoaded', () => {
  // DOM is parsed, images may still be loading
  init();
});

window.addEventListener('load', () => {
  // Everything (images, scripts) is loaded
});

Intersection Observer (lazy load / reveal on scroll)

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target); // stop watching once visible
    }
  });
}, { threshold: 0.1 }); // 10% visible triggers callback

document.querySelectorAll('.reveal').forEach(el => observer.observe(el));

6 common mistakes

Mistake Problem Fix
querySelector returns null Element not in DOM yet Move script to bottom of <body> or use DOMContentLoaded
el.innerHTML = userInput XSS vulnerability Use el.textContent or sanitise with DOMPurify
Removing listener with anonymous function Never removes Store reference: const fn = () => {}; el.removeEventListener('click', fn)
Modifying DOM in a loop Many repaints Use DocumentFragment or build HTML string once
getElementsByClassName treated as array No forEach directly Convert: Array.from(...) or use querySelectorAll
event.target vs event.currentTarget Wrong element in delegation target = clicked element, currentTarget = element with listener

6 FAQ

Q: querySelector or getElementById — which is faster?
getElementById is marginally faster since it uses the browser's ID index directly. For most apps the difference is imperceptible; use querySelector for its flexibility.

Q: Why does my script run before the DOM is ready?
Your <script> tag is in <head> without defer. Add defer to the script tag (<script src="app.js" defer>) or move the script to the bottom of <body>.

Q: What's the difference between children and childNodes?
children contains only element nodes. childNodes also includes text nodes (whitespace between tags) and comment nodes. Almost always use children.

Q: Can I use querySelectorAll with forEach directly?
Yes. NodeList has a native .forEach() method. However, it lacks .map(), .filter(), and .reduce() — convert to an array with Array.from() or spread ([...nl]) for those.

Q: How do I read the value of a CSS variable set in a stylesheet?
getComputedStyle(el).getPropertyValue('--my-var').trim(). The .trim() removes potential leading whitespace from the stylesheet declaration.

Q: Is it safe to use innerHTML to render HTML I control?
Yes, as long as you never include user-supplied data. For any string that passes through user input, form fields, URL params, or API responses, use textContent or a sanitiser. Even trusted-looking strings can carry injected scripts if the data source is ever compromised.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools