JavaScript工做原理(九):使用MutationObserver跟蹤DOM的改變

mutation_observer
Web應用程序在客戶端愈來愈重要,緣由不少,好比須要更豐富的用戶界面來容納更復雜的應用程序必須提供的功能,實時計算等等。node

複雜性的增長使得在Web應用程序的生命週期中的每一個特定時刻都很難知道UI的確切狀態。web

若是你正在構建某種框架或僅僅是一個庫,這會變得更加困難,例如,它必須做出反應並執行某些依賴於DOM的操做。瀏覽器

概觀

MutationObserver是現代瀏覽器提供的Web API,用於檢測DOM中的變化。使用此API,能夠偵聽新添加或刪除的節點,屬性更改或文本節點文本內容的更改。網絡

你爲何想這麼作?框架

MutationObserver API在不少狀況下能夠很是方便地使用。例如:編輯器

  • 您但願通知您的網絡應用訪問者在他當前所在的頁面上發生了一些變化。
  • 您正在研究一種新的花式JavaScript框架,該框架可根據DOM如何變化來動態加載JavaScript模塊。
  • 您可能正在使用所見即所得的編輯器,試圖實現撤銷/重作功能。經過利用MutationObserver API,你能夠在任什麼時候候都能很容易undo他們

圖片描述

這些只是MutationObserver如何提供幫助的幾個例子。函數

如何使用MutationObserver

在你的應用中實現MutationObserver至關容易。您須要經過傳遞一個函數來建立一個MutationObserver實例,該函數在每次發生突變時都會被調用。函數的第一個參數是在一個批次中發生的全部改變的集合。每一個改變都提供有關其類型和已發生變化的信息。
MutationObserver API,您能夠在任何給定的位置知道進行了哪些更改,所以您能夠輕鬆地撤消它們。性能

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

建立的對象有三個方法:優化

  • observe - 開始傾聽變化。須要兩個參數 - 您要觀察的DOM節點和一個設置對象
  • disconnect - 中止監聽更改
  • takeRecords - 在回調被觸發前返回最後一批更改。

如下片斷顯示瞭如何開始觀察:動畫

// Starts listening for changes in the root HTML element of the page.
mutationObserver.observe(document.documentElement, {
  attributes: true,
  characterData: true,
  childList: true,
  subtree: true,
  attributeOldValue: true,
  characterDataOldValue: true
});

如今假設你有一些簡單的DIV在DOM中:

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

使用jQuery,你你能夠移除div的class屬性:

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

正如咱們開始觀察的那樣,在調用mutationObserver.observe(...)以後,咱們將在相應的MutationRecord的控制檯中看到一個日誌:
圖片描述

這是由刪除class屬性引發的變異。

最後,爲了在完成後中止觀察DOM,能夠執行如下操做:

// Stops the MutationObserver from listening for changes.
mutationObserver.disconnect();

現在,MutationObserver獲得了普遍的支持:
圖片描述

備擇方案

然而,MutationObserver並不老是可用的。那麼在MutationObserver出現以前開發者會採起什麼措施呢?

還有其餘幾個選項可用:

  • 輪詢
  • MutationEvents
  • CSS動畫

輪詢

最簡單和最簡單的方式是經過輪詢。使用瀏覽器setInterval WebAPI,您能夠設置一個任務,按期檢查是否發生了任何更改。固然,這種方法會顯着下降網絡應用程序/網站的性能。

MutationEvents

在2000年,引入了MutationEvents API。雖然有用,但DOM中的每一次更改都會觸發突變事件,這又會致使性能問題。如今,MutationEvents API已被棄用,很快,現代瀏覽器將再也不支持它。

這是MutationEvents的瀏覽器支持:
圖片描述

CSS動畫

一個有點奇怪的選擇是依賴CSS動畫。這聽起來有點混亂。基本上,這個想法是建立一個動畫,一旦一個元素被添加到DOM就會觸發該動畫。動畫開始的那一刻,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;
}

這將動畫添加到容器元素的全部子節點。當動畫結束時,插入事件將觸發。

咱們須要一個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。

相關文章
相關標籤/搜索