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

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

一、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://www.fly63.com/article/detial/10997

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

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

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

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

用standard来管理JavaScript 代码规范

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

Web 前端开发代码规范(基础)

对于一个多人团队来说,制定一个统一的规范是必要的,因为个性化的东西无法产生良好的聚合效果,规范化可以提高编码工作效率,使代码保持统一的风格,以便于代码整合和后期维护。

Node.js的模块加载机制(CommonJS规范)

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Node环境中,一个.js文件就称之为一个模块(module)

web前端js中ES6的规范写法

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

编码规范_html代码规范化编写

嵌套的节点应该缩进;在属性上,使用双引号,不要使用单引号;属性名全小写,用中划线做分隔符;不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的);不要忽略可选的关闭标签;

CommonJS 规范中的 module、module.exports 区别

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。module.exports属性表示当前模块对外输出的接口

W3C 代码标准规范

W3C通过设立领域(Domains)和标准计划(Activities)来组织W3C的标准活动,围绕每个标准计划,会设立相关的W3C工作组织(包括工作组、社区组、商务组等)。W3C会根据产业界的标准需求调整Domains和Activity的设置及相关的工作组设置。

css3代码书写规范

不要使用 @import 与 <link> 标签相比,@import 指令要慢很多,不光增加了额外的请求次数,还会导致不可预料的问题。CSS有些属性是可以缩写的,比如padding,margin,font等等,这样精简代码同时又能提高用户的阅读体验。

点击更多...

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