DOM & Browser
The DOM — selection and manipulation, attributes vs properties, event handling, propagation, and custom elements.
8 questions
JuniorTheoryVery commonHow do getElementById, querySelector, and querySelectorAll differ for finding DOM elements?
How do getElementById, querySelector, and querySelectorAll differ for finding DOM elements?
getElementById matches one element by its id attribute and returns that element or null. querySelector takes any CSS selector and returns the first match, or null. querySelectorAll returns a static NodeList of every match (empty if none). The CSS-selector methods are more flexible but slower than the id lookup.
Common mistakes
- ✗Thinking
querySelectorAllreturns a liveNodeListthat updates — it is static - ✗Forgetting
querySelector('#id')needs the#prefix thatgetElementById('id')omits - ✗Expecting
querySelectorAllto returnnullfor no matches rather than an empty list
Follow-up questions
- →Why does iterating a
NodeListwithforEachwork, butmapdoes not without converting it first? - →How does a static
NodeListdiffer from the liveHTMLCollectionreturned bygetElementsByClassName?
MiddleTheoryVery commonWhat are the three phases of event propagation, and how do bubbling and capturing differ?
What are the three phases of event propagation, and how do bubbling and capturing differ?
An event travels in three phases: capturing goes down from window through the ancestors to the target, the target phase fires on the element itself, then bubbling goes back up through the ancestors. Handlers run in the bubbling phase by default; passing true (or {capture: true}) to addEventListener makes one run during capturing instead. Bubbling lets a parent react to events on its descendants.
Common mistakes
- ✗Thinking handlers run in capturing by default — bubbling is the default
- ✗Believing capturing goes up and bubbling goes down — it is the reverse
- ✗Assuming events fire only on the target and never reach ancestor elements
Follow-up questions
- →How does
stopPropagationdiffer when called in the capturing phase versus the bubbling phase? - →Why do some events like
focusnot bubble, and what is theirfocusincounterpart?
JuniorTheoryCommonWhat is the difference between an HTML attribute and the corresponding DOM property?
What is the difference between an HTML attribute and the corresponding DOM property?
An attribute is the string written in the HTML source; getAttribute('value') returns that initial markup text. A property is the live value on the DOM object, so input.value reflects what the user has typed and updates as they type. The two sync only at parse time for most attributes; afterward changing the property leaves the attribute frozen at its original markup. Names also differ — the class attribute maps to the className property.
Common mistakes
- ✗Expecting
getAttribute('value')to return the user's current typed text - ✗Assuming every attribute name equals its property name (
classvsclassName) - ✗Believing attribute and property stay synced both ways after the initial parse
Follow-up questions
- →Why do
data-*attributes round-trip through thedatasetproperty as live values? - →When does setting a boolean property like
checkeddiffer from setting its attribute?
JuniorTheoryCommonHow does addEventListener register a handler, and what is the event object passed to it?
How does addEventListener register a handler, and what is the event object passed to it?
addEventListener(type, handler) attaches a callback for an event type without overwriting existing ones, so a target can hold many handlers. When the event fires, the browser calls each handler with an Event object describing it — type, target (where it originated), currentTarget, plus type-specific data like key or coordinates. removeEventListener detaches a handler given the same reference.
Common mistakes
- ✗Thinking a second
addEventListeneroverwrites the first instead of adding another - ✗Passing an anonymous function then being unable to
removeEventListenerit - ✗Calling the handler
onClick()instead of passing the reference, firing it immediately
Follow-up questions
- →Why can't you remove a listener that was added as an inline anonymous arrow function?
- →What does the third argument (
captureor an options object) toaddEventListenercontrol?
MiddleTheoryCommonHow do textContent, innerHTML, style, attributes, and classList change a DOM element?
How do textContent, innerHTML, style, attributes, and classList change a DOM element?
textContent reads or writes an element's plain text, treating tags literally. innerHTML reads or replaces its markup, parsing the string as HTML. style sets inline CSS per property (el.style.fontSize). setAttribute/getAttribute work on HTML attributes, while classList adds, removes, or toggles individual CSS classes without clobbering the rest. Prefer textContent over innerHTML for untrusted text to avoid injection.
Common mistakes
- ✗Using
innerHTMLwith untrusted strings, opening an XSS injection - ✗Assigning to
classNameto add one class, wiping the existing classes - ✗Expecting
textContentto render HTML tags rather than show them literally
Follow-up questions
- →Why is reassigning
innerHTMLon a large element slower than appending a single node? - →When does
setAttribute('value', x)diverge from setting the.valueproperty directly?
MiddleTheoryCommonWhat is event delegation, and how do target and currentTarget differ inside the handler?
What is event delegation, and how do target and currentTarget differ inside the handler?
Event delegation attaches one listener to a common ancestor and relies on bubbling to handle events from many descendants, instead of binding each child. Inside the handler, event.target is the element that actually triggered the event, while event.currentTarget is the element the listener is bound to. You inspect target (often via closest) to decide which child was acted on, including ones added later.
Common mistakes
- ✗Swapping
targetandcurrentTarget—targetis what was acted on - ✗Thinking delegation needs a listener on every child element
- ✗Forgetting delegation also catches dynamically added descendants
Follow-up questions
- →Why is
event.target.closest('.item')safer thanevent.targetfor clicks on nested children? - →How does delegation reduce memory and setup cost on a list with thousands of rows?
MiddleTheoryOccasionalHow do you define a custom element, and what are its lifecycle callbacks and shadow DOM?
How do you define a custom element, and what are its lifecycle callbacks and shadow DOM?
You define a class extending HTMLElement and register it with customElements.define('my-tag', MyClass); the name must contain a hyphen. Lifecycle callbacks run automatically: connectedCallback when it enters the DOM, disconnectedCallback when removed, and attributeChangedCallback when an observed attribute changes. attachShadow({ mode: 'open' }) gives the element an encapsulated shadow tree whose markup and styles are scoped, isolated from the surrounding document.
Common mistakes
- ✗Forgetting the custom element tag name must contain a hyphen
- ✗Expecting
attributeChangedCallbackto fire for attributes not inobservedAttributes - ✗Assuming shadow DOM styles leak into or out of the surrounding document
Follow-up questions
- →Why must
observedAttributesbe declared forattributeChangedCallbackto fire? - →How does a
<slot>element project light-DOM children into the shadow tree?
SeniorTheoryOccasionalHow do preventDefault, stopPropagation, stopImmediatePropagation, and return false differ?
How do preventDefault, stopPropagation, stopImmediatePropagation, and return false differ?
preventDefault cancels the default action but does not stop the event travelling. stopPropagation halts further capturing or bubbling to other elements, yet still runs the current element's other handlers; stopImmediatePropagation also skips those remaining handlers. A passive listener promises not to call preventDefault, letting the browser scroll without waiting. In an inline HTML handler, return false does both prevent-default and stop-propagation.
Common mistakes
- ✗Conflating
preventDefault(cancels default) withstopPropagation(stops travel) - ✗Expecting
return falseto cancel the default inside anaddEventListenercallback - ✗Thinking a passive listener can still call
preventDefaultto block scrolling
Follow-up questions
- →Why did browsers make
touchstart/wheellisteners passive by default for scroll performance? - →When two handlers are on the same element, what stops the second from running?