Notifications

No notifications

/Phase 4

DOM Manipulation & Events

The DOM (Document Object Model) is JavaScript's API for reading and changing HTML. This page covers the everyday vanilla toolkit — querySelector, creating and inserting elements, classList/dataset, forms, and event handling (including event delegation, the trick that makes one listener serve thousands of children).

On this page

Detailed Theory

# DOM Manipulation & Events

Selecting elements

document.getElementById("main");                  // single, by id
document.querySelector(".card.featured");         // first match — CSS selector
document.querySelectorAll("ul li");               // NodeList of all matches
querySelectorAll returns a NodeList (live-ish, iterable, has forEach). Convert with [...nodes] if you need real array methods.

Reading & writing content

el.textContent = "Hello";              // safe — sets text
el.innerHTML   = "<b>Hello</b>";        // ⚠️ parses HTML — XSS risk if user input!
el.value;                              // for inputs
el.checked;                            // for checkboxes/radios

Creating & inserting nodes

const li = document.createElement("li");
li.textContent = "New item";
li.classList.add("done");

list.append(li); // at end (accepts text + nodes) list.prepend(li); // at start list.insertBefore(li, list.firstChild); li.remove(); // remove self li.replaceWith(other);

Classes

el.classList.add("active");
el.classList.remove("hidden");
el.classList.toggle("dark");
el.classList.contains("active");
Avoid el.className = "..." — it overwrites everything.

Attributes & data-*

el.setAttribute("href", "/about");
el.getAttribute("href");
el.removeAttribute("disabled");

// data-* attributes // HTML: <button data-user-id="42" data-role="admin"> btn.dataset.userId; // "42" (camelCased) btn.dataset.role; // "admin" btn.dataset.role = "user";

Styles

el.style.backgroundColor = "tomato";
el.style.cssText = "color: white; padding: 8px;";

const cs = getComputedStyle(el); cs.color;

But prefer toggling classes over inline styles in real apps.

Events

btn.addEventListener("click", e => {
    console.log(e.target);             // the actual clicked element
    console.log(e.currentTarget);      // the element the listener is on
    e.preventDefault();                 // stop default (e.g. form submit)
    e.stopPropagation();                // stop bubbling up
});
Always pair addEventListener with removeEventListener if you'll re-render — or use { once: true }.

Event delegation

Instead of attaching a listener to every
  • , attach ONE to the parent:
    list.addEventListener("click", e => {
        const item = e.target.closest("li");
        if (!item) return;
        console.log("clicked", item.dataset.id);
    });
    Faster, less memory, works for items added later.

    Forms

    form.addEventListener("submit", e => {
        e.preventDefault();
        const data = new FormData(form);
        const obj  = Object.fromEntries(data);     // { name: "...", email: "..." }
        console.log(obj);
    });
    HTML provides validation: required, type="email", pattern, min/max — let the browser do the checks.

    Common patterns

    // run after the DOM is parsed
    document.addEventListener("DOMContentLoaded", init);

    // element traversal el.parentElement; el.children; // HTMLCollection el.nextElementSibling; el.closest(".card"); // walk up to nearest matching ancestor

    // scrolling el.scrollIntoView({ behavior: "smooth", block: "start" });

    Performance tips

    • Batch DOM writes: build markup as a string or DocumentFragment, insert once.
    • Reads (offsetWidth, getBoundingClientRect) force layout — don't interleave with writes inside loops.
    • Toggle a CSS class instead of setting many style.* lines.

    When to use a framework

    The DOM is the "metal" beneath React/Vue/Svelte. For tiny widgets, vanilla is plenty. For large apps with lots of state, frameworks save you from manual DOM bookkeeping.