Browser & BOM
The BOM, navigation and page lifecycle, same-origin policy, timers, web storage, and web workers.
11 questions
JuniorTheoryVery commonWhat do setTimeout and setInterval do, and how do clearTimeout and clearInterval cancel them?
What do setTimeout and setInterval do, and how do clearTimeout and clearInterval cancel them?
setTimeout(fn, ms) schedules fn to run once after roughly ms milliseconds; setInterval(fn, ms) runs it repeatedly every ms until stopped. Each returns a numeric timer id. Pass that id to clearTimeout(id) to cancel a pending timeout or clearInterval(id) to stop a repeating interval. The delay is a minimum, not a guarantee, since the callback waits for a free call stack.
Common mistakes
- ✗Thinking the delay is exact rather than a minimum that waits for a free stack
- ✗Passing the function to
clearTimeoutinstead of the returned timer id - ✗Forgetting
setIntervalkeeps firing forever untilclearIntervalis called
Follow-up questions
- →Why might
setIntervalcallbacks overlap or drift, and how does a recursivesetTimeoutavoid that? - →What is the browser's ~4ms minimum delay clamp for deeply nested timers?
JuniorTheoryCommonHow do window and document differ, and what do location, history, and navigator provide?
How do window and document differ, and what do location, history, and navigator provide?
window is the global object and the root of the page; document is its child, the DOM tree of the page's content. The BOM exposes browser-level objects off window: location reads and changes the current URL, history navigates the session stack via back()/forward(), and navigator reports browser and platform info like userAgent. The BOM is not formally standardized.
Common mistakes
- ✗Treating
windowanddocumentas interchangeable rather than parent and child - ✗Confusing
history(session navigation) withlocation(current URL) - ✗Assuming the BOM is standardized like the DOM is
Follow-up questions
- →Why are global
vardeclarations attached towindowbutletdeclarations are not? - →How does
history.pushStatelet a single-page app change the URL without a reload?
JuniorTheoryCommonHow do localStorage, sessionStorage, and cookies differ in lifetime, size, and scope?
How do localStorage, sessionStorage, and cookies differ in lifetime, size, and scope?
localStorage persists per origin until explicitly cleared, even across browser restarts. sessionStorage lives only for the current tab and is wiped when that tab closes. Cookies are per origin too but expire by a set date, are limited to ~4KB, and are sent with every HTTP request to the server. Both storages hold ~5MB of strings and stay client-side only.
Common mistakes
- ✗Believing
sessionStorageis shared across tabs — it is isolated per tab - ✗Thinking storage values can be objects when both store only strings
- ✗Forgetting cookies travel with every request, unlike Web Storage
Follow-up questions
- →Why must you
JSON.stringifyan object before putting it intolocalStorage? - →How can a duplicated tab share state, given
sessionStorageis per tab?
MiddleTheoryCommonHow do DOMContentLoaded and load differ, and how do defer and async change script timing?
How do DOMContentLoaded and load differ, and how do defer and async change script timing?
DOMContentLoaded fires once the HTML is parsed and the DOM is built, before images and stylesheets finish; load fires later, after every subresource has loaded. A plain <script> blocks parsing while it downloads and runs. defer downloads in parallel but runs after parsing, in document order, just before DOMContentLoaded. async also downloads in parallel but runs the instant it arrives, so order is not guaranteed and it may run before or after parsing.
Common mistakes
- ✗Treating
DOMContentLoadedandloadas the same moment - ✗Expecting
asyncscripts to run in document order - ✗Believing
deferblocks HTML parsing while downloading
Follow-up questions
- →Why is
deferpreferred over placing a<script>at the end of<body>? - →Why can an
asyncanalytics script safely ignore execution order but a framework bundle cannot?
MiddleTheoryCommonWhat does setTimeout(fn, 0) actually do, and how do debounce, throttle, and requestAnimationFrame differ?
What does setTimeout(fn, 0) actually do, and how do debounce, throttle, and requestAnimationFrame differ?
setTimeout(fn, 0) does not run immediately — it queues fn as a macrotask after the current synchronous code (and the browser clamps to ~4ms when nested). Debounce delays a function until calls stop for a quiet period; throttle runs it at most once per interval. requestAnimationFrame schedules a callback right before the next repaint, syncing to the display refresh, which makes animations smoother than a fixed-delay setInterval.
Common mistakes
- ✗Thinking
setTimeout(fn, 0)runs synchronously rather than after current code - ✗Confusing debounce (wait for quiet) with throttle (cap per interval)
- ✗Using
setIntervalfor animation instead ofrequestAnimationFrame's repaint sync
Follow-up questions
- →Why does
requestAnimationFramepause on a background tab whilesetIntervalkeeps running? - →Would you pick debounce or throttle for a search-as-you-type box, and why?
SeniorTheoryCommonHow do requestAnimationFrame, timers, and microtasks relate to reflow and repaint, and how do workers help?
How do requestAnimationFrame, timers, and microtasks relate to reflow and repaint, and how do workers help?
Microtasks (Promise callbacks) drain after the current task but before rendering; macrotasks like setTimeout run later; requestAnimationFrame runs just before the next repaint, ideal for visual updates. Layout changes trigger reflow (recomputing geometry, expensive and cascading), while style-only changes trigger a cheaper repaint. Batch DOM reads/writes against thrashing, offload heavy CPU work to a Web Worker, and removeEventListener on detached nodes to avoid leaks.
Common mistakes
- ✗Confusing reflow (geometry, expensive) with repaint (style-only, cheaper)
- ✗Interleaving DOM reads and writes, forcing repeated synchronous layout
- ✗Leaving listeners on removed nodes, keeping them alive as a memory leak
Follow-up questions
- →Why does reading
offsetHeightafter a style write force a synchronous reflow? - →How would you batch animation updates inside a single
requestAnimationFramecallback?
MiddleTheoryOccasionalWhat is the same-origin policy, and how do CORS and cross-origin iframes relate to it?
What is the same-origin policy, and how do CORS and cross-origin iframes relate to it?
An origin is the triple of scheme, host, and port; two URLs share an origin only when all three match. The same-origin policy stops a page from reading responses or the DOM of another origin, blocking data theft. CORS lets a server opt in by sending Access-Control-Allow-Origin, relaxing the read restriction for fetches. A cross-origin iframe still renders, but the parent cannot touch its DOM and the two communicate only via postMessage.
Common mistakes
- ✗Forgetting the port (and scheme) are part of the origin, not just the host
- ✗Thinking CORS is set by the client rather than opted into by the server
- ✗Assuming a parent can read a cross-origin iframe's DOM directly
Follow-up questions
- →Why does a CORS preflight
OPTIONSrequest precede certain cross-origin fetches? - →How does
postMessagesafely pass data across a cross-origin iframe boundary?
MiddleTheoryOccasionalWhat is a Web Worker, how does postMessage communicate with it, and why can't it touch the DOM?
What is a Web Worker, how does postMessage communicate with it, and why can't it touch the DOM?
A Web Worker runs a script on a separate background thread, so heavy work does not block the main UI thread. It has no shared memory with the page: you exchange data through postMessage, and each side reads it via an onmessage / message event. The worker has no access to the DOM, window, or document, so it cannot update the page directly — it sends results back for the main thread to render.
Common mistakes
- ✗Thinking a worker can read or write the DOM directly
- ✗Expecting
postMessageto return a value rather than fire an asyncmessageevent - ✗Assuming the worker shares variables with the page rather than copying data
Follow-up questions
- →How do Transferable objects let
postMessagemove anArrayBufferwithout copying it? - →When would you choose a Web Worker over an async
awaitfor a long computation?
SeniorTheoryOccasionalHow do the browser security model, shadow DOM encapsulation, and the History API support SPA routing?
How do the browser security model, shadow DOM encapsulation, and the History API support SPA routing?
The security model isolates origins (scheme, host, port): the same-origin policy blocks cross-origin DOM and response reads, so untrusted content cannot steal data. Shadow DOM adds a second boundary inside one origin — its tree and styles are encapsulated, scoped away from the document. SPA routing uses the History API: pushState/replaceState change the URL and push entries without a reload, and a popstate handler re-renders views on back/forward, so navigation stays client-side.
Common mistakes
- ✗Thinking
pushStateitself firespopstaterather than only back/forward doing so - ✗Believing shadow DOM relaxes the same-origin policy across origins
- ✗Assuming the History API requires a server reload to change the URL
Follow-up questions
- →How does an SPA router avoid breaking a deep-linked URL on a hard refresh?
- →Why does shadow DOM encapsulation not weaken the cross-origin security boundary?