Part 17 — The DOM
Selecting, Creating, inserting, removing, Traversal & attributes, Events, bubbling, capturing, delegation 🎯, Forms & storage
The Document Object Model is a live tree of objects representing the page. JS reads and changes it to make pages interactive. (These APIs are browser-only, not part of ECMAScript.)
Selecting#
document.getElementById("id");
document.querySelector(".card"); // first match (CSS selector) 🟢
document.querySelectorAll("li"); // static NodeList of all matches
element.closest(".panel"); // nearest ancestor matching selector
element.matches(".active"); // boolean
⚠️ querySelectorAll returns a static NodeList; getElementsByClassName/getElementsByTagName return live collections that update as the DOM changes.
Creating, inserting, removing#
const el = document.createElement("div");
el.textContent = "safe text"; // 🟢 no HTML parsing (XSS-safe)
el.classList.add("card");
el.dataset.id = "42"; // data-id="42"
parent.append(el, "text", otherNode); // modern, multiple nodes
parent.prepend(el);
el.before(node); el.after(node); el.replaceWith(node);
el.remove(); // delete self
⚠️ innerHTML parses HTML — never set it from user input (XSS). Prefer textContent. If you must build HTML, sanitize (DOMPurify) or use element.setHTML() where available.
Traversal & attributes#
el.children; el.parentElement; el.nextElementSibling;
el.getAttribute("href"); el.setAttribute("aria-hidden", "true");
el.hidden = true; el.style.color = "red";
el.classList.toggle("open", isOpen);
Events, bubbling, capturing, delegation 🎯#
ELI12. When you click a button inside a list, the click "bubbles" up: button → list → body → document. You can catch it anywhere along the way. Event delegation means putting one listener on the parent instead of many on each child.
graph TD
D[document] -->|capture ↓| B[body]
B --> UL[ul]
UL --> LI[li clicked]
LI -->|bubble ↑| UL
UL --> B2[body]
B2 --> D2[document]
el.addEventListener("click", handler, { capture:false, once:true, passive:true });
el.removeEventListener("click", handler); // must be the SAME function reference
// event delegation — one listener handles many children
list.addEventListener("click", (e) => {
const item = e.target.closest("li");
if (!item) return;
console.log("clicked", item.dataset.id);
});
// prevent default & stop propagation
form.addEventListener("submit", (e) => {
e.preventDefault(); // stop page reload
e.stopPropagation(); // stop bubbling to ancestors
});
- Capturing phase goes top→target; bubbling goes target→top (default
addEventListenerlistens in bubble phase). Pass{capture:true}for the capture phase. passive:truepromises you won't callpreventDefault— lets the browser scroll smoothly.- Delegation benefits: fewer listeners, works for dynamically added elements.
Forms & storage#
const data = new FormData(formEl);
data.get("email");
Object.fromEntries(data); // → plain object
localStorage.setItem("k", JSON.stringify(v)); // persists, ~5MB, strings only, sync
JSON.parse(localStorage.getItem("k"));
sessionStorage; // cleared when tab closes
document.cookie; // small, sent with requests
Observers & misc#
new MutationObserver(cb).observe(node, { childList:true, subtree:true });
new IntersectionObserver(cb).observe(el); // visibility (lazy-load, infinite scroll)
new ResizeObserver(cb).observe(el); // element size changes
history.pushState({}, "", "/new-url"); // SPA routing
navigator.clipboard.writeText("copied"); // async clipboard
Best practices: batch DOM writes (reads then writes) to avoid layout thrashing; use documentFragment or requestAnimationFrame for bulk updates; prefer textContent over innerHTML; remove listeners you no longer need.
Interview questions (Part 17):
- Explain event bubbling vs capturing and event delegation's benefits.
innerHTMLvstextContent— security and performance.- Live vs static NodeLists.
- Why must
removeEventListenerreceive the same function reference?