理解 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>

控制台查看效果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM