# 交叉观察者 - Intersection Observer
Intersection Observer (opens new window) 交叉观察者
懒加载、吸顶、触底效果,原来可以如此的容易。
# 交叉观察者是个啥?
MDN的介绍:
IntersectionObserver
接口,提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态
的方法,祖先元素与视窗(viewport
)被称为根(root
)。
简单来说,IntersectionObserver
翻译为交叉观察者
,它的任务就是监听目标元素
跟指定的父元素
(用户不指定,默认为viewport
)是否发生了交叉行为
。
交叉行为
就是指监听的目标元素
是否进入或者离开了指定父元素
的内部。(经典场景: 图片懒加载
)
# 基本用法
- 构造函数
new IntersectionObserver(callback, options);
- callback
发生交叉行为
的回调,接受一个entries
参数,返回当前已监听
并且发生了交叉行为
的目标元素
的集合。
new IntersectionObserver(entries => {
entries.forEach(item => console.log(item));
// ...
});
看看item
里面包含的常用
属性。
属性 | 说明 |
---|---|
boundingClientRect | 空间信息 |
intersectionRatio | 元素可见区域的占比 |
isIntersecting | 是否正在交叉行为 ,可用做判断元素是否可见 |
target | 目标节点,跟event.target 一样 |
注意
页面初始化的时候会触发一次callback
,entries
为所有已监听的目标集合
。
- options
顾名思义,它是一个配置
参数,对象类型,选填,常用
属性如下:
属性 | 说明 |
---|---|
root | 指定父元素,默认为视窗 |
rootMargin | 触发交叉 的偏移值,默认为"0px 0px 0px 0px" (上左下右,正数向外扩散,负数向内收缩) |
- 常用方法
名称 | 说明 | 参数 |
---|---|---|
observe | 开始监听一个目标元素 | 节点 |
unobserve | 停止监听一个目标元素 | 节点 |
taskRecords | 返回所有监听的目标元素集合 | |
disconnect | 停止所有监听 |
# 简单例子
- 假设页面上有一个
class="box"
的盒子且父元素为视窗
let box = document.querySelector(".box");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
let tips = item.isIntersecting ? "进入了父元素的内部" : "离开了父元素的内部";
console.log(tips);
});
});
observer.observe(box); // 监听一个box
- 假设页面上有多个
class="box"
的盒子且父元素为视窗
let box = document.querySelectorAll(".box");
let observer = new IntersectionObserver(entries => console.log(`发生交叉行为,目标元素有${entries.length}个`));
box.forEach(item => observer.observe(item)); // 监听多个box
# 实际应用
# 图片懒加载
以前都是监听
浏览器滚动,然后遍历拿到每个图片的空间信息
,然后判断一些位置信息从而进行图片加载,而现在只需要交给交叉观察者
做:
let images = document.querySelectorAll("img.lazyload");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
item.target.src = item.target.dataset.origin; // 开始加载图片
observer.unobserve(item.target); // 停止监听已开始加载的图片
}
});
});
images.forEach(item => observer.observe(item));
提示
使用该方法还有一个好处,就是图片横向滚动
(轮播图)进来的时候也是有效的。
传统的懒加载只是监听全局滚动条的滚动,像这种小细节还是无法实现的(传统的实现方法并不是判断目标是否出现在视窗,所以横向的图片会一起加载,即使你没有向左滑动),所以这也是交叉观察者
的一大优点。
# 触底
我们在列表底部放一个参照元素
,然后让交叉观察者
去监听:
假设html
结构如下:
<!-- 数据列表 -->
<ul>
<!-- 多个li -->
<li>index</li>
</ul>
<!-- 参照元素 -->
<div class="reference"></div>
然后监听参照元素
:
new IntersectionObserver(entries => {
let item = entries[0]; // 拿第一个就行,反正只有一个
if (item.isIntersecting) {
console.log("滚动到了底部,开始请求数据");
}
}).observe(document.querySelector(".reference")); // 监听参照元素
# 动画展示
相信很多人都需要过这种需求,当某个元素出现的时候就给该元素加个动画
,比如渐变、偏移等。
假设html
结构如下:
<ul>
<!-- 多个li -->
<li></li>
</ul>
假设scss
代码如下:
ul {
li {
&.show {
// 默认从左边进来
animation: left 1s ease;
// 偶数从右边进来
&:nth-child(2n) {
animation: right 1s ease;
}
}
}
}
@keyframes left {
from {
opacity: 0;
transform: translate(-20px, 20px); // right动画改成20px, 20px即可
}
to {
opacity: 1;
}
}
然后开始监听:
let list = document.querySelectorAll("ul li");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
item.target.classList.add("show"); // 增加show类名
observer.unobserve(item.target); // 移除监听
}
});
});
list.forEach(item => observer.observe(item));
# 总结
使用交叉观察者
的前提必须是子元素跟父元素发生交叉
。两个非父子元素的交叉
那是不行的。