JavaScript DOM Manipulation Cheat Sheet
Covers selecting DOM elements, creating and modifying nodes, handling events with delegation, and toggling classes with vanilla JavaScript.
Selecting Elements
Query the DOM with CSS selectors or legacy accessors.
document.getElementById("app"); // Single element by iddocument.querySelector(".card"); // First match (CSS selector)document.querySelectorAll(".card"); // NodeList of all matchesdocument.getElementsByClassName("card"); // Live HTMLCollectiondocument.getElementsByTagName("li"); // Live HTMLCollection// Iterating a NodeListdocument.querySelectorAll(".card").forEach(el => console.log(el));
Creating & Modifying Nodes
Build, insert, and remove elements.
const el = document.createElement("div");el.textContent = "Hello"; // Safe text (no HTML parsing)el.innerHTML = "<b>Hello</b>"; // Parses HTML (careful with user input)el.setAttribute("data-id", "42");el.id = "greeting";document.body.appendChild(el); // Add as last childparent.insertBefore(el, referenceNode);el.remove(); // Remove from the DOMconst clone = el.cloneNode(true); // Deep clone (true = include children)
Event Handling
Listen for and delegate DOM events.
const btn = document.querySelector("#submit");btn.addEventListener("click", (event) => { event.preventDefault(); // Stop default action (e.g. form submit) console.log("clicked", event.target);});btn.removeEventListener("click", handlerFn);// Event delegation: listen on a parent, filter by targetdocument.querySelector("ul").addEventListener("click", (e) => { if (e.target.matches("li")) { console.log("item clicked:", e.target.textContent); }});
Classes & Inline Styles
Toggle CSS classes and set styles from JavaScript.
const el = document.querySelector(".box");el.classList.add("active");el.classList.remove("hidden");el.classList.toggle("open");el.classList.contains("active"); // true/falseel.style.backgroundColor = "coral";el.style.setProperty("--gap", "8px"); // Set a CSS custom property
Quick Reference
Handy DOM properties and methods.
- el.parentElement- Reference to the direct parent element
- el.children- Live HTMLCollection of child elements (no text nodes)
- el.closest('.card')- Nearest ancestor (including itself) matching a selector
- el.dataset.id- Read/write a data-id attribute via the camelCased dataset API
- el.getBoundingClientRect()- Returns size and position relative to the viewport
- DOMContentLoaded- Fires when HTML is parsed, before images/styles finish loading
Observing DOM Changes
React to nodes being added, removed, or mutated without polling.
const target = document.querySelector("#list");const observer = new MutationObserver((mutations) => { for (const m of mutations) { if (m.type === "childList") { console.log("added:", m.addedNodes.length, "removed:", m.removedNodes.length); } }});observer.observe(target, { childList: true, subtree: true, attributes: true });// Stop observing when no longer neededobserver.disconnect();
Lazy-Loading with IntersectionObserver
Detect when an element enters the viewport without expensive scroll listeners.
const io = new IntersectionObserver((entries) => { for (const entry of entries) { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; // Swap in the real image io.unobserve(img); } }}, { rootMargin: "200px", threshold: 0.1 });document.querySelectorAll("img[data-src]").forEach(img => io.observe(img));
template Element & DocumentFragment
Build batches of nodes off-DOM to avoid repeated reflows.
const tpl = document.querySelector("#row-template"); // <template id="row-template">...</template>const frag = document.createDocumentFragment();for (const item of items) { const node = tpl.content.cloneNode(true); node.querySelector(".name").textContent = item.name; frag.appendChild(node);}// Single reflow instead of one per itemdocument.querySelector("#rows").appendChild(frag);
Custom Events & Shadow DOM
Dispatch app-specific events and encapsulate a component's internal markup.
// Custom events for component-to-component communicationconst el = document.querySelector("#widget");el.dispatchEvent(new CustomEvent("widget:ready", { detail: { id: 42 }, bubbles: true,}));document.addEventListener("widget:ready", (e) => console.log(e.detail.id));// Shadow DOM: style/markup encapsulationclass MyWidget extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "open" }); shadow.innerHTML = `<style>p{color:teal}</style><p>Inside shadow DOM</p>`; }}customElements.define("my-widget", MyWidget);
Advanced Reference
APIs that come up once basic selection and events are second nature.
- el.matches(selector)- Tests an element against a CSS selector without querying the whole document
- requestAnimationFrame(fn)- Schedules fn right before the next repaint; batch layout-affecting DOM writes here to avoid jank
- el.hasAttribute vs el.property- Attributes are the initial HTML string; properties are the live JS value (e.g. input.value vs input.getAttribute('value'))
- document.createRange()- Builds a Range for programmatic text selection or precise DOM insertion points
- el.animate([...], opts)- Web Animations API: runs a keyframe animation directly from JS, returning a controllable Animation object
- ResizeObserver- Fires a callback when an element's box size changes, independent of window resize events
- el.replaceChildren(...nodes)- Clears and replaces all children in one call, replacing the old innerHTML = '' + append pattern
Prefer textContent over innerHTML when inserting untrusted or dynamic strings — innerHTML parses its argument as HTML and is a common source of cross-site scripting (XSS) vulnerabilities.