一直以来我们要监控2个元素的相对位置,总是比较麻烦的,而且之前也只能通过js以及每个元素的top值来控制,这也极易拖慢整个网站的性能。然而,随着网页的发展,对上述检测的需求也随之增加,多种情况下都需要用到元素交集变化的信息。如:
- 当页面滚动时,懒加载图片或其他内容。
- 实现”无限滚动“功能页面
- 可以统计一些广告元素的曝光情况
- 根据用户滚动位置来控制执行任务或者动画
相对于过去,我们在检测交集时,需要涉及到事件监听,以及对每个目标元素执行Element.getBoundingClientRect()
方法来获取所需信息。然后通过每个元素的信息来检测元素的交叉。
而且在类似无限滚动的场景下,我们不仅仅需要获取一些元素的位置信息,更需要监控例如scroll等事件,并且很多情况下,也需要依赖一些第三方库来进行监控,并且在第三方库中具体执行了什么,我们并不知晓,这样很是影响性能,并且得到的体验也不是特别友好。
那在这种背景下,我们有更好的方法吗?
IntersectionObserver概念
Intersection Observer的出现,解决了这个问题,Intersection Observer API 会在浏览器注册一个观察者,并且可以设定据地要观察的目标(target),当目标元素(target)以及根元素或者指定的外层元素(root元素)相互交叉的时候触发事件。
用法介绍
//首先我们要先创建观察者
var observer = new IntersectionObserver(callback, options);
//接下来我们要设置具体观察哪个目标
let target = document.querySelector('#id');
observer.observe(target);
我们在来看看创建监控函数时要传递的options中的参数:
参数名 | 描述 | 类型 | 默认值 |
---|---|---|---|
root | 指定根目录,也就是当目标元素显示在这个元素中时会触发监控回调 | Dom元素 | null,即浏览器窗口 |
rootMargin | 类似于css的margin,设定root元素的边框区域。值与css的margin一样“10px 10px 10px 10px” 对应“top right bottom left" | String | 0 |
threshold | 可以是单一的number也可以是一个number数组,这个值可以控制target元素进入root元素中可见性超过的阙值,当达到这个值则会出发函数,并且我们也可以使用数据来让元素在进入时在不同的可见度返回多次值 | number或array | 0 |
实战
我们先来看一个最简单的操作:
这是我们的html:
//我们创建三个容器,并且把第三个容器中的p定位目标
我是第一页
我是第二页
我是第三页
接下来我们创建观察者,以及设定要观察的目标:
var observer = new IntersectionObserver((entries, observer) => {
alert("进入");
}, {});
let target = document.querySelector('.listItem');
observer.observe(target);
我们来设定一下对应的root,让target的目标换成一个指定的元素。
//我们设定一个外层容器,并且把这个容器先搞小一点,方便查看:
var options = {
root: document.querySelector('.con')
}
var observer = new IntersectionObserver((entries, observer) => {
console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);
我们可以指定对应的被观察者要进入哪个root根容器中才会触发回调。
接下来我们在来尝试更复杂的去控制:
需要注意的是,当我们这样来设置rootMargin时,会出现错误:
var options = {
rootMargin: "100px 0 0 0"
}
虽然其规范与css得我margin一样,但是当值是0的时候,我们是无法直接使用0的,要添加上单位,如px、rem、em等,正确写法如下:
var options = {
rootMargin: "1
00px 0px 0px 0px"
}
我们来看一下这个图,当设置rootMargin时,就相当于把对应的元素放大,如下图,root为黄色区域,但是我们设定了rootMargin,实际上对于target来说,整个蓝色区域都会认定为root的监控区域,当target进入蓝色区域机会触发回调。
让我们来看看如何设定吧:
var options = {
root: document.querySelector('.con'),
rootMargin: "0px 0px 100px 0px"
}
var observer = new IntersectionObserver((entries, observer) => {
console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);
注意:我们设置的margin是root的而不是target的,所以在一下的这个demo中,当target往上滑动时,target具体root还差100px时就触发了回调,这个要设定为bottom而非top。
我们来看看threshold属性:
threshold 此参数的范围为0.0-1.0,并且我们可以设置一个number值,也可以设置一个number的Array数组,来触发多次回调。
首先我们设定一个触发值:
var options = {
root: document.querySelector('.con'),
threshold: 1.0
}
var observer = new IntersectionObserver((entries, observer) => {
console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);
我们设定,当target全部显示到root中时才会触发回调:
这次我们来尝试设置多次触发点:
var options = {
root: document.querySelector('.con'),
threshold: [0, 0.5, 1.0]
}
var observer = new IntersectionObserver((entries, observer) => {
console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);
我们设定了在target开始出现,出现一半,以及全部漏出的时候都会触发,所以应该会触发三次回调,看看效果呢。
截止到此,我们应该已经知道IntersectionObserver的使用方法。
IntersectionObserver回调中的回参
既然我们知道此API的使用方式,那么我们也一定很好奇此函数具体返回了什么。
函数的callback会返回2个值: [IntersectionObserverEntry] 和 IntersectionObserver。
-
IntersectionObserverEntry: 提供了target和root交叉之后的一些信息,此值无法主动创建,但是可以通过
IntersectionObserver.takeRecords()
来获取。
参数内容如下:
-
IntersectionObserver: 提供了当前创建的观察者IntersectionObserver的所有信息。
尾声
这个API,可能大家遇到或者听说过的都不多,不过如果有幸你们看到了它,那它可能会给你的代码带来一些新的优化灵感,如果能达到这样的效果,那么这篇文章就有了其存在的价值。