比如 元素从不可见到可见、元素大小的改变、元素的属性和子节点的修改等等,这类事件的监听,浏览器提供了 5 种 Observer 来监听这些变动:MutationObserver、IntersectionObserver、PerformanceObserver、ResizeObserver、ReportingObserver

IntersectionObserver

它可以监听一个元素和可视区域相交部分的比例,然后在可视比例达到某个阈值的时候触发回调。

用法

new IntersectionObserver(callback, options)

const options = {
  root: null,
  rootMargin: 0,
  thresholds: 1,
}
const io = new IntersectionObserver((entries) => {
  console.log(entries)
  // TODO...
}, options)

接收两个参数 callback 和 options,回调函数中参数 entries 是 IntersectionObserverEntry 实例。描述了 目标元素 与 root 的交叉状态。

回调函数

属性 描述
boundingClientRect 返回包含目标元素的边界信息,返回结果与 element.getBoundingClientRect() 相同
intersectionRatio 返回目标元素出现在可视区的比例
intersectionRect 用来描述 root 和目标元素的相交区域
isIntersecting 返回一个布尔值。1. 如果目标元素出现在 root 可视区,返回 true。2. 如果从 root 可视区消失,返回 false
rootBounds 用来描述交叉区域观察者(intersection observer)中的根.
target 目标元素:与根出现相交区域改变的元素 (Element)
time 返回一个记录从 IntersectionObserver 的时间原点到交叉被触发的时间的时间戳

注意:表格中加粗的属性是比较常用的判断条件

options 选项

options 是一个对象,可选参数:

属性 描述
root 所监听对象的具体祖先元素。如果未传入值或值为 null,则默认使用顶级文档的视窗(一般为 html)
rootMargin 计算交叉时添加到根(root)边界盒 bounding box 的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。所有的偏移量均可用像素(px)或百分比(%)来表达, 默认值为"0px 0px 0px 0px"
threshold 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会触发 callback。默认值为 0

实例方法

属性 描述
observe() 开始监听一个目标元素
unobserve() 停止监听特定目标元素
takeRecords() 返回所有观察目标的 IntersectionObserverEntry 对象数组
disconnect() 使 IntersectionObserver 对象停止全部监听工作

应用场景:

  1. 图片懒加载
  2. 埋点曝光
  3. 滚动动画

MutationObserver

它可以监听对元素的属性的修改、对它的子节点的增删改

用法

var observer = new MutationObserver(callback)

/*
 *观察者回调
 *records:变化记录数组
 *instance:观察者对象本身
 */
const callback = (records, instance) => {
  console.log(records)
  console.log(instance)
  records.map((record) => {
    console.log('Mutation Type: ' + record.type)
    console.log('Mutation Change Attribute: ' + record.attributeName)
    console.log('Previous attribute value: ' + record.oldValue)
  })
}
observer = new MutationObserver(callback)

callback,即回调函数接收两个参数,第一个参数是一个包含了所有 MutationRecord 对象的数组,第二个参数则是这个 MutationObserver 实例本身。

Mutation 事件列表

名称 描述
DOMAttrModified 节点属性变更
DOMAttributeNameChanged 节点属性属性节点名字改变
DOMCharacterDataModified 节点中的文本节点改变
DOMElementNameChanged 节点移除
DOMNodeInserted 节点子节点插入
DOMNodeRemoved 节点子节点移除
DOMNodeInsertedIntoDocument 节点插入文档
DOMSubtreeModified 节点子节点修改

观察者 observe

//观察者配置项
var options = {
  childList: true, //观察目标节点的子节点的新增和删除
  subtree: true, //观察目标节点的所有后代节点
  attributes: true, //观察目标节点的属性节点
  attributeOldValue: true, //在attributes属性已经设为true的前提下, 将发生变化的属性节点之前的属性值记录下来
  attributeFilter: [], //一个属性名数组(不需要指定命名空间),只有该数组中包含的属性名发生变化时才会被观察到,其他名称的属性发生变化后会被忽略想要设置那些删选参数的话
  characterData: true, //如果目标节点为characterData节点(一种抽象接口,具体可以为文本节点,注释节点,以及处理指令节点)时,也要观察该节点的文本内容是否发生变化
  characterDataOldValue: true, //在characterData属性已经设为true的前提下,将发生变化characterData节点之前的文本内容记录下来(记录到下面MutationRecord对象的oldValue属性中)
}

//待观察的DOM节点
var element = document.getElementById('text')

//执行观察
observer.observe(element, options)

实例方法

名称 描述
disconnect() 停止观察
takeRecords() 清除变动记录

应用场景

  1. Vue 源码里尝试使用 mutationObserver 来做降级

PerformanceObserver

ResizeObserver

问题:窗口我们可以用 addEventListener 监听 resize 事件,那元素呢?

元素可以用 ResizeObserver 监听大小的改变,当 width、height 被修改时会触发回调。也可以做 echarts 图表自适应

用法

const box = document.querySelector('#box')

setTimeout(() => {
  box.style.width = '200px'
}, 1000)

const resizeObserver = new ResizeObserver((entries) => {
  console.log('当前大小', entries)
})
resizeObserver.observe(box)

可以拿到元素和它的位置、尺寸。这样我们就实现了对元素的 resize 的监听。

ReportingObserver