IntersectionObserver
异步检测元素是否进入视口 - 现代高效的可见性检测 API
API 说明
IntersectionObserver 提供了一种异步检测目标元素与祖先元素或视口相交情况的方法。相比传统的 scroll 事件监听,它具有更好的性能。
const observer = new IntersectionObserver(callback, options);
// 回调函数
function callback(entries, observer) {
entries.forEach(entry => {
console.log('元素可见性:', entry.isIntersecting);
console.log('相交比例:', entry.intersectionRatio);
console.log('相交区域:', entry.intersectionRect);
});
}
// 配置选项
const options = {
root: null, // 视口
rootMargin: '0px', // 根边距
threshold: 0 // 触发阈值(0-1)
};
// 开始观察
observer.observe(targetElement);
- isIntersecting - 元素是否与根元素相交(是否可见)
- intersectionRatio - 相交比例(0 到 1)
- intersectionRect - 相交区域的矩形信息
- root - 用作视口的元素,默认为浏览器视口
- rootMargin - 根元素的边距,可以扩大或缩小触发区域
- threshold - 触发回调的相交比例阈值
💡 性能优势
IntersectionObserver 使用浏览器内部优化,不会频繁触发主线程。相比 scroll + getBoundingClientRect 的组合,性能大幅提升。
示例 1:基础用法 - 检测元素是否可见
滚动页面,观察元素进入/离开视口时的状态变化
目标元素
可见性状态
isIntersecting:
-
相交比例:
0%
触发次数:
0
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口');
} else {
console.log('元素离开视口');
}
});
});
observer.observe(document.getElementById('box'));
示例 2:自定义阈值(threshold)
设置多个触发阈值,精确控制触发时机
↓ 向下滚动 ↓
目标元素
继续滚动...
阈值触发记录
当前比例:
0%
触发阈值:
-
触发历史:
-
const options = {
threshold: [0, 0.25, 0.5, 0.75, 1.0] // 多个阈值
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
console.log(`可见比例: ${Math.round(entry.intersectionRatio * 100)}%`);
});
}, options);
示例 3:rootMargin - 提前触发
使用 rootMargin 提前或延迟触发回调,实现图片预加载等场景
↓ 向下滚动(元素进入前 100px 就会触发)↓
目标元素
继续滚动...
提前触发检测
状态:
等待中
到视口距离:
0px
rootMargin:
100px
💡 应用场景
rootMargin: '100px' 表示元素距离视口还有 100px 时就会触发回调。这对于图片懒加载非常有用,可以在用户看到图片之前就开始加载。
示例 4:无限滚动
检测到底部元素时自动加载更多内容
内容项 1
内容项 2
内容项 3
加载中...
加载状态
加载指示器状态:
检测中
已加载项数:
3
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
// 加载更多内容
loadMoreContent();
}
}, {
rootMargin: '100px' // 提前 100px 触发
});
observer.observe(document.getElementById('loading'));
示例 5:懒加载图片
图片进入视口时才加载 src
↓ 向下滚动查看图片加载效果 ↓
图片 1 - 等待加载
图片 2 - 等待加载
图片 3 - 等待加载
继续滚动...
加载统计
已加载图片:
0 / 3
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.getAttribute('data-src');
// 加载图片
img.src = src;
img.classList.add('loaded');
// 停止观察已加载的图片
observer.unobserve(img);
}
});
});
document.querySelectorAll('.lazy-image').forEach(img => {
imageObserver.observe(img);
});
示例 6:视差动画
根据元素在视口中的位置实现平滑的动画效果
视差元素
动画状态
可见比例:
0%
缩放比例:
1.0
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const ratio = entry.intersectionRatio;
// 根据可见比例调整缩放
const scale = 0.5 + (ratio * 0.5);
entry.target.style.transform = `scale(${scale})`;
});
}, {
threshold: Array.from({length: 100}, (_, i) => i / 100) // 0% 到 100% 每隔 1%
});
浏览器兼容性
| API | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| IntersectionObserver | ✓ 51+ | ✓ 55+ | ✓ 12.1+ | ✓ 15+ |
| IntersectionObserver v2 | ✓ 74+ | 部分支持 | ✗ | ✓ 79+ |
💡 Polyfill
对于不支持 IntersectionObserver 的浏览器,可以使用 W3C polyfill
总结
- 高性能:不会频繁触发回调,性能优于 scroll 事件
- 异步:不阻塞主线程,用户体验更好
- 灵活配置:支持 threshold 和 rootMargin 精确控制触发时机
- 常用场景:图片懒加载、无限滚动、动画触发、广告曝光统计等
- 注意事项:回调中避免复杂计算,观察后记得在适当时机 unobserve