IntersectionObserver 是浏览器提供的一个原生 API,用于异步监听目标元素与其祖先元素或视口(viewport)之间的交叉状态(即“可见性”)变化。
简单说,它能告诉你:某个元素是否进入了浏览器可视区域,以及进入了多少。
在它出现之前,判断元素是否可见,通常要监听 scroll 事件,然后用 getBoundingClientRect() 手动计算位置。但这种方式有两大痛点:
性能差:scroll 事件触发极其频繁,频繁计算会导致页面卡顿。
代码复杂:需要手动处理节流、边界条件等。
IntersectionObserver 则把这一切交给浏览器底层去处理,异步、高效、不阻塞主线程,是性能最优的可见性监测方案。
// 1. 创建观察器实例
const observer = new IntersectionObserver((entries) => {
//entries 回调函数接收一个数组,包含所有被观察元素的状态变化信息。每个 entry 包含 target(目标元素)、isIntersecting(是否交叉)、intersectionRatio(可见比例 0~1)等。
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入可视区域了!', entry.target)
// 可以在这里做:懒加载图片、埋点上报、播放动画等
}
})
}, {
// 配置项(可选)
threshold: 0.5, // 阈值,决定“可见多少比例时才触发回调”。可以是 0(刚露头)、0.5(半可见)、1(完全可见),也可以是数组 [0, 0.25, 0.5, 0.75, 1] 表示每个比例都触发。目标元素有50%可见时才触发回调
rootMargin: '0px', // 扩大或缩监视窗口边界,比如 '50px' 表示当元素距离视口边缘还有50px时就算“可见”。
root: document.getElementById('content'); //默认是视口(viewport),也可以指定为某个容器元素,实现“元素在某个滚动容器内可见”的监听。
})
// 2. 指定要观察的目标元素
const target = document.querySelector('#myElement')
observer.observe(target)
// 3. 停止观察(可选)
// observer.unobserve(target)
// observer.disconnect()
实际应用场景
- 图片懒加载(最常见)
// javascript
const imgObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src // 替换真实图片地址
imgObserver.unobserve(img) // 加载后停止观察
}
})
})
document.querySelectorAll('img[data-src]').forEach(img => imgObserver.observe(img))
无限滚动(下拉加载更多)
当列表底部的“加载中”占位元素进入视口时,自动请求下一页数据。埋点上报(曝光统计)
商品卡片或广告位进入视口时,自动上报曝光数据,用于数据分析。动画触发(滚动进入时播放)
页面滚动到某个区块时,触发 CSS 动画或 JS 动画执行。广告可见性监测
检测广告是否真正被用户看到(而非只是加载了但没滚动到),用于结算依据。
重要注意事项
兼容性:现代浏览器(Chrome 51+、Firefox 55+、Safari 12.1+)都支持,但在 IE 中完全不支持,需要 polyfill。
回调触发时机:即使 threshold: 0,元素只有在首次进入或完全离开时才会触发回调。如果元素已经可见,初始状态不会触发,需要配合 getBoundingClientRect() 额外处理。
性能依然要注意:虽然比 scroll 事件好很多,但如果同时观察成千上万个元素,仍然可能消耗资源。建议通过 unobserve 及时释放不需要观察的元素。
rootMargin 的单位:支持 px 和 %,但 % 是相对于 root 尺寸的,使用时要注意。