核心是“怎么稳又快”:window.scrolly 初次读取需等 domcontentloaded 或 load 事件;scroll 用 requestanimationframe 节流更稳;监听 div 滚动须满足 overflow + scrollheight > clientheight;到达某位置优先用 intersectionobserver。

直接绑定 window.addEventListener('scroll') 就能监听,但不加节流会卡顿、读不到真实值、移动端还可能丢帧——核心不是“能不能”,而是“怎么稳又快”。
为什么 window.scrollY 有时是 0?
刚加载完就调 window.scrollY,或者在 DOMContentLoaded 前读取,大概率拿到 0。这不是 bug,是浏览器还没完成初始布局或滚动状态未同步。
- 首次读取必须等
DOMContentLoaded或load事件之后 - 所有后续读取都应放在
scroll回调里,而不是靠单次取值判断位置 - 移动端 WebView(如旧版 UC)可能在 touchstart 后首帧仍返回 0,需配合
touchmove监听并设{ passive: false }
用 requestAnimationFrame 节流比手写 debounce 更稳
scroll 事件每秒可触发上百次,requestAnimationFrame 天然对齐屏幕刷新率,且避免了时间戳计算和闭包维护成本。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 不用自己记
lastExecTime或清timeoutId - 代码更短、逻辑更直:只管“下一帧要不要执行”,不关心“上次啥时候执行的”
- 示例写法:
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
ticking = true;
requestAnimationFrame(() => {
console.log(window.scrollY); // ✅ 安全、稳定、不卡
ticking = false;
});
}
});
监听 div 滚动却没反应?先查 overflow 和 scrollHeight
给 <div id="list"> 绑了 <code>scroll 事件却从不触发,90% 是 CSS 没配对。
- 必须同时满足:
height(或max-height) +overflow-y: auto(或scroll) - 子内容总高度要大于容器高度,即
container.scrollHeight > container.clientHeight - 别用
window.pageYOffset读 div 滚动——该用container.scrollTop - 调试时打开 DevTools,在 Elements 面板看 computed 样式里的
overflow和实际尺寸对比
滚动到某位置才执行逻辑?优先用 IntersectionObserver
如果目标只是“当某个区块进入视口时触发动画或加载”,硬监听 scroll 是杀鸡用牛刀。IntersectionObserver 不仅性能更好,还能准确处理懒加载、吸顶临界点等场景。
- 它不依赖滚动事件频率,也不需要手动算
getBoundingClientRect() - 对 DOM 变化响应更可靠,尤其适合动态插入的内容
- 兼容性已覆盖所有现代浏览器(Chrome 51+、Firefox 55+、Safari 12.1+)
- 简单示例:
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('active');
}
});
});
observer.observe(document.querySelector('.trigger-section'));
真正容易被忽略的是:滚动监听本身不难,难的是区分“我要响应滚动过程”还是“我只关心是否到达某处”。前者才用 scroll + requestAnimationFrame,后者直接上 IntersectionObserver 更干净。










