理解 Mutation Observer

.array p { counter-increment: longen; margin-left: 10px }
.array p::before { content: counter(longen) "." }
.alink { font-size: 16px; color: blue }

阅读目录

  • 1.理解 Mutation Observer

回到顶部

1.理解 Mutation Observer

Mutation Observer(变动观察器) 是监听DOM变动的接口,DOM发生任何变动,Mutation Observer会得到通知。
它与事件类似,但有所不同,事件是同步的,也就是说DOM发生变动,事件立刻会处理,而Mutation Observer则是异步,它不会立即处理,而是等页面上所有的DOM完成后,执行一次,如果页面上要操作100次DOM的话,那么事件会监听100次DOM,而Mutation Observer只会执行一次,等所有的DOM操作完成后,执行。

它的特点是:
1. 等待所有脚本任务完成后,才会执行,即采用异步方式。
2. DOM的变动记录封装成一个数组进行处理,而不是一条条的个别处理DOM变动。
3. 还可以观测发生在DOM的所有类型变动,也可以观测某一类变动。

浏览器支持如下:

下面的代码是检测浏览器是否支持该属性。如下代码:

var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
// 监测浏览器是否支持
var observeMutationSupport = !!MutationObserver;

MutationObserver 构造函数

使用 MutationObserver 构造函数,新建一个观察器实例,同时指定该实例的回调函数。如下:
var observer = new MutationObserver(callback);

观察器回调函数会在每次DOM发生变动后调用,接受2个参数,第一个是变动数组,第二个是观察器实例。

Mutation Observer 实例的方法
1. observe() 该方法所要观察的DOM节点,以及所要观察的特定变动。
该方法接受2个参数,第一个参数是所要观察的DOM元素,第二个所要观察的变动类型。
调用方式:observer.observe(dom, options);
那么类型有如下:
childList: 子节点的变动。
attributes: 属性的变动。
characterData: 节点内容或节点文本的变动。
subtree 所有后代节点的变动。

需要观察哪一种变动类型,需要在options对象中指定为true即可;但是如果设置subtree的变动,必须同时指定childList, attributes 和 characterData 中的一种或多种。

1. 监测childList的变动;

如下demo代码:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

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

     Observer.observe(list, {
        childList: true,
        subtree: true
     });
     list.appendChild(document.createElement(‘div‘));
     list.appendChild(document.createTextNode(‘foo‘));
     list.removeChild(list.childNodes[0]);
     list.childNodes[0].appendChild(document.createElement(‘div‘));
  </script>
</html>

控制台查看效果

如上代码 在控制台上打印信息如下,我们打印第一个回调参数 mutations 后,截图如下:

2. 监测characterData的变动

代码如下:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
        });
     });
    Observer.observe(list, {
      childList:true,
      characterData:true,
      subtree:true
    });
    list.childNodes[0].data = "cha";
  </script>
</html>

控制台查看效果

3. 监测属性的变动;
代码如下:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
        });
     });
    Observer.observe(list, {
      attributes: true
    });
    // 设置节点的属性  会触发回调函数
    list.setAttribute(‘data-value‘, ‘111‘);

    // 重新设置属性 会触发回调
    list.setAttribute(‘data-value‘, ‘2222‘);

    // 删除属性 也会触发回调
    list.removeAttribute(‘data-value‘);
  </script>
</html>

控制台查看效果

如下图所示:

除了基本的变动类型之外,options对象还可以设定以下属性
attributeOldValue: {boolean} 表示观察attributes变动时,是否需要记录变动前的属性值。
characterDataOldValue: {boolean} 表示观察characterData变动时,是否需要记录变动前的值。
attributeFilter {Array} 表示需要观察的特定属性 比如 [‘class‘, ‘src‘]

1. attributeOldValue 属性变动前,是否需要记录变动之前的值; 代码如下:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
        });
     });
    Observer.observe(list, {
      attributes: true,
      attributeOldValue: true
    });
    // 设置节点的属性  会触发回调函数
    list.setAttribute(‘data-value‘, ‘111‘);

    // 删除属性
    list.setAttribute(‘data-value‘, ‘2222‘);
  </script>
</html>

控制台查看效果

如上截图的oldValue 就是变动之前的值

2. characterData变动时,是否需要记录变动前的值。
如下代码:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
        });
     });
    Observer.observe(list, {
      childList:true,
      characterData:true,
      subtree:true,
      characterDataOldValue: true
    });
    // 设置数据 触发回调
    list.childNodes[0].data = "aaa";

    // 重新设置数据 重新触发回调
    list.childNodes[0].data = "bbbb";
  </script>
</html>

控制台查看效果

第一次设置数据,记录变动前的数据如下:

第二次设置数据,记录变动前的回调如下:

可以看到oldValue 的变动值;

attributeFilter {Array} 表示需要观察的特定属性 比如 [‘class‘, ‘src‘];代码如下:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
        });
     });
    Observer.observe(list, {
      attributes: true,
      attributeFilter: [‘data-value‘]
    });
    // 第一次设置属性 data-key 不会触发的,因为data-value 不存在
    list.setAttribute(‘data-key‘, 1);

    // 第二次会触发
    list.setAttribute(‘data-value‘, 1);
  </script>
</html>

控制台查看效果

下面我们做一个简单的demo编辑器,首先给父级元素 ol 设置 contenteditable 让容器可编辑,然后构造一个observer 监听子元素的变化,每次回车的时候,控制台输出它的内容;如下代码:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol contenteditable oninput="" style=‘border: 1px solid red‘>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          console.log(mutation);
          if(mutation.type === ‘childList‘) {
            var list_values = [].slice.call(list.children).map(function(node) {
              return node.innerHTML;
            }).filter(function(s) {
              if(s === ‘<br>‘) {
                return false;
              } else {
                return true;
              }
            });
            console.log(list_values);
          }
        });
     });
    Observer.observe(list, {
      childList: true
    });   

  </script>
</html>

控制台查看效果

现在我们继续可以做一个类似于 input和textarea中的 valueChange的事件一样的,监听值变化,之前的值和之后的值,如下代码:

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘demo‘>
      <ol contenteditable oninput="" style=‘border: 1px solid red‘>
        <li>111111</li>
      </ol>
    </div>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var list = document.querySelector(‘ol‘);

     var Observer = new MutationObserver(function(mutations, instance){
        mutations.forEach(function(mutation){
          var enter = {
            mutation: mutation,
            el: mutation.target,
            newValue: mutation.target.textContent,
            oldValue: mutation.oldValue
          };
          console.log(enter);
        })
     });
    Observer.observe(list, {
      childList: true,
      attributes: true,
      characterData: true,
      subtree: true,
      characterDataOldValue: true,
    });   

  </script>
</html>

控制台查看效果

注意: 对input 和 textarea 不起作用的。

编辑器统计字数的demo

<!DOCTYPE html>
<html>
  <head>
    <title>演示Vue</title>
    <style>

    </style>
  </head>
  <body>
    <div id=‘editor‘ contenteditable style="width: 240px; height: 80px; border: 1px solid red;"></div>
    <p id="textInput">还可以输入100字</p>
  </body>

  <script type="text/javascript">
     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
     var editor = document.querySelector(‘#editor‘);
     var textInput = document.querySelector(‘#textInput‘);
     var observer = new MutationObserver(function(mutations){
        mutations.forEach(function(mutation) {
          if(mutation.type === ‘characterData‘) {
            var newValue = mutation.target.textContent;
            textInput.innerHTML = "还可以输入" +  (1000 - newValue.length+"字");
          }
        });
     });
    observer.observe(editor, {
      childList: true,
      attributes: true,
      characterData: true,
      subtree: true,
      characterDataOldValue: true,
    });   

  </script>
</html>

控制台查看效果

时间: 2024-10-19 14:54:28

理解 Mutation Observer的相关文章

用最简单的例子理解观察者模式(Observer Pattern)

假设有一个软件公司,每当有新产品推出,就把信息通知到一些客户. 把通知这个动作抽象成一个接口. public interface IService { void Notif(); } 客户如果想获得通知,就需要实现以上的接口.这里的客户被看作是观察者. public class CustomerA : IService { public void Notif() { Console.WriteLine("客户A收到通知了~~"); } } public class CustomerB

Vue系列---理解Vue.nextTick使用及源码分析(五)

_ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DOM操作. 三. Vue.nextTick的调用方式如下: 四:vm.$nextTick 与 setTimeout 的区别是什么? 五:理解 MutationObserver 六:nextTick源码分析 回到顶部 一. 什么是Vue.nextTick()? 官方文档解释为:在下次DOM更新循环结束之

JavaScript中定时器

JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务. setTimeout() setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行.它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器. var timerId = setTimeout(func|code, delay) 上面代码中,setTimeout函数接受两个参数,第一个参数func|cod

百度前端学院两个月学习量任务说明

任务说明 初级班 & 中级班 初级班和中级班的任务内容基本一致,但是在细节要求和时间要求上会不一样. 任务一:HTML.CSS基础 初级班:11天 中级班:4天 任务二:JavaScript基础 初级班:13天 中级班:7天 任务三:一个简单的to-do APP,巩固之前的学习,并深入学习JavaScript语言的一些特性.以及相关的一些设计模式 初级班:7天 中级班:7天 任务:关于移动.node.js.ES6.CSS Processing.JavaScript模块化.前端工程化,掌握目前前端

[IOS_HTML5]IOS7下HTML5的各种坑

 这些天 Apple 已经推出了 iOS 7 以及 iPhone 5S 和 iPhone 5C .Apple 面向 web 开发者仅仅发布了 10% 的所需信息,我可以说这是自 1.0 以来,bug 最多的 Safari 版本嘛.文本我将介绍新的 API 和特性,以及如果你有网站或 webapp ,马上需要处理的大多数问题. 简而言之 没有时间读这篇长文? UI 变化:工具栏色彩,新的全屏导航问题,新的主屏图标尺寸:iPhone 未使用 <title>:可能与新手势冲突. 新设备:对 web

JS异步编程 (1)

JS异步编程 (1) 1.1 什么叫异步 异步(async)是相对于同步(sync)而言的,很好理解. 同步就是一件事一件事的执行.只有前一个任务执行完毕,才能执行后一个任务.而异步比如: setTimeout(function cbFn(){ console.log('learnInPro'); }, 1000); console.log('sync things'); setTimeout就是一个异步任务,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,

Promise和setTimeout执行顺序 面试题

看到过下面这样一道题: (function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5);

JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化

摘要: 掌握MutationObserver. 这是专门探索 JavaScript 及其所构建的组件的系列文章的第10篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! JavaScript 是如何工作的:深入V8引擎&编写优化代码的5个技巧! JavaScript 是如何工作的:内存管理+如何处理4个常见的内存泄漏 ! JavaScript 是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理(转)

前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正. ----超长文+多图预警,需要花费不少时间.---- 如果看完本文后,还对进程线程傻傻分不清,不清楚浏览器多进程.浏览器内核多线程.JS单线程.JS运行机制的区别.那么请回复我,一定是我写的还不够清晰,我来改... ----正文开始---- 最近发现有不少介绍JS单线程运行机制的文章,但是发现很多都仅仅是介绍某一部分的知识,而且各个地方的说法还不统一,容易造成困惑. 因此准备梳理这块知识点,结合已有的认知,基于网上的大量参