IntersectionObserver 是最稳的检测方式,通过 rootMargin 设为 "0px 0px -100% 0px" 和 threshold: [0],监听 isIntersecting 为 false 且 boundingClientRect.top ≤ 0 时判定刚吸顶。

为什么单纯靠 position: sticky 无法知道“是否已吸顶”
浏览器不提供原生事件或属性来通知你某个元素“刚刚开始 sticky”,getComputedStyle(el).position 在吸顶前后都返回 sticky,不是 fixed;而 el.getBoundingClientRect().top 在吸顶过程中会突变(比如从 120 跳到 0),但这个跳变点受滚动抖动、缩放、iframe 等干扰,不可靠。
IntersectionObserver 是最稳的检测方式
它能精确感知元素与视口的相对位置变化,且不依赖滚动监听,性能好、无抖动。关键在于设置 threshold: [0] 并监听 isIntersecting === false 时的 boundingClientRect.top 是否 ≤ 0。
- 给 sticky 元素加一个唯一
id或data-sticky-target,方便 Observer 绑定 - 初始化
IntersectionObserver时,rootMargin设为"0px 0px -100% 0px":让触发区“上移一整个视口”,这样当元素顶部刚触达视口顶部时就触发回调 - 在回调中检查
entry.boundingClientRect.top ,才确认进入吸顶态(排除完全离开视口的情况) - 避免重复触发:用
observer.unobserve(el)或布尔标记位控制只响应一次
滚动监听 fallback 方案(兼容老浏览器)
当 IntersectionObserver 不可用(如 IE11 或某些 WebView),退回到 scroll 事件 + getBoundingClientRect(),但必须加防抖和阈值容错:
详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。
- 用
requestAnimationFrame节流,别直接写window.addEventListener('scroll', handler) - 判断逻辑不是
el.getBoundingClientRect().top === 0,而是el.getBoundingClientRect().top (容忍像素级误差) - 同时检查
el.offsetParent是否存在,防止元素被display: none或visibility: hidden干扰 - 首次触发后缓存
el.offsetTop,后续用window.scrollY >= el.offsetTop辅助验证,避开因父容器 transform 导致的 getBoundingClientRect 失准
容易忽略的边界情况
sticky 吸顶状态不是全局一致的——它依赖滚动上下文。如果 sticky 元素在 overflow: auto 的局部容器里,那“吸顶”是相对于该容器顶部,不是视口顶部。此时 IntersectionObserver 的 root 必须设为该容器,否则永远收不到正确回调。
立即学习“前端免费学习笔记(深入)”;
另外,Safari 15.4–16.6 在 iframe 内使用 IntersectionObserver 有 bug,boundingClientRect 始终为 0;这种场景下只能硬切回 scroll + offsetTop 判断,并手动减去 iframe 的 offsetTop。

















