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

IntersectionObserver

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

用法

new IntersectionObserver(callback, options)

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

接收两个参数 callbackoptions,回调函数中参数 entriesIntersectionObserverEntry 实例。描述了 目标元素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 监听大小的改变,当 widthheight 被修改时会触发回调。也可以做 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