DOM Basics and Events
What this domain covers
Selecting and updating elements, and the event model: registration, propagation, delegation, and the difference between target and currentTarget.
Selecting and changing nodes
document.querySelector(css) returns the first match or null. querySelectorAll returns a static NodeList that supports forEach. getElementsByClassName and getElementsByTagName return live HTMLCollections without forEach. Prefer textContent for plain text; innerHTML parses markup and reintroduces XSS risk. classList.add/toggle/contains beats string surgery on className, and dataset.foo maps to the data-foo attribute.
Events
addEventListener(type, handler, options) allows several handlers per element, unlike assigning onclick. The handler receives an event object: target is the element that originated the event, currentTarget is the element whose listener is currently running. Events bubble upward by default; stopPropagation() halts that, preventDefault() cancels the default action for cancelable events only. Options include { once: true } and { capture: true }.
Readiness
Scripts in <head> run before the body exists; use the DOMContentLoaded event or the defer attribute so querySelector finds its target.
Worked example
const list = document.querySelector('#list');
list.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (!btn) return;
console.log(btn.dataset.id, e.currentTarget === list);
});
Reasoning: one listener on the parent handles every current and future button - event delegation. e.target may be a nested <span>, so closest('button') walks up to the real button and dataset.id reads data-id. currentTarget is the element the listener is attached to, so it equals list; target may not.
Traps
- Assuming
querySelectorAllresults update after DOM changes. - Building HTML with
innerHTMLinside a loop and losing listeners. - Calling
preventDefaulton a non-cancelable event or a passive listener. - Confusing
targetwithcurrentTargetin delegated handlers.