原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland

本系列持续更新中,Github 地址请查阅这里

这是 JavaScript 工作原理的第十章。

网络应用在客户端日益复杂,这是由很多因素的造成的,比如需要更加丰富的界面交互以提供更加复杂的应用功能,实时计算等等。

网络应用的日益复杂导致无法知晓其生命周期中指定时刻准确的交互界面状态。

如果你正在构建一些框架或者一个库,这会更加的困难,比如,你无法通过监测 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 应该会有看过其脏检查所导致的性能问题。在我的另一个系列里面有稍微介绍了下,具体可以查看这里

MutationEvents

早在 2000 年,就推出了 MutationEvents API 。虽然挺管用的,但是每个单一的 DOM 变化都会触发 mutation 事件,结果又会造成性能问题。现在,MutationEvents 接口已经被废弃,不久的将来,现代浏览器全都将停止支持该接口。

以下是 MutationEvents 的浏览器兼容情况:

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 的。

本系列持续更新中,Github 地址请查阅这里

JavaScript 工作原理之十-使用 MutationObserver 监测 DOM 变化的更多相关文章

  1. How Javascript works (Javascript工作原理) (十) 使用 MutationObserver 监测 DOM 变化

    个人总结: 这篇文章介绍了几种监测DOM变化的方法,重点介绍的是一个新浏览器API叫做MutationObserver. 注意:不要和Vue.js种 Object.defineProperty() 的 ...

  2. How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧

    个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析——即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的 ...

  3. How Javascript works (Javascript工作原理) (十五) 类和继承及 Babel 和 TypeScript 代码转换探秘

    个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCall ...

  4. How Javascript works (Javascript工作原理) (十二) 网络层探秘及如何提高其性能和安全性

    个人总结:阅读完这篇文章需要20分钟,这篇文章主要讲解了现代浏览器在网络层传输所用到的一些技术, 应当对 window.performance.timing 这个API所有了解. 这是 JavaScr ...

  5. How Javascript works (Javascript工作原理) (四) 事件循环及异步编程的出现和 5 种更好的 async/await 编程方式

    个人总结: 1.讲解了JS引擎,webAPI与event loop合作的机制. 2.setTimeout是把事件推送给Web API去处理,当时间到了之后才把setTimeout中的事件推入调用栈. ...

  6. How Javascript works (Javascript工作原理) (三) 内存管理及如何处理 4 类常见的内存泄漏问题

    个人总结: 1.两种垃圾回收机制: 1)引用标记算法:如果检测到一个对象没有被引用了,就清除它. ***这种算法不能处理循环引用的情况*** 2)标记—清除算法:从根(全局变量)开始向后代变量检测,任 ...

  7. How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧

    个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...

  8. How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈

    个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...

  9. How Javascript works (Javascript工作原理) (九) 网页消息推送通知机制

    个人总结: 1.介绍了网页消息推送通知机制 全文地址:https://github.com/Troland/how-javascript-works 这是 JavaScript 工作原理的第九章. 现 ...

随机推荐

  1. OGG-DDL复制

    http://blog.sina.com.cn/s/blog_96d348df0102vg6q.html OGG目前只支持Oracle和TeraData的ddl复制,Oracle数据库能够支持除去数据 ...

  2. UITextView输入中文时限制字数

    最近因为项目中输入用户简称时限制长度,被测试部多次提交问题,最终将解决方法分享给大家. 刚开始用最简单的方法,在textView的代理方法里面做处理 - (void)textViewDidChange ...

  3. fedora23安装firefox中的flash插件-最终解决问题是: 要给libflashplayer.so以777权限, 开始给的755权限没有实现!

    下载的flash插件是一个rpm包. ===================================== rpm查看文件属于哪个包? 要看这个rpm包安装过还是没有安装过? (如果不用-p就是 ...

  4. HttpWebRequest、WebClient、RestSharp、HttpClient区别和用途

    HttpWebRequest 已经不推荐直接使用了,这已经作为底层机制,不适合业务代码使用,比如写爬虫的时候WebClient 不想为http细节处理而头疼的coder而生,由于内部已经处理了通用设置 ...

  5. Docker安装及部署实例.Net Core

    1.什么是Docker Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱 ...

  6. Week1 - 169.Majority Element

    这周刚开始讲了一点Divide-and-Conquer的算法,于是这周的作业就选择在LeetCode上找分治法相关的题目来做. 169.Majority Element Given an array ...

  7. KETTLE——(例)简单的字段转换

    一个简单的小例子:结合数据抽取.简单的字段转换.数据输出. 资源库连接.数据输入.数据输出.参见之前的文章. ​ 基本的转换结构是这样的,我们从表中输入,选择我们需要的字段,已经对应的名称,然后将数据 ...

  8. sql中的isnull

    ISNULL 使用指定的替换值替换 NULL. 语法ISNULL ( check_expression , replacement_value ) 参数check_expression 将被检查是否为 ...

  9. idea 社区版本创建javaweb项目 使用tomcat

    1.创建maven  webapp项目 2.pom文件添加依赖及tomcat7-maven-plugin插件 <dependencies> <dependency> <g ...

  10. 自己挖的坑自己填--docker创建实例出现Waiting for SSH to be available…

    在之前使用Docker for Windows Installer.exe直接安装,通过docker-machine-driver-vmwareworkstation.exe实现docker和VM的共 ...