正确监听滚动需在回调内读取window.scrolly,用requestanimationframe节流,移动端需同时监听touchmove并设{passive:false},监听div时须确保其有固定高度和overflow-y:auto。

直接用 window.addEventListener('scroll', handler) 就能监听,但绝大多数人一写就卡、一上线就掉帧、一测就发现移动端不触发——问题不在“会不会加”,而在“加得对不对”。
为什么 scroll 事件里 window.scrollY 有时是 0?
不是代码错了,是读得太早或太死:
- 页面刚加载完立刻读
window.scrollY,DOM 还没完成初始布局,返回 0(尤其在安卓 WebView 或旧版 UC 中) - 只在首次进入时读一次,之后滚动完全不更新——
scroll是持续触发的,不是“滚动结束才通知” - 在
scroll回调外硬编码取值,比如写成const y = window.scrollY; window.addEventListener('scroll', () => console.log(y)),y 永远不变
正确做法:所有位置读取必须放在 scroll 回调内部,或用 requestAnimationFrame 包一层再读。
怎么避免滚动卡顿?别用 setTimeout 节流
setTimeout 或自定义 throttle 函数只能控制频率,但无法对齐渲染帧。浏览器滚动时每秒可能触发上百次,而屏幕刷新率通常只有 60fps(约每 16ms 一帧),强行塞进定时器反而造成帧丢失。
- 优先用
requestAnimationFrame:它天然绑定下一帧,且同一帧内多次调用只执行最后一次 - 不要在回调里反复查
document.documentElement.scrollTop和window.pageYOffset,统一用window.scrollY(现代浏览器兼容性好、语义清晰) - 如果只是做“滚动到某位置触发效果”,比如懒加载、吸顶导航,直接换
IntersectionObserver,它不依赖 scroll 事件,更轻、更准、不占主线程
示例节流结构:
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
ticking = true;
requestAnimationFrame(() => {
console.log(window.scrollY); // ✅ 安全、稳定、不卡
ticking = false;
});
}
});
移动端 scroll 事件为什么监听不到?
不是 bug,是浏览器主动限制。iOS Safari 和部分安卓 WebView 对 scroll 事件做了激进节流,甚至延迟派发,导致首帧丢失、touchmove 后无响应。
- 必须同时监听
touchmove,并显式传{ passive: false },否则 iOS 会静默丢弃事件 -
scroll回调里不能调用event.preventDefault(),否则会报错Unable to preventDefault inside passive event listener - 需要拦截滚动(如滚动锁、下拉刷新)→ 必须用
{ passive: false };仅读取位置(如吸顶、懒加载)→ 推荐{ passive: true },Chrome 控制台警告别忽略
稳妥写法:
window.addEventListener('scroll', handleScroll, { passive: true });
window.addEventListener('touchmove', handleScroll, { passive: false });
监听某个 div 而不是整个页面,为什么没反应?
不是 JS 写错了,是 CSS 没配对:
- 该
<div> 必须有明确的 <code>height(或max-height),不能是height: auto - 必须设
overflow-y: auto或scroll,且子内容高度 > 自身高度(可用 DevTools 检查scrollHeight > clientHeight) - 绑定事件对象是那个
div元素,不是window;获取位置用element.scrollTop,不是window.scrollY - 父容器若也设置了
overflow,可能截获事件,建议父级加overflow: hidden
检查顺序比改代码更重要:先打开 DevTools → Elements → 选中元素 → 查 Computed → 看 overflow 是否生效、scrollHeight 是否大于 clientHeight。











