IntersectionObserver
📌 什么是 IntersectionObserver?
IntersectionObserver 提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法.
常见用途包括:
图片或组件的懒加载
实现无限滚动
页面滚动时触发动画
可见性埋点(如 PV/曝光统计)
🔧 基本使用方式
js
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口:', entry.target);
// 在此触发加载或动画逻辑
observer.unobserve(entry.target); // 可选,观察一次后取消
}
});
}, {
root: null, // 默认为浏览器视口
rootMargin: '0px', // root 边距,可扩大或缩小可视区域
threshold: 0.1 // 可见比例阈值,0.1 表示 10% 可见时触发
});
const target = document.querySelector('#myElement');
observer.observe(target);
🧠 参数说明
- root:设置用来检测可见性的“容器”,null 表示浏览器视口。
- rootMargin:设置根元素的 margin,可以用来提前触发。
- threshold:阈值,可以是 0~1 之间的数或数组,表示多少比例可见时触发回调。
上面代码中,IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:callback 是可见性变化时的回调函数,callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。option 是配置对象(该参数可选)
构造函数的返回值是一个观察器实例。实例的 observe 方法可以指定观察哪个 DOM 节点。
js
// 开始观察
io.observe(document.getElementById('example'))
// 停止观察
io.unobserve(element)
// 关闭观察器
io.disconnect()
实例:无限滚动
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Intersection Observer 实现一个无限滚动</title>
<style>
div {
width: 80%;
margin: 0 10%;
display: flex;
flex-direction: column;
}
.item {
height: 200px;
width: 200px;
margin: 20px auto;
text-align: center;
line-height: 200px;
font-size: 60px;
}
.item:nth-child(odd) {
background-color: red;
}
.item:nth-child(even) {
background-color: blue;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var io = new IntersectionObserver(
(entries, observer) => {
entries.forEach((i) => {
console.log("i.boundingClientRect", i.boundingClientRect)
if (i.boundingClientRect.bottom < 0) {
unobserve(i.target) //i.target被观察的目标元素,是一个 DOM 节点对象
add(10)
console.log('===========')
}
})
},
{
/* Using default options. Details below */
}
)
function unobserve(target) {
// 停止观察
io.unobserve(target)
}
let time = 0
function add(num = 10) {
const box = document.getElementById('box')
let list = []
let i = 1
while (i <= num) {
let item = document.createElement('div')
item.className = 'item'
item.innerText = time * 10 + i
list.push(item)
i++
if (i === 5) {
io.observe(item)
}
}
box.append(...list)
time++
console.log(time)
}
add()
</script>
</body>
</html>
惰性加载(lazy load)
有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载"。
有了 IntersectionObserver API,实现起来就很容易了。
设置 option 的 rootMargin (偏移量)
html
<img data-src="real.jpg" alt="图片" />
<script>
const imgs = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
obs.unobserve(img);
}
});
});
imgs.forEach(img => observer.observe(img));
</script>