Intersection Observer交叉观察器示例解析
完锤子 人气:0前言
作为一个前端搬砖仔,偶尔会遇到关于视窗的问题...
其中就经常会遇到一些数据上报问题,比如某某组件是否被用户看到了,如果看到就要上报数据,让产品同学方便进行数据分析。
分析这个场景可以发现,必须要用户看到了我才能上报,怎么判断用户是否看到了这个组件呢?
传统的方法是操作dom,通过这些数据或方法(offsetTop、scrollTop,getBoundingClientRect)来进行比较。
频繁的对DOM状态的计算,会造成不小的性能损耗,导致页面卡顿。
接下来介绍一种我最近比较常用的新方式:Intersection Observer(交叉观察器)
Intersection Observer 翻译过来即交叉观察器
主要是用于监听目标元素与指定的元素视窗是否发生交叉
一句话总结:Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。 – MDN
使用
一、利用IntersectionObserver构造函数创建一个观察器实例
<template> <div class="home"></div> </template> <script> export default { name: "HomeView", mounted() { // 可见性发生变化后的回调 function callback() { console.log("触发了"); } // 交叉观察器配置项 let options = {}; // 生成交叉观察器 const observer = new IntersectionObserver(callback); }, }; </script>
(callback是当元素可见比例超过指定阈值后会调用的一个回调函数,options是一个可以用来配置 observer 实例的对象。)
输出一下IntersectionObserver构造函数返回的实例
实例属性:
root:root 属性用来获取当前 intersectionObserver 实例的根元素,用于判断元素是否可见区域。
(注:这个既可以是 target 元素祖先元素也可以是指定 null 则使用浏览器视口 做为容器 (root)。)
rootMargin:一个类似于margin的字符串参数,就可以使用 root margin 来调整根矩形大小。
(注:由于使用Intersection Observer的过程中所有区域均被当做一个矩形看待,因此当我们需要调整元素边界的矩形时,用 root margin 来调整大小。)
threshold:阈值,可以传一个0 ~ 1的number,也可以传个0 ~ 1范围的number数组。
(注:如果传一个0 ~ 1的number,如0.5则说明,目标元素和root相交超过50%时,即二者重合超过目标元素的50%时,触发回调,如果是一个数组[0.1, 0.2, 0.5]则分别在10% 20% 50%处触发回调)
二、观察器实例监听元素相交
<template> <div class="home"> <div id="target" class="target-item"></div> </div> </template> <script> export default { name: "HomeView", mounted() { // 可见性发生变化后的回调 function callback(data) { console.log("触发了"); console.log(data); } // 交叉观察器配置项 let options = {}; // 生成交叉观察器 const observer = new IntersectionObserver(callback); // 获取目标节点 let target = document.getElementById("target"); // 监听目标元素 observer.observe(target); }, }; </script> <style scoped> .home { height: 300vh; } .home .target-item { margin-top: 120vh; width: 20px; height: 20px; background: #000; } </style>
可以看到
通过滚动,可以不停监听到观察器的回调。
三、观察器回调函数参数
看到在观察器的回调中是返回了一个对象的,修改原来的回调函数,对应看看到底返回了啥?
export default { name: "HomeView", mounted() { // 可见性发生变化后的回调 function callback(data) { console.log("触发了"); data.map((item) => { // 目标元素的getBoundingClientRect的返回值。 console.log(item.boundingClientRect); // 目标元素和根元素交叉区域的getBoundingClientRect的返回值。 console.log(item.intersectionRatio); // 目标元素的可见比例,相当于二者重合了多少。 console.log(item.intersectionRect); // 目标元素与根元素是否相交 console.log(item.isIntersecting); // 根元素的getBoundingClientRect的返回值 console.log(item.rootBounds); // 目标元素,是个dom console.log(item.target); // 从首次创建观察者到触发指定阈值发生交叉的时间 console.log(item.time); }); } // 交叉观察器配置项 let options = { threshold: 0.5, }; // 生成交叉观察器 const observer = new IntersectionObserver(callback, options); // 获取目标元素 let target = document.getElementById("target"); // 监听目标元素 observer.observe(target); }, };
可以看到控制台输出了下述内容:
可以看到返回了很多东西
其中就有我们熟悉的元素的getBoundingClientRect属性。
回调参数属性:
boundingClientRect: 对象,返回了目标元素的getBoundingClientRect的返回值。
intersectionRatio:对象,返回了目标元素和根元素交叉区域的getBoundingClientRect的返回值。
intersectionRect: 数字,目标元素的可见比例,相当于二者重合了多少。
isIntersecting: 布尔值,目标元素与根元素相交是否相交
(如果相交,则返回 true ,若为true则说明至少到达了一个阈值,如果为false,说明目标元素在阈值范围内不可见)
rootBounds: 对象,返回了根元素的getBoundingClientRect的返回值。
target:对象,目标元素,是个dom
time: 数字,从首次创建观察者到触发指定阈值发生交叉的时间
因此通过观察器的回调参数可以做到精确监听目标的,通过这些参数可以更好的支持如下拉加载技术上报等场景,由于该属性是原生api因此比起频繁的操作dom会来的更丝滑。
四、观察器的实例方法
通过上文我们已经说明了,如何使用交叉观察器,并介绍了观察器的实例属性及回调参数
下面说一下观察器的实例方法!
// 生成交叉观察器 const observer = new IntersectionObserver(callback, options); // 获取目标元素 let target = document.getElementById("target"); // 监听目标元素 observer.observe(target);
可以看到监听目标元素的时候用了 observe 方法,这就是观察器的实例方法之一—— 监听目标元素
观察器的实例方法一共有四个
disconnect:停止对所有的目标元素的观察
observe:监听目标元素(可监听多个)
takeRecords:返回一个IntersectionObserverEntry对象数组(观察器回调参数就是这个),里面存放的各个目标元素的相交信息
unobserve:停止对一个指定目标元素的观察
通过代码来演示下上述方法:
<template> <div class="home"> <div id="target1" class="target1-item"></div> <div id="target2" @click="unobserve()" class="target2-item"></div> <div style="margintop: 10px" @click="disconnect">disconnect</div> </div> </template> <script> export default { name: "HomeView", data() { return { observer: {}, }; }, mounted() { // 可见性发生变化后的回调 function callback(data) { console.log(data[0].target.id); } // 生成交叉观察器 this.observer = new IntersectionObserver(callback); // 获取目标元素1 let target1 = document.getElementById("target1"); // 获取目标元素2 let target2 = document.getElementById("target2"); // 监听目标元素1 this.observer.observe(target1); // 监听目标元素2 this.observer.observe(target2); }, methods: { unobserve() { let target2 = document.getElementById("target2"); // 停止监听目标元素2 this.observer.unobserve(target2) }, disconnect() { // 停止监听所有目标元素 this.observer.disconnect(); }, }, }; </script>
代码效果如下所示:
可以看到一开始是监听元素1和元素2,接着停止元素2的监听,最后停止所有元素的监听
加载全部内容