How Javascript works (Javascript工作原理) (十) 使用 MutationObserver 监测 DOM 变化
个人总结:
这篇文章介绍了几种监测DOM变化的方法,重点介绍的是一个新浏览器API叫做MutationObserver。
注意:不要和Vue.js种 Object.defineProperty() 的Getter,Setter弄混,还有proxy...那些是监测对象的变化,这个说的是监测DOM的变化。
附:
1.Object.defineProperty(obj, props) (其实还有Object.defineProperties这个api..).
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
使用:
var obj = {};
Object.defineProperty(obj,'a', { set:function(){ console.log(1) }}); //setter
obj.a = 3 // 输出了1
2.Proxy
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
示例:
网络应用在客户端日益复杂,这是由很多因素的造成的,比如需要更加丰富的界面交互以提供更加复杂的应用功能,实时计算等等。
网络应用的日益复杂导致无法知晓其生命周期中指定时刻准确的交互界面状态。
如果你正在构建一些框架或者一个库,这会更加的困难,比如,你无法通过监测 DOM 来响应并执行一些特定的操作。
概述
MutationObserver 是现代浏览器提供的用来检测 DOM 变化的网页接口。你可以使用这个接口来监听新增或者删除节点,属性更改,或者文本节点的内容更改。
可以干点啥好呢?
你可以在以下几种情况信手拈来 MutationObserver 接口。比如:
- 通知用户当前所在的页面所发生的一些变化。
- 通过使用一些很棒的 JavaScript 框架来根据 DOM 的变化来动态加载 JavaScript 模块。
- 可能当你在开发一个所见即所得编辑器的时候,使用 MutationObserver 接口来收集任意时间点上的更改,从而轻松地实现撤消/重做功能。

这只是几个 MutationObserver 的使用场景。
如何使用 MutationObserver
在应用中集成 MutationObserver 是相当简单的。通过往构造函数 MutationObserver 中传入一个函数作为参数来初始化一个 MutationObserver 实例,该函数会在每次发生 DOM 发生变化的时候调用。MutationObserver 的函数的第一个参数即为单个批处理中的 DOM 变化集。每个变化包含了变化的类型和所发生的更改。
var mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
创建的实例对象拥有三个方法:
observe-开始进行监听。接收两个参数-要观察的 DOM 节点以及一个配置对象。disconnect-停止监听变化。takeRecords-触发回调前返回最新的批量 DOM 变化。
以下为开始监听的代码片段:
// 开始监听页面根元素 HTML 变化。
mutationObserver.observe(document.documentElement, {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
现在,假设你写了一个简单的 div 元素:
<div id="sample-div" class="test"> Simple div </div>
可以使用 jQuery 来移除 div 的 class 属性:
$("#sample-div").removeAttr("class");
当调用 mutationObserver.observe(…) 就可以开始监听 DOM 变化。
当每次发生 DOM 变化的时候,会打印出各个 MutationRecord 日志信息:

这一变化是由移除 class 属性所引起的。
最后,如果想停止监听 DOM 变化可以使用如下方法:
// MutationObserver 停止监听 DOM 变化
mutationObserver.disconnect();
现在,MutationObserver 浏览器兼容情况很好:

替代方法
然而,之前 MutationObserver 并没有被广泛使用。那么,当没有 MutationObserver 的时候,开发者是如何解决监听 DOM 变化的呢?
有几下几种可用的方法:
- 轮询
- MutationEvents
- CSS 动画
轮询
最简单且粗糙的方法即使用轮询。使用浏览器内置的 setInterval 网页接口你可以创建一个定时任务来定时检查 DOM 的变化。当然了,这个方法会显著地减弱网络应用/网站的性能。
其实,这是可以理解为脏检查,如果有使用过 AngularJS 应该会有看过其脏检查所导致的性能问题。在我的另一个系列里面有稍微介绍了下,具体可以查看这里。
CSS 动画
依靠 CSS 动画 是一个有点令人感到新奇的替代方案。这听起来会让人有些困惑。大体上,实现思路是这样的,创建一个动画,一旦在 DOM 中添加一个元素就会触发该动画。开始执行 CSS 动画的时候就会触发 animationstart 事件:假设为该事件添加事件监听器,就可以准确知晓 DOM 中添加元素的时机。动画的运行时间周期必须非常的短以便让用户感知不到,即体验更佳。
首先,需要一个父级元素,在里面监听节点添加事件:
<div id=”container-element”></div>
为了处理节点的添加,需要创建关键帧序列动画,该序动画在添加节点的时候启动:
@keyframes nodeInserted {
from { opacity: 0.99; }
to { opacity: 1; }
}
创建好关键帧之后,在需要监听的元素上应用动画。注意到那个短暂的持续时间-在浏览器端动画痕迹会非常平滑(即用户会感觉不到有动画发生):
#container-element * {
animation-duration: 0.001s;
animation-name: nodeInserted;
}
这样会为 container-element 的所有后代节点添加动画。当动画结束,触发 insertion 事件。
我们需要创建一个函数作为事件监听器。在函数内部,开始必须使用 event.animationName 代码进行检查,确保是我们所监听的动画。
var insertionListener = function(event) {
// 确保是所监听的动画
if (event.animationName === "nodeInserted") {
console.log("Node has been inserted: " + event.target);
}
}
为父元素绑定事件监听器:
document.addEventListener(“animationstart”, insertionListener, false); // standard + firefox
document.addEventListener(“MSAnimationStart”, insertionListener, false); // IE
document.addEventListener(“webkitAnimationStart”, insertionListener, false); // Chrome + Safari
这里采用了事件委托。
CSS 动画浏览器支持情况:
MutationObserver 有几点优势。本质上,它会监听 DOM 可能发生的每个变化并且性能更优,因其会批量 DOM 变化之后才触发回调事件。总之,MutationObserver 的兼容性很好,并且还有一些垫片,这些垫片底层是基于 MutationEvents 的。How Javascript works (Javascript工作原理) (十) 使用 MutationObserver 监测 DOM 变化的更多相关文章
- JavaScript 工作原理之十-使用 MutationObserver 监测 DOM 变化
原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第十章. 网络 ...
- JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化
摘要: 掌握MutationObserver. 这是专门探索 JavaScript 及其所构建的组件的系列文章的第10篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工 ...
- How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈
个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...
- How Javascript works (Javascript工作原理) (四) 事件循环及异步编程的出现和 5 种更好的 async/await 编程方式
个人总结: 1.讲解了JS引擎,webAPI与event loop合作的机制. 2.setTimeout是把事件推送给Web API去处理,当时间到了之后才把setTimeout中的事件推入调用栈. ...
- JavaScript定时器的工作原理(翻译)
JavaScript定时器的工作原理(翻译) 标签(空格分隔): JavaScript定时器 最近在看ajax原理的时候,看到了一篇国外的文章,讲解了JavaScript定时器的工作原理,帮助我很好的 ...
- [中英对照]How PCI Works | PCI工作原理
How PCI Works | PCI工作原理 Your computer's components work together through a bus. Learn about the PCI ...
- [中英对照]How PCI Express Works | PCIe工作原理
How PCI Express Works | PCIe工作原理 PCI Express is a high-speed serial connection that operates more li ...
- How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧
个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析——即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的 ...
- How Javascript works (Javascript工作原理) (十五) 类和继承及 Babel 和 TypeScript 代码转换探秘
个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCall ...
随机推荐
- hibernate---crateria
Leslie 趁还没忘掉,赶快记录下来 Hibernate中Criteria的完整用法 转自:http://www.360doc.com/content/090313/10/26262_2794855 ...
- 洛谷1613 跑路 倍增 + Floyd
首先,我们一定要认识到本题中的最短时间所对应的道路不一定是在起点到终点的最短路.例如,起点到终点的最短路为 151515 ,那么对 151515 进行二进制拆分的话是 111111111111 ,这时 ...
- 闭包、this、ES6特性
声明一个函数 function fun1 函数内定义一个变量比如 var x=1 如果这时候在function内声明一个函数 fun2 fun2中对这个函数外面的x进行操作 这时候把fun2的地址付给 ...
- 如何让select中的滚动条自动定位到框中选中项的位置
document.getElementById("hidScrollTop").value = document.getElementById("slcYZYongFa& ...
- STM32 SPI 发送第一个数据不成功问题
STM32的标准库,跟HAL库都是很实用的, 在使用SPI库的过程中一定要注意时序的问题. 我在调试SPI过程中,调试了两个IC,都是用HAL库, 第一个IC没出问题,第二个IC出现了第一次发送数据不 ...
- -bash: wget 未找到命令的解决办法
在Linux操作系统中,我们会经常要用到wget下载文件.wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性. 在linux中使用wget时,若报-bash: wget: comman ...
- java中的接口中的方法
题目如下:(多选题)请选择以下接口定义正确的方法() A:public static void main (String[] args); B:private void test(); C:publi ...
- 2015 Multi-University Training Contest 5 hdu 5348 MZL's endless loop
MZL's endless loop Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Oth ...
- Objective-C 和 Core Foundation 对象相互转换
iOS同意Objective-C 和 Core Foundation 对象之间能够轻松的转换: CFStringRef aCFString = (CFStringRef)aNSString; NSSt ...
- 怎么用命令行运行jar文件
假设你配置好了jre环境,你如今有一个打包好的jar文件,你能够这样子開始运行 java -classpath example.jar mainClass -classpath告诉虚拟机在哪里找类的字 ...