监视Dom树变化-MutationObserver

2018-08-09  本文已影响151人  wwmin_

MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
MutationObserver兼容性,MutationObserver

MutationObserver 兼容性

构造函数

MutationObserver()创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。

~注意:MutationObserver只能监测到诸如属性改变、增删子结点等,对于自己本身被删除,是没有办法的, 但是可以通过监测父结点来达到要求。~

方法

disconnect()
阻止 MutationObserver 实例继续接收通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。
observe()
配置MutationObserver在DOM更改匹配给定选项时,通过其回调函数开始接收通知.
takeRecords()
MutationObserver的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord对象的新Array中.它的一个作用是,比如你对一个节点的操作你不想马上就做出反应,过段时间在显示改变了节点的内容。

var observe=new MutationObserver(function(){});
observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text节点'));
var record = observe.takeRecords();              //此时record保存了改变记录列表  
//当调用takeRecords方法时,记录队列被清空因此不会触发MutationObserver中的callback回调方法。
target.appendChild(document.createElement('span'));
observe.disconnect();                            //停止对target的观察。
//MutationObserver中的回调函数只有一个记录,只记录了新增span元素

//之后可以对record进行操作
//...

MutationObserver 的实例的observe方法用来启动监听,它接受两个参数。
第一个参数:所要观察的 DOM 节点,第二个参数:一个配置对象,指定所要观察的特定变动,有以下几种:

属性 描述
childList 如果需要观察目标节点的子节点(新增了某个子节点,或者移除了某个子节点),则设置为true.
attributes 如果需要观察目标节点的属性节点(新增或删除了某个属性,以及某个属性的属性值发生了变化),则设置为true.
characterData 如果目标节点为characterData节点(一种抽象接口,具体可以为文本节点,注释节点,以及处理指令节点)时,也要观察该节点的文本内容是否发生变化,则设置为true.
subtree 除了目标节点,如果还需要观察目标节点的所有后代节点(观察目标节点所包含的整棵DOM树上的上述三种节点变化),则设置为true.
attributeOldValue 在attributes属性已经设为true的前提下,如果需要将发生变化的属性节点之前的属性值记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true.
characterDataOldValue 在characterData属性已经设为true的前提下,如果需要将发生变化的characterData节点之前的文本内容记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true.
attributeFilter 一个属性名数组(不需要指定命名空间),只有该数组中包含的属性名发生变化时才会被观察到,其他名称的属性发生变化后会被忽略.

MutationObserver的callback回调函数是异步的,只有在全部DOM操作完成之后才会调用callback。

2.当只设置{ childList: true}时,表示观察目标子节点的变化
如果想要观察到子节点以及后代的变化需设置{childList: true, subtree: true}
attributes选项用来观察目标属性的变化,用法类似与childList,目标属性的删除添加以及修改都会被观察到。
3.我们需要注意的是characterData这个选项,它是用来观察CharacterData类型的节点的,只有在改变节点数据时才会观察到,如果你删除或者增加节点都不会进行观察,还有如果对不是CharacterData类型的节点的改变不会观察到

observe.observe(target,{ characterData: true, subtree: true});
target.childNodes[0].textContent='改变Text节点';              //观察到
target.childNodes[1].textContent='改变p元素内容';              //不会观察到
target.appendChild(document.createTextNode('新增Text节点'));  //不会观察到
target.childNodes[0].remove();                               //删除TEXT节点也不会观察到

我们只需要记住只有对CharacterData类型的节点的数据改变才会被characterData为true的选项所观察到。
4.最后关注一个特别有用的选项attributeFilter,这个选项主要是用来筛选要观察的属性,比如你只想观察目标style属性的变化,这时可以如下设置:

observe.observe(target,{ attributeFilter: ['style'], subtree: true});
target.style='color:red';                      //可以观察到
target.removeAttribute('name');                //删除name属性,无法观察到 

MutationRecord
变动记录中的属性如下:

1.type:如果是属性变化,返回"attributes",如果是一个CharacterData节点(Text节点、Comment节点)变化,返回"characterData",节点树变化返回"childList"
2.target:返回影响改变的节点
3.addedNodes:返回添加的节点列表
4.removedNodes:返回删除的节点列表
5.previousSibling:返回分别添加或删除的节点的上一个兄弟节点,否则返回null
6.nextSibling:返回分别添加或删除的节点的下一个兄弟节点,否则返回null
7.attributeName:返回已更改属性的本地名称,否则返回null
8.attributeNamespace:返回已更改属性的名称空间,否则返回null
9.oldValue:返回值取决于type。对于"attributes",它是更改之前的属性的值。对于"characterData",它是改变之前节点的数据。对于"childList",它是null

其中 type、target这两个属性不管是哪种观察方式都会有返回值,其他属性返回值与观察方式有关,比如只有当attributeOldValue或者characterDataOldValue为true时oldValue才有返回值,只有改变属性时,attributeName才有返回值等。

示例

The following example was adapted from this blog post.

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true, subtree: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }else{
            console.log('The ' + mutation.attributeName + ' subtree was modified.');
          }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

完整示例:

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>mutation observer</title>
  </head>
  <body>
    <div id="app">
      <h2>
        监视dom树变化
      </h2>
      <div id="deleteId">
        看我的颜色
      </div>
    </div>
    <script>
      var targetNode = document.getElementById("app");
      var config = {
        attributes: true,
        childList: true,
        subtree: true
      };
      var callback = function (mutationsList) {
        for (var mutation of mutationsList) {
          if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
          } else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
          }else{
            console.log('The ' + mutation.attributeName + ' subtree was modified.');
          }
        }
      };
      let observer = new MutationObserver(callback);
      observer.observe(targetNode, config);
      // later , you can stop observing
      //observer.disconnect();
      setTimeout(() => {
        var dom = document.getElementById("deleteId");
        dom.style.color = "red";
      }, 1000);
      setTimeout(() => {
        var dom = document.getElementById("deleteId");
        dom.style.fontSize = '20px';
      }, 2000);
      setTimeout(() => {
        var dom = document.getElementById("deleteId");
        document.getElementById('app').removeChild(dom);
      }, 5000);
    </script>
  </body>
</html>

ES6 语法

// Select the node that will be observed for mutations
let targetNode = document.querySelector(`#id`);

// Options for the observer (which mutations to observe)
let config = {
    attributes: true,
    childList: true,
    subtree: true
};

// Callback function to execute when mutations are observed
const mutationCallback = (mutationsList) => {
    for(let mutation of mutationsList) {
        let type = mutation.type;
        switch (type) {
            case "childList":
                console.log("A child node has been added or removed.");
                break;
            case "attributes":
                console.log(`The ${mutation.attributeName} attribute was modified.`);
                break;
            case "subtree":
                console.log(`The subtree was modified.`);
                break;
            default:
                break;
        }
    }
};

// Create an observer instance linked to the callback function
let observer = new MutationObserver(mutationCallback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

使用场景:
检测dom变化动态执行方法, 检测dom变化重新渲染dom防止别人篡改,例如网页水印制作防止人为去掉dom水印等等场景.

参考连接:

https://segmentfault.com/a/1190000012787829
https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver

上一篇 下一篇

猜你喜欢

热点阅读