优先用 IntersectionObserver。它不绑定 scroll 事件,避免频繁重排重绘和漏帧;scroll 事件易卡顿掉帧,需节流或 requestAnimationFrame 优化,且存在 passive 事件、scrollTop 兼容性等问题。

滚动监听用 window.addEventListener('scroll') 还是 IntersectionObserver?
优先用 IntersectionObserver。它不绑定在 scroll 事件上,不会触发频繁重排重绘,也不会因节流不当导致监听漏帧。而 window.addEventListener('scroll') 在未加节流或使用 requestIdleCallback 时,容易卡顿、掉帧,尤其在低端安卓机或复杂 DOM 场景下。
实际场景中,如果你只是想“当某元素进入视口时隐藏/显示导航栏”,IntersectionObserver 一行配置就能搞定;但若需精确到像素级偏移(比如滚动 127px 时触发),才考虑 scroll + getBoundingClientRect() 手动计算。
-
IntersectionObserver兼容性已覆盖 Chrome 51+、Firefox 55+、Safari 12.1+、Edge 79+,iOS 12.2+ 起支持 - 不要给
IntersectionObserver的rootMargin写成"0px 0px -100px 0px"这种带负号的字符串——它只接受字符串,但负值表示向外扩展(即提前触发),正数才是向内收缩(延迟触发) - 监听单个元素时,别忘了调用
observer.unobserve(target)避免内存泄漏,尤其在 SPA 页面切换时
IntersectionObserver 怎么精准控制“隐藏”时机?
关键在 rootMargin 和 threshold 两个参数。比如你想让顶部导航栏在用户向下滚动 60px 后隐藏,不是监听滚动距离,而是把导航栏设为 target,再用 rootMargin: "60px 0px 0px 0px" —— 这样当导航栏的上边缘离视口顶部还有 60px 时,就视为“进入”状态,isIntersecting 变为 true,此时执行隐藏逻辑。
如果要更精细控制(例如只在元素 30% 进入时触发),就设 threshold: 0.3;多个阈值可写成 [0, 0.3, 0.6, 1],回调中通过 entries[0].intersectionRatio 判断当前比例。
立即学习“前端免费学习笔记(深入)”;
- 隐藏动作建议用
element.classList.add('hidden')配合 CSS.hidden { transform: translateY(-100%); transition: transform 0.3s ease; },比直接display: none更利于复位 - 避免在回调里直接修改
style.top或频繁读取offsetTop,这会强制同步布局(forced reflow) - 初始化时先手动调用一次
observer.observe(target),否则首屏加载可能错过初始状态
为什么 scroll 事件监听总延迟或失效?
常见原因是没处理好被动事件(passive event)和浏览器优化策略。Chrome 56+ 默认将 scroll 监听器标记为 passive,禁止在回调中调用 preventDefault();但如果你写了 { passive: false } 却没配 preventDefault(),或反过来该禁用却没禁,都可能被浏览器静默忽略。
另一个坑是监听对象选错:用 document.body 或 document.documentElement 获取 scrollTop 时,不同浏览器行为不一致(Chrome 用 document.documentElement.scrollTop,Firefox 有时返回 0),应统一用 window.pageYOffset 或封装一个兼容函数。
- 节流别用
setTimeout套壳,直接上throttle(func, 16)(目标 60fps)或更稳妥的requestAnimationFrame包裹 - 移动端注意:iOS Safari 在地址栏收起/展开时会触发额外 scroll,
pageYOffset突变但并非用户主动滚动,需结合performance.now()时间戳过滤抖动 - 别在
scroll回调里反复查询document.querySelector('.header'),缓存 DOM 引用
隐藏后怎么平滑恢复?方向判断不能只靠 lastScrollTop
单纯比较前后 scrollTop 容易误判,比如快速滚动后惯性回弹、iOS 橡皮筋效果、甚至键盘弹起导致视口高度变化,都会让 scrollTop 短暂反向。更可靠的方式是记录时间窗口内的滚动趋势:用长度为 3 的数组存最近三次 pageYOffset 和对应时间戳,算斜率符号;或者用 deltaY(来自 wheel 事件)辅助校验。
实际隐藏/恢复逻辑建议分离:隐藏由 IntersectionObserver 触发,恢复则监听用户向上滚动超过阈值(如 10px)且持续 100ms,再执行显示。这样避免来回抖动。
- 不要用
scroll事件监听恢复——它和隐藏用同一套逻辑,容易冲突 - 恢复动画建议加
will-change: transform提前告知浏览器,避免闪帧 - 如果页面有 fixed 元素,记得在隐藏/恢复时同步调整其
top值,否则会出现遮挡或错位











