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

MutationObserver

基本概念

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

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

目的: 1. 取代废弃的 MutationEvent

基本用法

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

javascript 复制代码
let observer = new MutationObserver(()=>console.log('Dom was changed'))

observer()方法

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

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

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

  • 需要观察变化的 DOM 节点

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

javascript 复制代码
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)

控制台 输出

回调和MutationRecord

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

javascript 复制代码
let observer = new MutationObserver( (mutationRecords) => console.log(mutationRecords) );

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

javascript 复制代码
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)

同时监听多个属性变化,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的示例

javascript 复制代码
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 实例

disconnect()方法

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

javascript 复制代码
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()。

javascript 复制代码
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 属性可以标识发生变化事件的目标节点。

javascript 复制代码
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')

控制台输出结果

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

重用 MutationObserver

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

它关联到新的目标节点。

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

的关联

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

javascript 复制代码
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)
相关推荐
然我16 分钟前
防抖与节流:如何让频繁触发的函数 “慢下来”?
前端·javascript·html
鱼樱前端20 分钟前
2025前端人一文看懂 Broadcast Channel API 通信指南
前端·vue.js
烛阴1 小时前
非空断言完全指南:解锁TypeScript/JavaScript的安全导航黑科技
前端·javascript
鱼樱前端1 小时前
2025前端人一文看懂 window.postMessage 通信
前端·vue.js
快乐点吧1 小时前
【前端】异步任务风控验证与轮询机制技术方案(通用笔记版)
前端·笔记
pe7er2 小时前
nuxtjs+git submodule的微前端有没有搞头
前端·设计模式·前端框架
七月的冰红茶2 小时前
【threejs】第一人称视角之八叉树碰撞检测
前端·threejs
爱掉发的小李2 小时前
前端开发中的输出问题
开发语言·前端·javascript
Dolphin_海豚2 小时前
一文理清 node.js 模块查找策略
javascript·后端·前端工程化
祝余呀2 小时前
HTML初学者第四天
前端·html