threshold: 0.5仅触发瞬时可见判断,无法验证“50%可见持续1秒”的时间维度要求;需结合isintersecting状态持续监测与时间戳逻辑才能满足iab标准。

为什么 threshold: 0.5 不足以满足 IAB 标准
IAB 的“50% 可见持续 1 秒”不是静态阈值判断,而是带时间维度的复合条件。threshold: 0.5 只保证回调在可见比例越过 50% 时触发一次,但无法确认是否连续维持——比如元素快速扫过视口(isIntersecting 为 true 仅一帧,就直接上报曝光,这属于典型虚假流量。
必须自行维护每个广告元素的可见起始时间戳,并在每次回调中校验 intersectionRatio >= 0.5 && entry.isIntersecting 是否持续成立。一旦中断(isIntersecting === false 或 intersectionRatio ),就重置计时器。
- 不要依赖单次回调做曝光判定
- 每个
entry.target需绑定独立的startTime和timeoutId -
threshold建议设为[0, 0.5, 1],避免漏掉从 40%→60% 的跃变过程
rootMargin 设置不当会放大误报率
rootMargin 扩展检测区域后,广告元素尚未真正进入用户视野就触发回调。例如设为 '200px 0px',在桌面端可能提前 200px 触发,而用户滚动速度较快时,该元素实际停留视口内不足 1 秒,但你的计时器已启动——若未及时清理,就会误报。
更危险的是移动端:设备像素比(dpr)和缩放行为会让 rootMargin 的像素值失真。实测发现,在 iOS Safari 缩放至 125% 时,'100px' 实际扩展约 150px,导致更早触发。
- 对广告监测场景,
rootMargin应保持默认'0px',或最多设为'50px'(且需配合防抖) - 若必须预加载,改用
threshold: [0.1, 0.5]+ 独立预取逻辑,而非扩大rootMargin - 避免使用百分比单位(如
'5%'),不同浏览器解析不一致
isIntersecting 为 true ≠ 元素视觉可见
isIntersecting 仅表示几何交集存在,完全不感知 CSS 渲染状态。常见陷阱包括:
-
opacity: 0或visibility: hidden:元素仍被 IntersectionObserver 认为“相交” - 父级
overflow: hidden且子元素部分溢出:交集计算仍基于原始getBoundingClientRect(),不考虑裁剪 - 被其他 DOM 层级遮挡(z-index 覆盖):API 无感知
必须叠加视觉可见性校验:
function isVisuallyVisible(el) {
const style = getComputedStyle(el);
if (style.opacity === '0' || style.visibility === 'hidden') return false;
if (style.display === 'none') return false;
return true;
}
并在回调中组合判断:entry.isIntersecting && isVisuallyVisible(entry.target)。
disconnect() 和 unobserve() 的调用时机很关键
广告组件常被动态挂载/卸载(如 React 中的 useEffect 清理),若忘记调用 observer.unobserve(target) 或 observer.disconnect(),会导致:
- 已销毁的 DOM 节点仍被观察,回调中访问
entry.target报TypeError: Cannot read property 'dataset' of null - 内存泄漏:Observer 持有对 target 的强引用,阻止 GC
- 重复上报:同一广告实例被多次 observe(如路由复用组件)
安全做法是:在广告组件卸载前显式 unobserve;全局 Observer 在页面离开前 disconnect;避免在回调中直接调用 unobserve 后又读取 entry.target ——应先缓存必要字段再解绑。
最易忽略的一点:多个广告位共用一个 Observer 实例时,unobserve 某个 target 不影响其余 target,但你得确保每个 target 的生命周期管理是独立的。










