Scroll listener per page view
trackScrollDepth() runs on every client-side route change in a single-page app. What goes wrong after a few minutes of browsing?
export function trackScrollDepth(pageId) { let maxDepth = 0; window.addEventListener("scroll", () => { const depth = (window.scrollY + window.innerHeight) / document.body.scrollHeight; maxDepth = Math.max(maxDepth, depth); }); return () => sendBeacon("/analytics", { pageId, maxDepth });}Show answer
line 3 · Resource leak
A new scroll listener is added on every navigation and never removed. Old listeners keep running (and keep their closures alive), so each scroll does more and more work and memory grows for the whole session.
const onScroll = () => { const depth = (window.scrollY + window.innerHeight) / document.body.scrollHeight; maxDepth = Math.max(maxDepth, depth); }; window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); sendBeacon("/analytics", { pageId, maxDepth }); };Interview follow-up: Removing a listener requires the same function reference that was added, so name the handler.