谁动了个人 DOM??!

元素的样式为什么频频被改?前端

DOM 的属性为什么离奇失踪??数组

消失的 DOM 到底是何人所为???浏览器

出现的陌生 DOM 到底是人是鬼????微信

这一切的背后是人性的扭曲仍是道德的沦丧?????异步

是无心写的 Bug 仍是有人故意而为之??????函数

让咱们跟随镜头,探寻变幻无穷的 DOM。工具

起源

最近在作项目的时候,遇到一个问题:组件化

就是一个好久之前写的页面,里面的代码很乱。而个人任务是将页面的高度与屏幕适配。在部分页面存在着几个 <iframe>,我须要调整样式使其高度与屏幕适配,可是不管我怎么调整,总会有一个 JavaScript 在不停地修改 <iframe> 的高度,使得它的高度超出屏幕而出现两个滚动条。性能

因为代码很乱,因此很难找到到底是哪一个代码在捣乱。spa

Chrome 中的解决方法

在 Chrome 中开发前端是很是开心的一件事情,由于浏览器提供了很是多的调试工具。而我常常用的一个就是:断点调试。

Chrome 提供了一个 DOM 监视的功能,当 DOM 发生变化的时候,自动暂停,这样就能很快定位是谁修改了 <iframe> 的高度了!

在 Elements 页面,选中指定的 DOM 节点,点击最前面的“...”符号(或者使用右键点击),在弹出的菜单中选择“Break On”便可,能够选择多个。

  • Subtree Modifications: 当节点树发生变化时
  • Attribute Modifications: 当节点属性发生变化时
  • Node Removal: 当节点被删除时(包括下级节点)

只要启用其中一个,在具体事件发生的时候,Chrome 就会自动中断到当前执行的脚本代码处。

因为 JavaScript 修改样式无非就是增长/删除 class,或是修改 style 属性,因此,使用 Attribute Modifications 便可找到具体是哪一个脚本在修改 DOM 了。

新的问题

在作前端开发的时候,常常会想要监听 DOM 节点的变化。当 DOM 变化的时候,触发一系列事件。

通常来讲,使用轮询的方式能够很是简单地解决这个问题,就是使用 setInterval 来不断地检查 DOM 是否发生了变化。这种方式简单粗暴,可是会遇到两个问题:时间间隔设置过长,DOM 变化响应不够及时;时间间隔设置太短,不只浪费 CPU,并且可能出现卡顿。

固然,也可使用 requestAnimationFrame 来作,原理其实和 setInterval 同样,只不过在必定程度上能够获得 DOM 变化实时响应,可是依旧是致使 CPU 运行时间片的浪费。

有没有更好的办法呢?

在旧版 DOM Events 标准中,有一个 Mutation events,能够用来监听 DOM 的变化,在 DOM 变化的时候触发事件。

在 DOM3 中定义了 9 种 Mutation 事件:DOMAttrModifiedDOMAttributeNameChangedDOMCharacterDataModifiedDOMElementNameChangedDOMNodeInsertedDOMNodeInsertedIntoDocumentDOMNodeRemovedDOMNodeRemovedFromDocumentDOMSubtreeModified

这 9 种事件能够直接经过 element.addEventListener 添加到 DOM 元素上。

可是,Mutation 事件已经被反对使用!而且从 Web 标准事件中删除了!

因为性能问题,Mutation 事件会致使 DOM 修改的性能下降 1.5~7 倍,而且不能经过移除事件来恢复性能。

而且这个事件在各个浏览器上的实现也存在差别。

因此,DOM4 开始,推荐使用 Mutation Observers 来代替 Mutation events

Mutation Observers

Mutation Observer API 能够用来监视 DOM 的变化,包括属性的变化、节点的增减、内容的变化等。

比 Mutation Events 高在哪里?

为何 Mutation Observers 要比 Mutation Events 好?

因为 Mutation Events 是监视到 DOM 发生变化时产生的事件,它会在任何一个 DOM 发生变化的时候马上被触发。而且,因为事件是同步进行的,因此若是 DOM 的变化较多,就会产生大量的事件回调,致使严重的性能问题。

Mutation Observers 虽然和 Mutation Events 很像,可是 Mutation Observers 不是事件,它是异步触发的,而且不是每次 DOM 变更都会触发,而是会等待屡次 DOM 变更完成后一次性触发,使用一个数组来记录 DOM 变更的步骤。这样一来,即便是频繁的 DOM 操做,对性能的影响也不会有多明显。

举个例子,我如今须要将一篇包含 1000 个段落的文章显示到页面上,也就是要往页面中插入 1000 个 <p></p>

若是使用 Mutation Events 的话,这时就会产生 1000 个 DOMNodeInserted 事件;而若是使用 Mutation Observers 就不同了,它只会触发一次,获得一个数组,包含了 1000 个插入节点的信息。

怎么用呢?

MutationObserver 是一个构造函数,可使用 new 来建立一个 MutationObserver 的实例。这个构造函数接受一个回调函数做为参数,也就是每次 Mutation Observers 触发时调用的函数,函数接受两个参数,第一个参数是 MutationRecord 数组,用于存储 DOM 的变化记录,第二个参数是 MutationObserver 实例自己。

MutationObserver 的实例有 3 个成员方法:observedisconnecttakeRecords

observe 用于注册监听器,接受两个参数,第一个参数是要监听的节点,第二个参数是监听的配置。

监听配置是一个对象,能够有 childListattributescharacterDatasubtreeattributeOldValuecharacterDataOldValueattributeFilter,要监听哪一种变化,只须要将对应的属性设置为 true 便可,其中 childListattributescharacterData 三者必须至少出现一个。

属性 数据类型 描述
childList boolean 观察目标增长或移除了子节点
attributes boolean 观察目标增长、删除或修改了某个属性
characterData boolean (目标为 characterData 节点时有效,包括文本节点、注释节点、处理指令节点等)文本内容发生了变化
subtree boolean 不只监视 ovserve 第一个参数指定的观察目标,同时监视全部的下级节点
attributeOldValue boolean 在监视 attributes 的时候,属性发生变化后是否要记录变化前的内容
characterDataOldValue boolean 在监视 characterData 的时候,文本内容发生变化后是否要记录变化前的内容
attributeFilter Array<string> 一个属性名数组,能够用于过滤 attributes 的变化

注册成功后,构造函数里提供的回调函数将会被调用,第一个参数就获得了变化数组。变化对象的结构包含如下属性:

属性 数据类型 描述
type String 变化类型,对应监听配置对象中的 childListattributescharacterData
target Node 变化的目标节点,若是 typeattributescharacterData,则 target 为变化节点,不然为变化节点的父节点
addedNodes NodeList 被添加的节点列表(可能为 null
removedNodes NodeList 被删除的节点列表(可能为 null
previousSibling Node 被添加或被删除的节点的前一个兄弟节点(可能为 null
nextSibling Node 被添加或被删除的节点的后一个兄弟节点(可能为 null
attributeName String 变化的属性名称(可能为 null
attributeNamespace String 变化的属性所在的 XML 命名空间(可能为 null
oldValue String 若是 typeattributescharacterData,则 oldValue 为变化前的值,不然为 null

disconnect 用于中止监听。

takeRecords 用于清空并返回当前 MutationObserver 记录的 DOM 变化步骤。

浏览器兼容性

能够看到,兼容性仍是很是好的,能够放心使用。

总结

MutationObserver 提供了比 Mutation Events 更高效、更灵活的 DOM 监视方案,能够根据本身的须要自定义监视对象,在组件化项目中能够发挥更大的价值——不须要组件内部提供接口,就能够收到组件内容变化的通知。

可是,MutationObserver 虽好,可不要滥用哦!

Chrome 提供的 Break On 功能看起来就像是 MutationObserver 的精简版,很是实用。


关注微信公众号:创宇前端(KnownsecFED),码上获取更多优质干货!

相关文章
相关标签/搜索