摘要: 掌握MutationObserver。

这是专门探索 JavaScript 及其所构建的组件的系列文章的第10篇。

如果你错过了前面的章节,可以在这里找到它们:

Web 应用程序在客户端变得越来越重,原因很多,例如需要更丰富的 UI 来容纳更复杂的应用程序提供的内容,实时计算等等。复杂性的增加使得在 Web 应用程序生命周期的每个给定时刻都很难知道 UI 的确切状态。

而当你在搭建某些框架或者库的时候,甚至会更加困难,例如,前者需要根据 DOM 来作出反应并执行特定的动作。

概述

Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

概念上,它很接近事件,可以理解为 DOM 发生变动就会触发 Mutation Observer 事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说,DOM 的变动立刻会触发相应的事件;Mutation Observer 则是异步触发,DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发。

这样设计是为了应付 DOM 变动频繁的特点。举例来说,如果文档中连续插入1000个 <li>元素,就会连续触发1000个插入事件,执行每个事件的回调函数,这很可能造成浏览器的卡顿;而 Mutation Observer 完全不同,只在 1000 个段落都插入结束后才会触发,而且只触发一次。

Mutation Observer有以下特点:

  • 它等待所有脚本任务完成后,才会运行,即采用异步方式
  • 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条地个别处理 DOM 变动
  • 它即可以观察发生在 DOM 节点的所有变动,也可以观察某一类变动

为什么要要监听 DOM?

在很多情况下,MutationObserver API 都可以派上用场。例如:

  • 你希望通知 Web 应用程序访问者,他当前所在的页面发生了一些更改。
  • 你正在开发一个新的 JavaScript 框架,需要根据 DOM 的变化动态加载 JavaScript 模块。
  • 也许你正在开发一个所见即所得(WYSIWYG) 编辑器,试图实现撤消/重做功能。通过利用 MutationObserver API,你可以知道在任何给定的点上进行了哪些更改,因此可以轻松地撤消这些更改。

这些只是 MutationObserver 可以提供帮助的几个例子。

MutationObserver 用法

在应用程序中实现 MutationObserver 相当简单。你需要通过传入一个函数来创建一个 MutationObserver 实例,每当有变化发生,这个函数将会被调用。函数的第一个参数是变动数组,每个变化都会提供它的类型和已经发生的变化的信息。

var mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});

这个被创建的对象有三个方法:

  • observe  — 启动监听
  • disconnect — 用来停止观察
  • takeRecords — 返用来清除变动记录,即不再处理未处理的变动。

observe()

observe 方法用来启动监听,它接受两个参数。

  • 第一个参数:所要观察的 DOM 节点
  • 第二个参数:一个配置对象,指定所要观察的特定变

下面的片段展示了如何开始启动监听(observe  ):

// 开始侦听页面的根 HTML 元素中的更改。
mutationObserver.observe(document.documentElement, {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});

现在,假设 DOM 中有一些非常简单的 div:

<div id="sample-div" class="test"> Simple div </div>

使用 JQuery 来移除这个 div 上的 class:

$("#sample-div").removeAttr("class");

正如我们已经开始观察到的,在调用 mutationObserver.observe(…) 之后,将在控制台中看到相应 MutationRecord 的日志:

这个是由移除 class 属性导致的变化。

MutationRecord 对象包含了DOM的相关信息,有如下属性:

type:观察的变动类型(attribute、characterData或者childList)

target:发生变动的 DOM 节点

addedNodes:新增的 DOM 节点

removedNodes:删除的 DOM 节点

previousSibling:前一个同级节点,如果没有则返回 null

nextSibling:下一个同级节点,如果没有则返回 null

attributeName:发生变动的属性。如果设置了attributeFilter,则只返回预先指定的属性。

oldValue:变动前的值。这个属性只对 attribute 和 characterData 变动有效,如果发生 childList 变动,则返回 null

最后,为了在任务完成后停止观察 DOM,可以执行以下操作:

mutationObserver.disconnect();

现在,MutationObserver 已经被广泛支持:

备择方案

MutationObserver 在之前还没有的,那么在 MutationObserver 还没出现之前,开发者采用什么方案呢?

这是几个可用的其他选项:

  • 轮询(Polling)
  • MutationEvents
  • CSS animations

轮询(Polling)

最简单和最简单的方法是轮询。使用浏览器 setInterval 方法,可以设置一个任务,定期检查是否发生了任何更改。当然,这种方法会显著降低web 应用程序/网站的性能。

MutationEvents

在2000年,MutationEvents API 被引入。虽然很有用,但在 DOM中 的每一次更改都会触发改变事件,这同样会导致性能问题。现在 MutationEvents API 已经被弃用,很快现代浏览器将完全停止支持它。

CSS animations

另一个有点奇怪的选择是依赖 CSS 动画。这听起来可能有点令人困惑。基本上,我们的想法是创建一个动画,一旦元素被添加到 DOM 中,动画就会被触发。动画开始的那一刻,animationstart 事件将被触发:如果已经将事件处理程序附加到该事件,那么你将确切地知道元素何时被添加到 DOM 中。动画的执行时间周期应该很小,用户几乎看不到它。

首先,需要一个父级元素,我们在它的内部监听节点的插入:

<div id=”container-element”></div>

为了得到节点插入的处理器,需要设置一系列的 keyframe 动画,当节点插入的时候,动画将会开始。

@keyframes nodeInserted {
from { opacity: 0.99; }
to { opacity: 1; }
}

创建 keyfram 后,还需要把它放入你想监听的元素上,注意应设置很小的 duration 值 —— 它们将会减弱动画在浏览器上留下的痕迹。

#container-element * {
animation-duration: 0.001s;
animation-name: nodeInserted;
}

这会将动画添加到 container-element 的所有子节点。 动画结束时,将触发插入事件。

我们需要一个 JavaScript 函数作为事件监听器。在函数中,必须进行初始的 event.animationName 检查以确保它是我们想要的动画。

var insertionListener = function(event) {
// Making sure that this is the animation we want.
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 中可能发生的每一个更改,并且在批量触发更改时,它的优化程度更高。最重要的是,所有主要的现代浏览器都支持 MutationObserver,还有一些使用引擎下 MutationEvents 的 polyfill。

原文:

https://blog.sessionstack.com...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具Fundebug

你的点赞是我持续分享好东西的动力,欢迎点赞!

一个笨笨的码农,我的世界只能终身学习!

更多内容请关注公众号《大迁世界》!

JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化的更多相关文章

  1. JavaScript 是如何工作的:JavaScript 的共享传递和按值传递

    摘要: 原始数据类型和引用数据类型的副本作为参数传递给函数. 原文:JavaScript 是如何工作的:JavaScript 的共享传递和按值传递 作者:前端小智 Fundebug经授权转载,版权归原 ...

  2. JavaScript 是如何工作的:JavaScript 的内存模型

    摘要: 从内存角度理解 let 和 const 的意义. 原文:JavaScript 是如何工作的:JavaScript 的内存模型 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

  3. JavaScript是如何工作的:编写自己的Web开发框架 + React及其虚拟DOM原理

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 19 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  4. JavaScript 是如何工作:Shadow DOM 的内部结构 + 如何编写独立的组件!

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 17 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  5. JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 15 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  6. JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能

    摘要: 理解浏览器渲染. 原文:JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这是专门探索 J ...

  7. JavaScript 工作原理之十-使用 MutationObserver 监测 DOM 变化

    原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第十章. 网络 ...

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

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

  9. JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景

    摘要: 理解Web Workers. 原文:JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

随机推荐

  1. 总结css

    一.背景  1. background-color: 背景颜色   2.background-image 设置图像为背景 url("http://images.cnblogs.com/cnb ...

  2. DOS命令(一)

    1. echo 输出内容,用来输出文字. [例如:echo hello] 2. titile 标题,用来修改标题. 3. color 背景色前景色,用来设置背景色和前景色 0 = 黑色 8 = 灰色 ...

  3. charles-web端开发者实用功能点

    ##网速模拟功能 throttle功能对于前端来说非常实用,可以看页面在低网速下的表现,从而找出优化的点. 在线上环境通常有些因为网速慢导致的bug,在本机无法重现,那时候就很抓瞎,如果嫌远程麻烦,推 ...

  4. Puppeteer 应用容器化

    Puppeteer 应用容器化 Intro Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库.可以通过Puppeteer的提供的api直接控 ...

  5. RabbitMQ消息队列系列教程(二)Windows下安装和部署RabbitMQ

    摘要 本篇经验将和大家介绍Windows下安装和部署RabbitMQ消息队列服务器,希望对大家的工作和学习有所帮助! 目录 一.Erlang语言环境的搭建 二.RabbitMQ服务环境的搭建 三.Ra ...

  6. ASP.NET Core 系列目录

    目录: ASP.NET Core 2.0 : 一. 概述 ASP.NET Core 2.0:二. 开发环境 ASP.NET Core 2.0 : 三. 项目结构 ASP.NET Core 2.0 : ...

  7. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  8. JDK1.8源码(二)——java.util.LinkedList

      LinkedList定义 LinkedList 是链表实现的线性表(双链表),元素有序且可以重复. public class LinkedList<E> extends Abstrac ...

  9. asp.net core 系列 10 配置configuration (上)

    一.  ASP.NET Core 中的配置概述 ASP.NET Core 中的应用配置是基于键值对,由configuration 程序提供. configuration  将从各种配置源提供程序操作键 ...

  10. Javascript 链式操作以及流程控制

    春节过后,感觉过年吃的油腻的食品转化的脂肪都长到 脑子去了. 根本转不动啊 上班第一天 实在是写不动代码了, 顺手打开多天为看的 收件箱,查看查看邮件,看看春节期间 风云变幻的前端圈又有哪些大事发生. ...