[譯] JavaScript 是如何運做的:用 MutationObserver 追蹤 DOM 的變化

JavaScript 是如何運做的:用 MutationObserver 追蹤 DOM 的變化

本系列專門研究 JavaScript 及其構建組件,這是第 10 期。在識別和描述核心元素的過程當中,咱們也分享了一些構建 SessionStack 的重要法則,SessionStack 是一個 JavaScript 應用,爲了幫助用戶實時查看和再現他們的 web 應用程序缺陷,它須要健壯而且高性能。javascript

web 應用正在持續的愈來愈側重客戶端,這是由不少緣由形成的,例如須要更豐富的 UI 來承載複雜應用的需求,實時運算,等等。css

持續增長的複雜度使得在 web 應用的生命週期的任意時刻中獲取 UI 的確切狀態愈來愈困難。前端

而當你在搭建某些框架或者庫的時候,甚至會更加困難,例如,前者須要根據 DOM 來做出反應並執行特定的動做。java

概覽

MutationObserver 是一個現代瀏覽器提供的 Web API,用於檢測 DOM 的變化。藉助這個 API,能夠監聽到節點的新增或移除,節點的屬性變化或者字符節點的字符內容變化。node

爲何你會想要監聽 DOM?android

這裏有不少 MutationObserver API 帶來極大便捷的例子,好比:ios

  • 你想要提醒 web 應用的用戶,他如今所瀏覽的頁面有內容發生了變化。
  • 你正在使用一個新的花哨的 JavaScript 框架,它根據 DOM 的變化動態加載 JavaScript 模塊。
  • 也許你正在開發一個 WYSIWYG 編輯器,並試着實現撤銷/重作功能。藉助 MutationObserver API,你在任什麼時候間都能知道發生了什麼變化,因此撤銷也就很是容易。

這裏有幾個關於 MutationObserver 是如何帶來便捷的例子。css3

如何使用 MutationObserver

MutationObserver 應用於你的應用至關簡單。你須要經過傳入一個函數來建立一個 MutationObserver 實例,每當有變化發生,這個函數將會被調用。函數的第一個參數是一個批次內全部的變化(mutation)的集合。每一個變化都會提供它的類型和已經發生的變化的信息。git

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

這個被建立的對象有三個方法:github

  • observe — 開始監聽變化。須要兩個參數 - 你須要觀察的 DOM 和一個設置對象
  • disconnect — 中止監聽變化
  • takeRecords — 在回調函數調用以前,返回最後一個批次的變化。

下面這個代碼片斷展現瞭如何開始觀察:

// 開始監聽頁面中根 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 屬性致使的變化。

最後,爲了在任務結束後中止對 DOM 的觀察,你能夠這樣作:

// 中止 MutationObserver 對變化的監聽。
mutationObserver.disconnect();
複製代碼

如今,MutationObserver 已經被普遍支持:

備擇方案

無論怎麼說,MutationObserver 並非一直就有的。那麼當 MutationObserver 出現以前,開發者用的是什麼?

這是幾個可用的其餘選項:

  • Polling
  • MutationEvents
  • CSS animations

Polling

最簡單的最接近原生的方法是 polling。使用瀏覽器的 setInterval web 接口你能夠設置一個在一段時間後檢查是否有變化發生的的任務。天然,這個方法將會嚴重的下降應用或者網站的性能。

MutationEvents

在 2000 年,MutationEvents API 被引入。儘管頗有用,可是每次 DOM 發生變化 mutation events 都會被觸發,這將再次致使性能問題。如今 MutationEvents 接口已經被廢棄,很快,現代瀏覽器將會徹底中止對它的支持。

這是瀏覽器對 MutationEvents 的支持:

CSS animations

一個有點奇怪的備選方案依賴於 CSS animations。這可能聽起來有些讓人困惑。基本上,這個方案是建立一個動畫,一旦一個元素被添加到了 DOM,這個動畫就將會被觸發。動畫開始的時候,animationstart 事件會被觸發:若是你對這個事件綁定了一個處理器,你將會確切的知道元素是何時被添加到 DOM 的。動畫執行的時間段很短,因此實際應用的時候它對用戶是不可見的。

首先,咱們須要一個父級元素,咱們在它的內部監聽節點的插入:

<div id=」container-element」></div>
複製代碼

爲了獲得節點插入的處理器,咱們須要設置一系列的 keyframe 動畫,當節點插入的時候,動畫將會開始。

@keyframes nodeInserted { 
 from { opacity: 0.99; }
 to { opacity: 1; } 
}
複製代碼

keyframes 已經建立,動畫還須要被應用於你想要監聽的元素。注意應設置很小的 duration 值 —— 它們將會減弱動畫在瀏覽器上留下的痕跡:

#container-element * {
 animation-duration: 0.001s;
 animation-name: nodeInserted;
}
複製代碼

這爲 container-element 的全部子節點都添加了動畫。當動畫結束後,插入的事件將會被觸發。

咱們須要一個做爲事件監聽者的 JavaScript 方法。在方法內部,必須確保初始的 event.animationName 檢測是咱們想要的那個動畫。

var insertionListener = function(event) {
  // 確保這是咱們想要的那個動畫。
  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。

MutationObserverSessionStack 庫中佔據了核心位置。

你一旦將 SessionStack 庫整合進 web 應用,它就開始收集 DOM 變化、網絡請求、錯誤信息、debug 信息等等,併發送到咱們的服務器。SessionStack 使用這些信息從新建立了你的用戶端發生的一切,並以發生在用戶端的一樣的方式將產品的問題展示給你。不少用戶認爲 SessionStack 記錄的實際是視頻 -- 然而它並無。記錄真實狀況的視頻是很耗費資源的,然而咱們收集的少許數據卻很輕量,並不會影響 UX 和你的 web 應用的性能。

若是你想要嘗試一下 SessionStack,這是一個免費的設計案例。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索