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.

An open notebook page with a confidently written note struck through, a measuring instrument lying across it.
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);
The measurement that should have happened a year earlier.

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.

  1. 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.
  2. Date the finding and name the version it was measured against. A note without a date cannot ever be identified as stale.
  3. Record how it was measured, so the next person can re-run it in a minute instead of re-deriving it in an afternoon.
  4. Prefer "do X" over "never do Y". A positive recommendation still leaves the alternative open for someone who measures.
  5. 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

Let's Build
What Comes Next.

Open to meaningful collaborations, AI-native systems, product strategy, and future-focused conversations.

“Human instinct. AI amplification.
Systemic execution.”

Suman Debnath

·

Brand Marketing Leader & AI Product Builder

© 2026

This site records visit data — pages viewed, time and scroll depth, device, your IP address and the approximate location and network provider derived from it — and sends it to me privately. It also runs Google Analytics and Vercel Analytics. Full detail and how to opt out.

This site, its code and its content are © 2026 Suman Debnath. All rights reserved — none of it is open source, and copying it needs permission first. Terms of use.