IntersectionObserver 回调不触发的五大原因:rootMargin 未设置、threshold 非数组、目标元素未挂载即 observe、data-src 缺失或命名错误、图片含 loading="lazy" 冲突。
不是代码写错了,而是浏览器压根没把它当“可观察对象”。最常漏掉的是:rootMargin 没设、threshold 写成数字而非数组、目标元素还没挂载就 observe()。
root 未显式设为 null:默认用顶层视口,但若父容器有 overflow: hidden 却没设为 root,观测范围就偏了threshold 必须是数组,比如 [0.1];写成 0.1(非数组)会静默失败,回调永不触发observer.observe(img):Vue 中 ref 未挂载、React 中 useEffect 没执行完都属此类data-src 属性缺失或命名错误(如写成 data-original),导致赋值时 img.dataset.src 是 undefined
loading="lazy",和 JS 懒加载冲突——必须加 loading="eager" 或直接移除该属性阈值太低(如 0.01)容易误触,太高(如 0.8)会导致用户已看到一半才开始加载;rootMargin 设太小(如 '0px')会等元素真进视口才动,用户一滚就白屏。
threshold: 0.01(1% 可见即触发) + rootMargin: '200px 0px'(提前 200px 开始请求)threshold: [0, 0.5],分别在刚出现和半进入时做不同处理(如先占位、再高清)isIntersecting 频繁翻转,建议加简单防抖:if (entry.isIntersecting && !img.dataset.loaded)
rootMargin 支持负值,比如 '-50px' 表示等元素离视口底边还有 50px 才算“即将进入”,适合需要更激进预加载的场景SVG 本身不支持 loading="lazy",且 <image href="..."></image> 的加载时机由 SVG 渲染引擎控制,不能靠 src 赋值。必须监听 SVG 容器,再动态注入 href 或 xlink:href。
data-src 在 <image> 上:<svg><image /></svg>
img.setAttribute('href', src);Safari 13.1 以下需降级:img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', src)
<image> 本身调用 observe()——它没有渲染尺寸,浏览器无法计算交集,应观察外层 <div class="svg-lazy">
observer.unobserve(entry.target),否则 SVG 容器持续触发,可能引发重复注入用 querySelectorAll 批量取图再 observe(),只对初始存在的元素有效。后续通过 Vue v-for、React map 或 innerHTML 插入的图片,引用已失效。
立即学习“前端免费学习笔记(深入)”;
document.querySelector 重新找图——框架重渲染后节点已替换,引用丢失template ref + onUpdated,React 用 useRef + useEffect,确保每次拿到真实 DOM 节点document 上的 DOMNodeInserted(不推荐)或改用 MutationObserver 监听新增的 .lazy 元素observer.observe() 封装成函数,在每次插入新图后显式调用,比如 initLazyImage(newImg)
真正难的不是写几行 IntersectionObserver,而是判断“这个元素此刻是否真的被浏览器识别为可观测目标”——它取决于 DOM 状态、CSS 渲染树、滚动容器层级,三者错一个,isIntersecting 就永远是 false。