MutationObserver 介面

我是小卡車呀發表於2020-10-14

MutationObserver 介面

可以在 DOM 被修改時非同步執行回撥。使用 MutationObserver 可以觀察整個文件、DOM樹的一部分,可以觀察元素屬性、子節點、文字的變化。

需要先建立 MutationObserver 建構函式並傳入一個回撥函式:

let observer = new MutationObserver(() => console.log("DOM 改變"));

observe() 方法

使用 observe() 方法與 DOM 關聯起來,接收兩個必需引數:要觀察變化的 DOM 節點,一個 MutationObserverInit 物件。

let observer = new MutationObserver(() => console.log("xkc div -- attributes changed"));
let xkcDiv = document.createElement("div");
document.body.appendChild(xkcDiv);
observer.observe(xkcDiv, { attributes: true });
xkcDiv.setAttribute("id", "xkc");
console.log("Synchronization task");
/**
 * Synchronization task
 * xkc div -- attributes changed
 */

由於 MutationObserver 中的回撥是非同步回撥,因此會先執行其他所有的同步任務。

  • 回撥與 MutationRecord

傳入回撥函式的第一個引數是一個 MutationRecord 例項。

每個回撥都會收到一個 MutationRecord 例項的陣列。其中包括一些 DOM 發生的改變資訊。每次執行回撥都會傳入一個 MutationRecord 的例項,與執行的順序所對應。

// 清空 body 內容
document.body.innerHTML = "";
let observer1 = new MutationObserver((MutationRecord) =>
  console.log(MutationRecord)
);
let xkcDiv1 = document.createElement("div");
document.body.appendChild(xkcDiv1);
observer1.observe(xkcDiv1, { attributes: true });
xkcDiv1.setAttribute("id", "xkc");
/**
[
  {
    ddedNodes: NodeList [],
    attributeName: "id",
    attributeNamespace: null,
    nextSibling: null,
    oldValue: null,
    previousSibling: null,
    removedNodes: NodeList [],
    target: div#xkc,
    type: "attributes"
  }
]
*/

連續修改會生成多個 MutationRecord 例項。

xkcDiv1.setAttribute("name", "xkc");
xkcDiv1.setAttribute("age", "20");
/*
[
  {
    addedNodes: NodeList [],
    attributeName: "id",
    attributeNamespace: null,
    nextSibling: null,
    oldValue: null,
    previousSibling: null,
    removedNodes: NodeList [],
    target: div#xkc,
    type: "attributes"
  },
  {
    addedNodes: NodeList [],
    attributeName: "name",
    attributeNamespace: null,
    nextSibling: null,
    oldValue: null,
    previousSibling: null,
    removedNodes: NodeList [],
    target: div#xkc,
    type: "attributes"
  },
  {
    addedNodes: NodeList [],
    attributeName: "age",
    attributeNamespace: null,
    nextSibling: null,
    oldValue: null,
    previousSibling: null,
    removedNodes: NodeList [],
    target: div#xkc,
    type: "attributes"
  }
]
*/

MutationRecord 例項的屬性

屬性說明
target被修改影響的目標節點
type字串,表示變化的型別:“attributes”、“characterData” 或 “childList”
oldValue如果在 MutationObserverInit 物件中啟用(attributeOldValue 或 characterData OldValue 為 true),“attributes” 或 “characterData” 的變化事件會設定這個屬性為被替代的值,“childList” 型別的變化始終將這個屬性設定為 null
attributeName對於 “attributes” 型別的變化,儲存被修改屬性的名稱,其他變化事件這個屬性為 null
attributeNamespace對於使用名稱空間的 “attributes” 型別的變化,這裡儲存被修改屬性的名稱,其他變化事件這個屬性為 null
addedNodes對於 “childList” 型別的變化,返回包含變化中新增的節點的 NodeList,預設為空 NodeList
removedNodes對於 “childList” 型別的變化,返回包含變化中刪除的節點的 NodeList ,預設為空 NodeList
previousSibling對於 “childList” 型別的變化,返回變化的節點的前一個同胞 Node ,預設為 null
nextSibling對於 “childList” 型別的變化,返回變化的節點的最後一個同胞 Node ,預設為 null

傳入回撥函式的第二個引數是一個觀察變化的 MutationObserver 的例項。

// 清空 body 內容
document.body.innerHTML = "";
let observer2 = new MutationObserver((mutationRecord, mutationObserver) => {
  console.log(mutationRecord, mutationObserver);
});
let xkcDiv2 = document.createElement("div");
document.body.appendChild(xkcDiv2);
observer2.observe(xkcDiv2, { attributes: true });
xkcDiv2.setAttribute("id", "xkc");
// [MutationRecord] MutationObserver 

disconnect() 方法

預設情況下,只要觀察的元素不被垃圾回收, MutationObserver 的回撥就會響應 DOM 變化事件,而被執行。想要提前終止執行回撥,可以使用 disconnect() 方法。

// 清空 body 內容
document.body.innerHTML = "";
let observer3 = new MutationObserver(() =>
  console.log("xkc div -- attributes changed")
);
let xkcDiv3 = document.createElement("div");
document.body.appendChild(xkcDiv3);
observer3.observe(xkcDiv3, { attributes: true });
xkcDiv3.setAttribute("id", "xkc");
// 終止
observer3.disconnect();
xkcDiv3.setAttribute("name", "xkc");
// 無任何列印輸出

同步使用 disconnect() 方法之後,不僅會停止此後變化事件的回撥,也會拋棄已經加入任務佇列中的非同步回撥。

想要讓加入到任務佇列中的非同步回撥執行,可使用 setTimeout() 讓已經入列的回撥執行完畢再呼叫 disconnect()

// 清空 body 內容
document.body.innerHTML = "";
let observer4 = new MutationObserver(() =>
  console.log("xkc div -- attributes changed")
);
let xkcDiv4 = document.createElement("div");
document.body.appendChild(xkcDiv4);
observer4.observe(xkcDiv4, { attributes: true });
xkcDiv4.setAttribute("id", "xkc");
// setTimeout
setTimeout(() => {
  // 終止
  observer4.disconnect();
  xkcDiv4.setAttribute("name", "xkc");
}, 0);
/*
[
  {
    addedNodes: NodeList [],
	attributeName: "id",
	attributeNamespace: null,
	nextSibling: null,
	oldValue: null,
	previousSibling: null,
	removedNodes: NodeList [],
	target: div#xkc,
	type: "attributes"
  }
]
*/

MutationObserverInit 與觀察範圍

屬性說明
subtree布林值,表示除了目標節點,是否觀察目標節點的子樹。如果是 false ,則只觀察目標節點變化,如果是 true,則觀察目標節點和其子樹。預設為 false
attributes布林值,表示是否觀察目標節點的屬性變化。預設為 false
attributeFilter字串陣列。表示要觀察的那些屬性的變化。設定為 true 時,也會將 attributes 設為 true,預設觀察所有屬性
attributeOldValue布林值,表示 MutationRecord 是否記錄變化之前的屬性值,設定為 true 時,也會將 attributes 設為 true ,預設為 false
characterData布林值,表示修改字元資料是否觸發變化事件,預設為 false
characterDataOldValue布林值,表示 MutationRecord 是否記錄變化之前的字元資料,設定為 true 時,也會將 characterData 設為 true,預設為 false
childList布林值,表示修改目標節點的子節點是否觸發變化事件,預設為 false

takeRecords() 方法

呼叫 MutationObserver 例項的 takeRecords() 方法可以清空記錄列隊,取出並返回其中的所有 MutationRecord 例項。

let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords));
observer.observe(document.body, { attributes: true });
document.body.className = "xkc";
document.body.className = "strive";
console.log(observer.takeRecords());
console.log(observer.takeRecords());
// [MutationRecord, MutationRecord]
// []

相關文章