DOM规范 - MutationObserver接口观察DOM变化

更新日期: 2022-01-01 阅读: 1.8k 标签: 规范

一、MutationObserver 接口说明

此接口可以在 dom 被修改时异步执行回调。使用 MutationObserver 可以观察整个文档、DOM 树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。

DOM3 中新引进 MutationObserver 接口是为了取代废弃的 DOM2 中的 MutationEvent。

二、基本用法

MutationObserver 的实例要通过 MutationObserver 构造函数并传入一个回调函数来创建。

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

1. observe() 方法

新创建的 MutationObserver 实例不会关联 DOM 的任何部分。需要使用 ovserve()方法与 DOM 关联起来。

此方法需传两个必须的参数:

  • 要观察其变化的 DOM 节点
  • 一个 MutationObserverInit 对象
MutationObserverInit 对象:用于控制哪些方面的变化,是一个键/值对形式配置选项的字典。
// 创建一个观察者(observer)并配置它观察 body 元素上的属性变化

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

document.body.className = 'foo'
console.log('Changed body class')

// Changed body class
// body attributes changed~

执行上述代码后,body 元素上的任何属性发生变化都会被这个 MutationObserver 实例发现,然后会异步执行注册的回调函数。而 body 元素后代的修改或其他非属性变化修改都不会触发回调进入任务队列。

注意,回调中的 console.log()是后执行的,这表明回调并非与实际的 DOM 变化同步执行。

2. 回调与 MutationRecord 参数

每个回调都会受到一个 MutationRecord 实例的数组。MutationRecord 实例包含信息包括发生了什么变化、以及 DOM 的哪一部分受到了影响。回调的第二个参数是 MutationObserver 的实例。

// 连续修改会生成多个MutationRecord实例。回调执行时会受到包含所有这些实例的数组,顺序为变化事件顺序。

let observer = new MutationObserver((MutationRecords, mutationObserver) => {
  console.log(MutationRecords, mutationObserver)
})
observer.observe(document.body, { attributes: true })

document.body.setAttribute('foo', 'bar')
document.body.className = 'testName'

// [MutationRecord, MutationRecord], MutationObserver

执行效果如下图:


MutationRecord 实例属性说明

属性说明
target被修改影响的目标节点
type字符串,表示变化的类型:"attributes"、"characterData"、"childList"
oldValue如果在 MutationObserverInit 对象中启用(attributeOldValue 或 characterData OldValue 为 true),"attributes" 或 "characterData" 的变化事件会设置这个属性为被替代的值"childList" 类型的变化始终将这个属性设置为 null
attributeName对于 "attributes" 类型的变化,这里保存被修改属性的名字。其他变化事件会将此设置为 null
attributeNamespace对于使用了命名空间的 "attributes" 类型的变化,这里保存被修改属性的名字。其他变化事件会将此设置为 null
addedNodes对于 "childList" 类型的变化,返回包含变化中添加节点的 NodeList。默认为空 NodeList
removedNodes对于 "childList" 类型的变化,返回包含变化中删除节点的 NodeList。默认为空 NodeList
previousSibling对于 "childList" 类型的变化,返回变化节点的前一个同胞 Node。默认为 null
nextSibling对于 "childList" 类型的变化,返回变化节点的后一个同胞 Node。默认为 null

3. disconnet() 方法

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

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

document.body.className = 'foo'

// (没有日志输出)
重用 MutationObserver:调用 disconnet() 方法并不会结束 MutationObserver 的生命。还可以重新使用这个观察者,再讲它关联到新的目标节点。

三、MutationObserverInit 观察范围

MutationObserverInit 对象用于控制对目标节点的观察范围。例:属性变化、文本变化和节点变化。

属性说明
subtreeboolean,表示除了目标节点,是否观察其子树(后代)。默认为 false,只观察目标节点的变化
attributesboolean,表示是否观察目标节点的属性变化。默认为 false
attributeFilter字符串数组,表示要观察哪些属性的变化。把这个值设置为 true,也会将 attributes 值转换为 true。默认为观察所有属性
attributeOldValueboolean,表示 MutationRecord 是否记录变化之前的值。把这个值设置为 true,也会将 attributes 值转换为 true。默认为 false
characterDataboolean,表示修改字符数据是否触发变化事件
characterOldValueboolean,表示 MutationRecord 是否记录变化之前的值。把这个值设置为 true,也会将 characterData 值转换为 true。默认为 false
childListboolean,表示修改目标节点的子节点是否触发变化事件。默认为 false
调用 observe() 时,MutationObserverInit 对象中 attributes、characterData、childList 属性必须至少有一项为 true。
<!-- 观察子节点 -->

<body>
  <div id="con"></div>

  <script>
    const conEle = document.getElementById('con')
    let observer = new MutationObserver((MutationRecords, mutationObserver) => {
      console.log(MutationRecords)
    })
    observer.observe(conEle, { childList: true })

    conEle.appendChild(document.createElement('p'))
  </script>
</body>

// [MutationRecord]

打印效果如下图:


四、异步回调与记录队列

1. 记录队列

每次 MutationRecord 被添加到 MutationObserver 的记录队列时,仅当之前没有已排期的微任务回调时(队列中微任务长度为 0),才会将观察者注册的回调(在初始化 MutationObserver 时传入)作为微任务调度到任务队列上。这样可以保证记录队列的内容不会被回调处理两次。

不过在回调的微任务异步执行期间,有可能又会发生更多的变化事件。因此被处理的回调会接收到一个 MutationRecord 实例的数组,顺序为它们进入记录队列的顺序。回调要负责处理这个数组的每一个实例,因为函数退出之后这些实例就不存在了。回调执行后,这些 MutationRecord 就用不着了,因此记录队列会被清空,其内容会被丢弃。

2. takeRecords() 方法

调用 MutationObserver 实例的 takeRecords() 方法可以清空记录队列,取出并返回其中的所有 MutationRecord 实例。

这在希望断开与观察目标的联系,但有希望处理由于调用 disconnet() 而被抛弃的记录队列中的 MutationRecord 实例时比较有用。

let observer = new MutationObserver((MutationRecords) => {
  console.log(MutationRecords)
})
observer.observe(document.body, { attributes: true })

document.body.className = 'foo'
document.body.className = 'bar'

console.log(111, observer.takeRecords())
console.log(222, observer.takeRecords())

// 111  [MutationRecord, MutationRecord]
// 222  []

五、内存与垃圾回收

将变化回调委托给微任务来执行可以保证事件同步触发,同时避免随之而来的混乱。为 MutationObserver 而实现的记录队列,可以保证即使变化事件被爆发式的触发,也不会显著的拖慢浏览器

无论如何,使用 MutationObserver 仍然不是没有代价的。因此理解什么时候避免出现这种情况就很重要了。

1. MutationObserver 的引用

MutationObserver 实例与目标节点之间的引用关系是非对称的。MutationObserver 拥有对观察目标节点的弱引用。因为是弱引用,所以不会妨碍垃圾回收程序回收目标节点。

然而,目标节点却拥有对 MutationObserver 的强引用。如果目标节点从 DOM 中被移除,随后被垃圾回收,则关联的 MutationObserver 也会被垃圾回收。

2. MutationRecord 的引用

记录队列中的每个 MutationRecord 实例至少包含对已有 DOM 节点的一个引用。如果变化是 childList 类型,则会包含多个节点的引用。记录队列和回调处理的默认行为是耗尽这个队列,处理每个 MutationRecord,然后让它们超出作用域并被垃圾回收。

有时候可能需要保存某个观察者的完整变化记录。保存这些 MutationRecord 实例,也就会保存它们引用的节点,因而会妨碍这些节点的回收。如果需要尽快地释放内存,建议从每个 MutationRecord 中抽取最有用的信息,然后保存到一个新对象中,最后抛弃 MutationRecord。

原文来自:https://www.cnblogs.com/Faith-Yin/archive/2022/01/01/15755677.html

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/10997

相关推荐

什么是驼峰命名?骆驼式命名法规范

骆驼式命名法(Camel-Case)又称驼峰式命名法,是电脑程式编写时的一套命名规则(惯例)。正如它的名称CamelCase所表示的那样,是指混合使用大小写字母来构成变量和函数的名字

web开发,前后分离接口规范

目前我们现在用的前后端分离模式属于第一阶段,下一阶段可以在前端工程化方面,对技术框架的选择、前端模块化重用方面,可多做考量。也就是要迎来“==前端为主的 MV* 时代==”。

CSS规范

CSS 指层叠样式表 (Cascading Style Sheets),定义如何显示 HTML 元素,但由于 CSS 天生全局性,随着项目复杂度增加,极易出现样式覆盖以及其它的问题。

前端变量命名规范

程序开发过程中变量命名不仅是一个头疼问题,也是一个对开发者综合素质的检验,它会直接影响到代码的最终交付质量、代码Review人员心智承受力。如何写出具有创造性、优雅性、易读性的高质量代码,需要开发者在实际工作中不断总结、提炼

js中箭头函数的编码规范,如何更好的使用箭头函数

当您必须使用匿名函数,请使用箭头函数表示法,它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。

Web前端开发规范手册

文件命名规则:文件名称统一用小写的英文字母、数字和下划线的组合。引文件统一使用index.htm index.html index.asp文件名(小写),图片的名称分为头尾两部分,用下划线隔开,头部分表示此图片的大类性质

W3C标准及规范

W3C的意思是万维网联盟(World Wide Web Consortium),创建于1994年10月,是一个会员组织,它的工作是对web进行标准化--->W3C 致力于实现所有的用户都能够对 web 加以利用

web前端js中ES6的规范写法

引号的使用,单引号优先(如果不是引号嵌套,不要使用双引号)、空格的使用问题:(关键字后 符号后 排版 函数 赋值符号= )等、不写没有使用过的变量,如果定义了一个变量,后来一直没有参与过运算,那么不应该定义这个变量...

JavaScript 命名约定最佳实践

在开发过程中,遵循标准的命名约定可以提高代码的可读性。下面就来看看 JavaScript 中命名约定的最佳实践。JavaScript 变量名称是区分大小写的,大写和小写字母是不同的。

用standard来管理JavaScript 代码规范

standard是一个开源的JS代码规范库,制定了所谓standard(标准)的JS代码规范,配合编辑器插件可以实时检查代码规范以及语法错误,通过执行命令检查代码规范以及语法错误,自动修复(可以直接修复的)不合规范的代码,使其符合规范

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!