原文请查阅 这里,略有删减,本文采用 知识共享署名 4.0 国际许可协议共享,BY Troland。
本系列持续更新中,Github 地址请查阅这里。javascript
这是 JavaScript 工做原理的第十章。java
网络应用在客户端日益复杂,这是由不少因素的形成的,好比须要更加丰富的界面交互以提供更加复杂的应用功能,实时计算等等。node
网络应用的日益复杂致使没法知晓其生命周期中指定时刻准确的交互界面状态。git
若是你正在构建一些框架或者一个库,这会更加的困难,好比,你没法经过监测 DOM 来响应并执行一些特定的操做。github
MutationObserver 是现代浏览器提供的用来检测 DOM 变化的网页接口。你可使用这个接口来监听新增或者删除节点,属性更改,或者文本节点的内容更改。web
能够干点啥好呢?浏览器
你能够在如下几种状况信手拈来 MutationObserver 接口。好比:网络
这只是几个 MutationObserver 的使用场景。session
在应用中集成 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 变化的呢?
有几下几种可用的方法:
最简单且粗糙的方法即便用轮询。使用浏览器内置的 setInterval 网页接口你能够建立一个定时任务来定时检查 DOM 的变化。固然了,这个方法会显著地减弱网络应用/网站的性能。
其实,这是能够理解为脏检查,若是有使用过 AngularJS 应该会有看过其脏检查所致使的性能问题。在个人另外一个系列里面有稍微介绍了下,具体能够查看这里。
早在 2000 年,就推出了 MutationEvents API 。虽然挺管用的,可是每一个单一的 DOM 变化都会触发 mutation 事件,结果又会形成性能问题。如今,MutationEvents
接口已经被废弃,不久的未来,现代浏览器全都将中止支持该接口。
如下是 MutationEvents
的浏览器兼容状况:
依靠 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 地址请查阅这里。