W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
Mutation Observer API 用來監(jiān)視 DOM 變動。DOM 的任何變動,比如節(jié)點的增減、屬性的變動、文本內(nèi)容的變動,這個 API 都可以得到通知。
概念上,它很接近事件,可以理解為當 DOM 發(fā)生變動,就會觸發(fā) Mutation Observer 事件。但是,它與事件有一個本質(zhì)不同:事件是同步觸發(fā),也就是說,DOM 的變動立刻會觸發(fā)相應(yīng)的事件;Mutation Observer 則是異步觸發(fā),DOM 的變動并不會馬上觸發(fā),而是要等到當前所有 DOM 操作都結(jié)束才觸發(fā)。
這樣設(shè)計是為了應(yīng)付 DOM 變動頻繁的特點。舉例來說,如果在文檔中連續(xù)插入1000個<p>
元素,就會連續(xù)觸發(fā)1000個插入事件,執(zhí)行每個事件的回調(diào)函數(shù),這很可能造成瀏覽器的卡頓;而 Mutation Observer 完全不同,只在1000個段落都插入結(jié)束后才會觸發(fā),而且只觸發(fā)一次。
Mutation Observer 有以下特點。
使用時,首先使用MutationObserver
構(gòu)造函數(shù),新建一個觀察器實例,同時指定這個實例的回調(diào)函數(shù)。
var observer = new MutationObserver(callback);
上面代碼中的回調(diào)函數(shù),會在每次 DOM 變動后調(diào)用。該回調(diào)函數(shù)接受兩個參數(shù),第一個是變動數(shù)組,第二個是觀察器實例,下面是一個例子。
var observer = new MutationObserver(function (mutations, observer) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
observe
方法用來開始監(jiān)聽,它接受兩個參數(shù)。
var article = document.querySelector('article');
var options = {
'childList': true,
'attributes':true
} ;
observer.observe(article, options);
上面代碼中,observe
方法接受兩個參數(shù),第一個是所要觀察的DOM元素是article
,第二個是所要觀察的變動類型(子節(jié)點變動和屬性變動)。
觀察器所能觀察的 DOM 變動類型(即上面代碼的options
對象),有以下幾種。
想要觀察哪一種變動類型,就在option
對象中指定它的值為true
。需要注意的是,如果設(shè)置觀察subtree
的變動,必須同時指定childList
、attributes
和characterData
中的一種或多種。
除了變動類型,options
對象還可以設(shè)定以下屬性:
attributeOldValue
:類型為布爾值,表示觀察attributes
變動時,是否需要記錄變動前的屬性值。characterDataOldValue
:類型為布爾值,表示觀察characterData
變動時,是否需要記錄變動前的值。attributeFilter
:類型為數(shù)組,表示需要觀察的特定屬性(比如['class','src']
)。// 開始監(jiān)聽文檔根節(jié)點(即<html>標簽)的變動
mutationObserver.observe(document.documentElement, {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
對一個節(jié)點添加觀察器,就像使用addEventListener
方法一樣,多次添加同一個觀察器是無效的,回調(diào)函數(shù)依然只會觸發(fā)一次。但是,如果指定不同的options
對象,就會被當作兩個不同的觀察器。
下面的例子是觀察新增的子節(jié)點。
var insertedNodes = [];
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
for (var i = 0; i < mutation.addedNodes.length; i++)
insertedNodes.push(mutation.addedNodes[i]);
})
});
observer.observe(document, { childList: true });
console.log(insertedNodes);
disconnect
方法用來停止觀察。調(diào)用該方法后,DOM 再發(fā)生變動,也不會觸發(fā)觀察器。
observer.disconnect();
takeRecords
方法用來清除變動記錄,即不再處理未處理的變動。該方法返回變動記錄的數(shù)組。
observer.takeRecords();
下面是一個例子。
// 保存所有沒有被觀察器處理的變動
var changes = mutationObserver.takeRecords();
// 停止觀察
mutationObserver.disconnect();
DOM 每次發(fā)生變化,就會生成一條變動記錄。這個變動記錄對應(yīng)一個MutationRecord
對象,該對象包含了與變動相關(guān)的所有信息。Mutation Observer 處理的是一個個MutationRecord
對象所組成的數(shù)組。
MutationRecord
對象包含了DOM的相關(guān)信息,有如下屬性:
type
:觀察的變動類型(attribute
、characterData
或者childList
)。target
:發(fā)生變動的DOM節(jié)點。addedNodes
:新增的DOM節(jié)點。removedNodes
:刪除的DOM節(jié)點。previousSibling
:前一個同級節(jié)點,如果沒有則返回null
。nextSibling
:下一個同級節(jié)點,如果沒有則返回null
。attributeName
:發(fā)生變動的屬性。如果設(shè)置了attributeFilter
,則只返回預(yù)先指定的屬性。oldValue
:變動前的值。這個屬性只對attribute
和characterData
變動有效,如果發(fā)生childList
變動,則返回null
。下面的例子說明如何讀取變動記錄。
var callback = function(records){
records.map(function(record){
console.log('Mutation type: ' + record.type);
console.log('Mutation target: ' + record.target);
});
};
var mo = new MutationObserver(callback);
var option = {
'childList': true,
'subtree': true
};
mo.observe(document.body, option);
上面代碼的觀察器,觀察<body>
的所有下級節(jié)點(childList
表示觀察子節(jié)點,subtree
表示觀察后代節(jié)點)的變動?;卣{(diào)函數(shù)會在控制臺顯示所有變動的類型和目標節(jié)點。
下面的例子說明如何追蹤屬性的變動。
var callback = function (records) {
records.map(function (record) {
console.log('Previous attribute value: ' + record.oldValue);
});
};
var mo = new MutationObserver(callback);
var element = document.getElementById('#my_element');
var options = {
'attributes': true,
'attributeOldValue': true
}
mo.observe(element, options);
上面代碼先設(shè)定追蹤屬性變動('attributes': true
),然后設(shè)定記錄變動前的值。實際發(fā)生變動時,會將變動前的值顯示在控制臺。
網(wǎng)頁加載的時候,DOM 節(jié)點的生成會產(chǎn)生變動記錄,因此只要觀察 DOM 的變動,就能在第一時間觸發(fā)相關(guān)事件,因此也就沒有必要使用DOMContentLoaded
事件。
var observer = new MutationObserver(callback);
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
上面代碼中,監(jiān)聽document.documentElement
(即HTML節(jié)點)的子節(jié)點的變動,subtree
屬性指定監(jiān)聽還包括后代節(jié)點。因此,任意一個網(wǎng)頁元素一旦生成,就能立刻被監(jiān)聽到。
下面的代碼,使用MutationObserver
對象封裝一個監(jiān)聽 DOM 生成的函數(shù)。
(function(win){
'use strict';
var listeners = [];
var doc = win.document;
var MutationObserver = win.MutationObserver || win.WebKitMutationObserver;
var observer;
function ready(selector, fn){
// 儲存選擇器和回調(diào)函數(shù)
listeners.push({
selector: selector,
fn: fn
});
if(!observer){
// 監(jiān)聽document變化
observer = new MutationObserver(check);
observer.observe(doc.documentElement, {
childList: true,
subtree: true
});
}
// 檢查該節(jié)點是否已經(jīng)在DOM中
check();
}
function check(){
// 檢查是否匹配已儲存的節(jié)點
for(var i = 0; i < listeners.length; i++){
var listener = listeners[i];
// 檢查指定節(jié)點是否有匹配
var elements = doc.querySelectorAll(listener.selector);
for(var j = 0; j < elements.length; j++){
var element = elements[j];
// 確?;卣{(diào)函數(shù)只會對該元素調(diào)用一次
if(!element.ready){
element.ready = true;
// 對該節(jié)點調(diào)用回調(diào)函數(shù)
listener.fn.call(element, element);
}
}
}
}
// 對外暴露ready
win.ready = ready;
})(this);
ready('.foo', function(element){
// ...
});
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: