IntersectionObserver

使用场景

  • 判断 DOM 出现在屏幕上以记录浏览量
  • 实现 img 标签的懒加载
  • 做一些数据上报...

什么是 IntersectionObserver?

**IntersectionObserver**接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。

当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。

重点看这里监听目标元素与其祖先或视窗交叉状态的手段,其实就是观察一个元素是否在视窗可见。

可以看到,交叉了就是说明当前元素在视窗里,当前就是可见的了。

API

var io = new IntersectionObserver(callback, options)

其实就是一个简单的构造函数。

以上代码会返回一个IntersectionObserver实例,callback是当元素的可见性变化时候的回调函数,options是一些配置项(可选)。

我们使用返回的这个实例来进行一些操作。

io.observe(document.querySelector('img'))  开始观察,接受一个DOM节点对象
// 下面两个函数很有用,在合适的时候停止和关闭观察器有利于节省性能
io.unobserve(element)   停止观察 接受一个element元素
io.disconnect() 关闭观察器

options

root

用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素

threshold

用来指定交叉比例,决定什么时候触发回调函数,是一个数组,默认是[0]。他代表的是观察的元素出现在视图中的高度(或观察的 root 节点)与他自身的高度的百分比, 如果不关闭观察器,则回调函数会在数组中的每个临界点触发。这是一个左开右闭的区间,之所以这样说请看案例:

const options = {
    root: null,
    threshold: [0, 0.5, 1]
}
var io = new IntersectionObserver(callback, options)
io.observe(document.querySelector('img'))

上面代码,我们指定了交叉比例为 0,0.5,1,当观察元素 img0%、50%、100%时候都会触发回调函数,你以为只会触发三次吗?当然不是,其实他触发了四次回调,如下图

intersectionRatio 代表了交叉比例,从图中可以看出在元素还没出现在视图中时已经调用了一次回调函数,此时 intersectionRatio 为 0,这就是上面提到的左开右闭,0 这个临界点比较特殊,他会在左右区间都触发回调。(如不关闭观察器则回调会在每次经过临界点触发)

rootMargin

用来扩大或者缩小视窗的的大小,使用 css 的定义方法,10px 10px 30px 20px表示 top、right、bottom 和 left 的值

const options = {
    root: document.querySelector('.box'),
    threshold: [0, 0.5, 1],
    rootMargin: '30px 100px 20px'
}

为了方便理解,我画了张图,如下

img

options

首先我们来看下图上的问题,蓝线是什么呢?他就是咱们定义的 root 元素,我们添加了rootMargin属性,将视窗的增大了,虚线就是现在的视窗,所以元素现在也就在视窗里面了。

由此可见,root 元素只有在rootMargin为空的时候才是绝对的视窗。

说了简单的 options,接下来我们看下callback

callback

上面我们说到,当元素的可见性变化时,就会触发 callback 函数。

callback 函数会触发两次,元素进入视窗(开始可见时)和元素离开视窗(开始不可见时)都会触发

var io = new IntersectionObserver(entries => {
  console.log(entries)
})

io.observe($0)

以上代码,请在 chrome 控制台进行调试,这里我使用了$0选择了上一次我审查元素的选择的节点

运行结果如下

img

运行结果

我们可以看到 callback 函数有个entries参数,它是个IntersectionObserverEntry对象数组,接下来我们重点说下 IntersectionObserverEntry 对象

IntersectionObserverEntry

IntersectionObserverEntry提供观察元素的信息,有七个属性。

boundingClientRect 目标元素的矩形信息 intersectionRatio 相交区域和目标元素的比例值 intersectionRect/boundingClientRect 不可见时小于等于 0 intersectionRect 目标元素和视窗(根)相交的矩形信息 可以称为相交区域 isIntersecting 目标元素当前是否可见 Boolean 值 可见为 true rootBounds 根元素的矩形信息,没有指定根元素就是当前视窗的矩形信息 target 观察的目标元素 time 返回一个记录从IntersectionObserver的时间到交叉被触发的时间的时间戳

上面几个矩形信息的关系如下

img

关系.png

👇 划重点

intersectionRatioisIntersecting是用来判断元素是否可见的,押题咯...

懒加载

好了,通过上面一些概念我们大概了解了IntersectionObserver是个什么东西,接下来我们用它来写点代码,写什么呢?没错就是懒加载。

通过 IntersectionObserver 来实现懒加载,就简单的多了,我们只需要设置回调,判断当前元素是否可见,再进行渲染操作就行了,而不用去关心内部的计算。

主要代码如下

const io = new IntersectionObserver(() => {
  // 实例化 默认基于当前视窗
})

let ings = document.querySelectorAll('[data-src]') // 将图片的真实url设置为data-src src属性为占位图 元素可见时候替换src

function callback(entries) {
  entries.forEach(item => {
    // 遍历entries数组
    if (item.isIntersecting) {
      // 当前元素可见
      item.target.src = item.target.dataset.src // 替换src
      io.unobserve(item.target) // 停止观察当前元素 避免不可见时候再次调用callback函数
    }
  })
}

imgs.forEach(item => {
  // io.observe接受一个DOM元素,添加多个监听 使用forEach
  io.observe(item)
})

⚠️ 注意

目前 IntersectionObserver 是一个实验中的功能,请酌情使用。

img

转载作者:an 祭 链接:https://www.jianshu.com/p/84a86e41eb2b

上次更新: 1/4/2020, 8:02:19 PM