The debugging note I wrote was wrong for a year
In short
I documented that window scroll listeners never fire on this site because the body is the scroll container. Half of that was true: document.body.scrollTop does read 0. The conclusion was not. When I finally measured, a window scroll listener fired 19 times during a single programmatic scroll. The note had been steering design decisions away from working approaches.

- The claim
- "window scroll listeners never fire — use IntersectionObserver"
- True part
- document.body.scrollTop reads 0 and setting it does nothing
- False part
- window.scrollY tracks correctly; listeners do fire
- Measured
- 19 listener calls across one programmatic scroll
- Cost
- Scroll-driven designs ruled out on the strength of a note
Somewhere early on I hit a scroll bug, worked out that document.body was the scroll container, and wrote the finding down so nobody would lose the same afternoon twice. The note said: the body is the scroll container, so window scroll listeners never fire — use IntersectionObserver.
It sat at the top of the project's instructions for a long time. It shaped decisions. And the second half of it was false.
#What was true
document.body computes to overflow: hidden auto here. That part is real and has real consequences: document.body.scrollTop reads 0, and writing to it does nothing at all. Any code that tried to read or drive scroll position through the body silently did nothing — which is exactly the bug I had originally been chasing.
Everything after that was inference, not observation.
#What was false
document.scrollingElement is <html>, not <body>. window.scrollY tracks the scroll position correctly. And a window scroll listener fires normally — nineteen times across a single programmatic scroll, when I finally sat down and counted.
let n = 0;
const count = () => n++;
window.addEventListener("scroll", count);
window.scrollTo({ top: 2000, behavior: "smooth" });
setTimeout(() => {
console.log("scrollingElement:", document.scrollingElement.tagName);
console.log("window.scrollY:", window.scrollY);
console.log("listener fired:", n, "times");
window.removeEventListener("scroll", count);
}, 1500);This is not an exotic diagnostic. It is nine lines and about forty seconds. It went unrun for a year because the note said the answer was already known — and the note was in a document I trusted, written by someone I had every reason to trust, namely me.
#The cost of a wrong note
The damage here was not a bug. It was a set of designs never attempted. A scroll-driven timeline got ruled out on the strength of one sentence. It works, incidentally — it is on the site now, and it depends on precisely the window scroll behaviour the note said did not exist.
#Writing findings that fail safely
I have not stopped writing debugging notes. I have changed how they are written.
- Separate the observation from the conclusion. "document.body.scrollTop reads 0" is an observation. "Therefore window listeners never fire" is a guess wearing an observation's clothes.
- Date the finding and name the version it was measured against. A note without a date cannot ever be identified as stale.
- Record how it was measured, so the next person can re-run it in a minute instead of re-deriving it in an afternoon.
- Prefer "do X" over "never do Y". A positive recommendation still leaves the alternative open for someone who measures.
- When a note turns out to be wrong, correct it in place and leave the correction visible. A silently edited note teaches nobody.
The note now reads: the body is the scroll container, so write to window or document.scrollingElement, never document.body.scrollTop. IntersectionObserver is the right default for reveal-on-scroll work. Do not rule out a scroll-driven design on the strength of this note without measuring first.
Same finding. Same practical advice for the common case. One sentence at the end that gives the next person permission to check — which is the only part that would have made a difference.
#The general point
Every codebase accumulates a body of received wisdom: the things everyone knows, that nobody has checked since whoever first said them. It is genuinely valuable — it is how a team stops relearning the same lessons. It is also completely unversioned, unowned, and untested.
The absolute claims in it are the ones worth re-measuring. "Never" and "always" are the words that stop investigation, so they are where a wrong belief survives longest.
Questions this answers
Do window scroll listeners fire when the body is the scroll container?
Yes. Even where document.body computes to overflow: hidden auto and document.body.scrollTop reads 0, document.scrollingElement remains <html>, window.scrollY tracks correctly, and window scroll listeners fire normally. The correct rule is to read and write scroll through window or document.scrollingElement rather than document.body.
Why does document.body.scrollTop return 0?
Because <html>, not <body>, is the scrolling element in standards mode. document.scrollingElement points at <html>, so body.scrollTop reads 0 and assigning to it has no effect. Use window.scrollY to read and window.scrollTo to write.
How should engineering debugging notes be written so they do not go stale?
Keep the observation separate from the conclusion drawn from it, date the finding and name the version measured, record the measurement so it can be re-run cheaply, and prefer positive recommendations over absolute prohibitions. Absolute claims stop future investigation, which is how an incorrect note survives longest.
See also
- Learnings — The skill map — knowledge domains, competencies and credentials
- Philosophy — Six operating principles guiding every system and decision




