监听DOM元素scroll事件需确保其可滚动:设固定高度、overflow-y: auto且子内容超限;读取用element.scrollTop而非window.scrollY;移动端需加touchmove+passive:false;到底部判断优先用IntersectionObserver。

监听特定 DOM 元素的 scroll 事件要先确认可滚动性
直接写 container.addEventListener('scroll', handler) 没用,前提是这个元素真能滚动。常见错误是给一个视觉上“看起来能滚”的 <div> 绑了事件,结果从不触发——它根本没产生滚动条。
- 必须设明确高度:
height: 300px或max-height: 400px - 必须设溢出行为:
overflow-y: auto(或scroll),仅overflow: hidden或默认visible不行 - 子内容总高度必须 > 容器高度,否则
scrollHeight > clientHeight为 false,浏览器不会启用滚动机制 - 用 DevTools 的 Elements 面板检查 computed 样式,确认
overflow-y是auto且scrollHeight > clientHeight
获取元素内部滚动位置要用 scrollTop,不是 scrollY
window.scrollY 只对整个视口有效;监听某个 <div class="list"> 时,它的滚动偏移量存在 element.scrollTop 里,读错就永远拿不到值。
- 正确写法:
container.addEventListener('scroll', () => console.log(container.scrollTop)) - 错误写法:
console.log(window.scrollY)或document.documentElement.scrollTop—— 这俩返回的是页面级位置,和容器无关 - 注意:
scrollTop是只读属性,赋值可触发滚动,但读取时必须在事件回调内实时取,不能缓存变量后反复用 - 移动端 WebKit 浏览器中,
scrollTop在 touchstart 后可能短暂失准,需配合touchmove补充校验(见下一条)
移动端监听要加 touchmove + passive: false
iOS Safari 和部分安卓 WebView 对 scroll 事件做了延迟派发甚至丢帧处理,纯靠 scroll 监听会漏掉首段滚动、卡顿明显,尤其在快速滑动时。
- 必须同时监听:
container.addEventListener('touchmove', handler, { passive: false }) -
{ passive: false }是关键,否则 iOS 会静默忽略preventDefault()(即使你没调用),导致事件不触发 - 不要在
touchmove回调里直接做重操作,仍需requestAnimationFrame节流 - 如果只是读位置,
touchmove中可直接读container.scrollTop;若需拦截滚动(如自定义下拉刷新),才需要event.preventDefault()
滚动到底部判断别依赖单次计算
用 container.scrollTop + container.clientHeight >= container.scrollHeight 判断是否到底,看似简单,但实际极易误判:图片懒加载未完成、广告异步插入、字体加载延迟都会让 scrollHeight 滞后,导致“明明到底了却不触发”。
立即学习“前端免费学习笔记(深入)”;
- 优先换
IntersectionObserver监听底部占位元素,更稳:rootMargin: '0px 0px 50px 0px'提前触发 - 若坚持用
scroll,必须在回调内每次重新读三个值,不能缓存scrollHeight变量 - 避免在
scroll里立刻发请求,应节流后延至requestAnimationFrame执行,防止高频重复请求 - 滚动停止检测不可靠,别等“完全停住”,而应在滚动间隙(如 100ms 内无新 scroll)就触发加载逻辑
scrollHeight 读取和比较逻辑锁在帧内,且避开 layout thrashing。



















