likes
comments
collection
share

【前端基础复盘】MutationObserver 接口的基本使用

作者站长头像
站长
· 阅读数 60

MutationObserver

基本概念

监听指定DOM节点,当节点(指定的配置选项)修改时异步执行回调。

观察对象:整个文档、DOM树的一部分、某个元素、元素属性、子节点、文本或前三者任意组合的变化。

目的: 1. 取代废弃的 MutationEvent

基本用法

MutationObserver实例调用 MutationObserver构造函数并传入一个回调函数来创建一个观察者

let observer = new MutationObserver(()=>console.log('Dom was changed'))

observer()方法

MutationObserver 实例不会关联DOM的任何部分

所以要想将 observer 和 DOM 关联需要使用 MutationObserver 提供的 observe() 方法

observe() 方法有两个必传的参数:

  • 需要观察变化的 DOM 节点

  • MutationObserverInit 对象: 用于控制观察那些方面变化的配置选项的字典 举个例子 创建一个观察者并配置它观察 body 元素上的属性变化

let observer = new MutationObserver(() => console.log('observe body changed'))
observer.observe(document.body, { attributes: true })
console.log('DOM属性改变前', document.body.style.background)

setTimeout(() => {
  document.body.style.background = '#eeeeee'
  console.log('DOM属性改变后', document.body.style.background)
}, 1000)


setTimeout(() => {
  document.body.className = 'container'
  console.log('body 元素的 className 改变了')
}, 1000)

setTimeout(() => {
  document.body.setAttribute('data-id', 123456)
  console.log('body 元素的 dataset.id 改变了')
}, 1000)

控制台 输出

【前端基础复盘】MutationObserver 接口的基本使用

回调和MutationRecord

每个回调函数都会收到一个 MutationRecord 实例的数组。

let observer = new MutationObserver( (mutationRecords) => console.log(mutationRecords) );

mutationRecords 实例中包含了监听的节点的变化信息,比如该节点发生了什么变化、节点的哪一部分收到了影响。

let observer = new MutationObserver((MutationRecord) => {
  console.log('<body> attribute changed')
  console.log('MutationRecord', MutationRecord)
})
observer.observe(document.body, { attributes: true })

setTimeout(() => {
  document.body.className = 'container'
  document.body.setAttribute('data-id', 123)
  document.body.style.background = '#eee'

  console.log('body 元素的 attributes 改变了')
}, 1000)

【前端基础复盘】MutationObserver 接口的基本使用

同时监听多个属性变化,MutationRecord 信息按顺序存入 MutationRecord 实例数组。

MutationRecord

其中属性说明如下

属性描述
target被修改影响的目标节点
type字符串,表示变化的类型:“attribute”、"characterData"或"childList"
oldValue如果在 MutationObserverInit 对象(observe方法的第二个参数)中启用(attributeOldValue 或characterData OldValue 为 true),"attribute"或“characterData”的变化实践会设置这个属性为被替代的值,"childList"类型的变化始终将这个属性设置为 null。详见 下面oldValue 示例
attributeName当变化的是Attributes 类型时,这里为被修改属性的名字,其它类型为 null
attributeNameSpace对于使用了命名空间的“attributes”类型的变化,这里保存被修改属性的名字,其它变化事件为null
addedNodes对于 “childList” 类型的变化,返回包含变化中添加节点的 NodeList 默认为空NodeList
removeNodes对于 “childList” 类型的变化,返回包含变化中删除节点的 NodeList 默认为空NodeList
previousSibling对于 “childList” 类型的变化,返回包含变化节点的前一个同胞Node默认为null
nextSibling对于 “childList” 类型的变化,返回包含变化节点的后一个同胞Node默认为null

关于oldValue的示例

let observer = new MutationObserver((MutationRecord) => {
  console.log('<body> attribute changed')
  console.log('MutationRecord', MutationRecord)
})
observer.observe(document.body, {
  attributes: true,
  attributeOldValue: true,
  characterDataOldValue: true,
})

setTimeout(() => {
  document.body.className = 'container'
  document.body.className = 'container2'
  document.body.setAttribute('data-id', 123)
  document.body.setAttribute('data-id', 456)
  document.body.style.background = '#eee'
  console.log('body 元素的 attributes 改变了')
}, 1000)

【前端基础复盘】MutationObserver 接口的基本使用

回调函数的第二个参数

回调函数的第二个参数是 观察变化的 MutationObserver 实例

disconnect()方法

默认情况下,被观察的对象只要不被垃圾回收,MutationObserver 的回调就会响应DOM变化事件,从而被执行。要提前终止执行回调,可以使用 disconnect() 方法。

let observer = new MutationObserver((MutationRecord) => {
  console.log('<body> attribute changed')
  console.log('MutationRecord', MutationRecord)
})
observer.observe(document.body, {
  attributes: true,
  attributeOldValue: true,
  characterDataOldValue: true,
})
document.body.className = 'container'
document.body.className = 'container2'
document.body.setAttribute('data-id', 123)
document.body.setAttribute('data-id', 456)
document.body.style.background = '#eee'
observer.disconnect()
document.body.setAttribute('data-id', 789) // 没有日志输出了,不会再触发回调

要想让已经加入任务队列的回调执行,可以使用 setTimeout()让已经入列的回调执行完毕再调用 disconnect()。

let observer = new MutationObserver((MutationRecord) => {
  console.log('<body> attribute changed')
  console.log('MutationRecord', MutationRecord)
})
observer.observe(document.body, {
  attributes: true,
  attributeOldValue: true,
  characterDataOldValue: true,
})

document.body.className = 'container'
document.body.className = 'container2'
document.body.setAttribute('data-id', 123)
document.body.setAttribute('data-id', 456)
document.body.style.background = '#eee'
// 输出上面已经入列的回调
setTimeout(() => {
  observer.disconnect()
  document.body.setAttribute('data-id', 789) // 没有日志输出了,不会再触发回调
}, 0)

复用 observe

多次调用 observe()方法,可以复用一个 MutationObserver 对象观察多个不同的目标节点。

MutationRecord 的 target 属性可以标识发生变化事件的目标节点。

let observer = new MutationObserver((MutationRecord) => {
  console.log(MutationRecord.map((item) => item.target))
})
let childA = document.createElement('div')
let childB = document.createElement('span')
document.body.append(childA)
document.body.append(childB)
observer.observe(childA, { attributes: true })
observer.observe(childB, { attributes: true })

childA.setAttribute('id', '10086')
childB.setAttribute('id', '10000')

控制台输出结果

【前端基础复盘】MutationObserver 接口的基本使用

使用 disconnect() 方法 会中断所有节点的监听。

重用 MutationObserver

调用 disconnect()并不会销毁 MutationObserver 还可以重新使用这个观察者,再将

它关联到新的目标节点。

下面的示例在两个连续的异步块中先断开然后又恢复了观察者与元素

的关联

下面写一个两个连续的异步程序线断开然后再重新连接观察者

let observer = new MutationObserver((MutationRecord) => {
  console.log(MutationRecord.map((item) => item.target))
})
let childA = document.createElement('div')
let childB = document.createElement('span')
document.body.append(childA)
document.body.append(childB)
observer.observe(childA, { attributes: true })
observer.observe(childB, { attributes: true })

childA.setAttribute('id', '10086')
childB.setAttribute('id', '10000')

setTimeout(() => {
  observer.disconnect() // 执行完上面已在任务队列的回调函数后,断开连接
  childB.setAttribute('id', '10000') // 不再监听和触发回调函数
}, 0)
// 重新建立连接
setTimeout(() => {
  observer.observe(childA, { attributes: true })
  observer.observe(childB, { attributes: true })
  childA.setAttribute('id', '10086')  
  childB.setAttribute('id', '10000') // 触发回调函数
}, 0)

【前端基础复盘】MutationObserver 接口的基本使用

转载自:https://juejin.cn/post/7272938670338883603
评论
请登录