了解HTML5中的MutationObserver

MutationObserver翻譯過來就是變動(dòng)觀察器十兢,字面上就可以理解這是用來觀察Node(節(jié)點(diǎn))變化的。MutationObserver是在DOM4規(guī)范中定義的耘擂,它的前身是MutationEvent事件胆剧,該事件最初在DOM2事件規(guī)范中介紹,到來了DOM3事件規(guī)范中正式定義醉冤,但是由于該事件存在兼容性以及性能上的問題被棄用秩霍。

MutationEvent
雖然MutationEvent已經(jīng)被棄用,但是我們還是需要了解它蚁阳,可能你會(huì)為了瀏覽器兼容性的問題而遇到它(萬惡的瀏覽器兼容性)铃绒。

MutationEvent總共有7種事件:DOMNodeInserted、DOMNodeRemoved螺捐、DOMSubtreeModified颠悬、DOMAttrModified矮燎、
DOMCharacterDataModified、DOMNodeInsertedIntoDocument和DOMNodeRemovedFromDocument赔癌。

MutationEvent的兼容性:

MutationEvent在IE瀏覽器中最低支持到IE9
在webkit內(nèi)核的瀏覽器中诞外,不支持DOMAttrModified事件
IE,Edge以及Firefox瀏覽器下不支持DOMNodeInsertedIntoDocument和DOMNodeRemovedFromDocument事件
MutationEvent中的所有事件都被設(shè)計(jì)成無法取消灾票,如果可以取消MutationEvent事件則會(huì)導(dǎo)致現(xiàn)有的DOM接口無法對(duì)文檔進(jìn)行改變峡谊,比如appendChild,remove等添加和刪除節(jié)點(diǎn)的DOM操作刊苍。
MutationEvent中最令人詬病的就是性能以及安全性的問題既们,比如下面這個(gè)例子:

document.addEventListener('DOMNodeInserted', function() {
    var newEl = document.createElement('div');
    document.body.appendChild(newEl);
});

document下的所有DOM添加操作都會(huì)觸發(fā)DOMNodeInserted方法,這時(shí)就會(huì)出現(xiàn)循環(huán)調(diào)用DOMNodeInserted方法正什,導(dǎo)致瀏覽器崩潰贤壁。還有就是MutationEvent是事件機(jī)制,因此會(huì)有一般事件都存在的捕獲和冒泡階段埠忘,此時(shí)如果在捕獲和冒泡階段又對(duì)DOM進(jìn)行了操作會(huì)拖慢瀏覽器的運(yùn)行脾拆。

另一點(diǎn)就是MutationEvent事件機(jī)制是同步的,也就是說每次DOM修改就會(huì)觸發(fā)莹妒,修改幾次就觸發(fā)幾次,嚴(yán)重降低瀏覽器的運(yùn)行名船,嚴(yán)重時(shí)甚至導(dǎo)致線程崩潰

<div id='block'></div>
var i=0;
block.addEventListener('DOMNodeInserted', function(e) {
     i++                                  
});
block.appendChild(docuemnt.createTextNode('1'));
console.log(i)                  //1
block.appendChild(docuemnt.createTextNode('2'));
console.log(i)                  //2
block.appendChild(docuemnt.createTextNode('3'));
console.log(i)                  //3

再看個(gè)例子:

<div id='block'>
  <span id='span'>Text</span>
</div>
block.addEventListener('DOMNodeInserted', function(e) {
     console.log('1');                                  //1
});
span.appendChild(docuemnt.createTextNode('other Text'));

span元素中添加節(jié)點(diǎn)會(huì)觸發(fā)block中的DOMNodeInserted事件,可是你只想觀察block的變化旨怠,不想觀察block中子節(jié)點(diǎn)的變化渠驼,這時(shí)你不得不在DOMNodeInserted事件中進(jìn)行過濾,把對(duì)span的操作忽略掉鉴腻,這無疑增加了操作的復(fù)雜性迷扇。

MutationObserver
MutationObserver的出現(xiàn)就是為了解決MutationEvent帶來的問題。
先看一下MutationObserver的瀏覽器兼容性:

image.png

我們可以看到MutationObserver在IE中最低要就是IE11爽哎,如果你的網(wǎng)站不需要支持IE或者只支持到IE11蜓席,那么你可以放心的使用MutationObserver,否則你可能需要用到上面提到的MutationEvent事件课锌,當(dāng)然如果你的網(wǎng)站還要支持IE8及以下版本厨内,那么你只能和Mutation說拜拜了。

MutationObserver是一個(gè)構(gòu)造器渺贤,接受一個(gè)callback參數(shù)雏胃,用來處理節(jié)點(diǎn)變化的回調(diào)函數(shù),返回兩個(gè)參數(shù)志鞍,mutations:節(jié)點(diǎn)變化記錄列表(sequence<MutationRecord>)瞭亮,observer:構(gòu)造MutationObserver對(duì)象。

var observe = new MutationObserver(function(mutations,observer){
})

MutationObserver對(duì)象有三個(gè)方法固棚,分別如下:

observe:設(shè)置觀察目標(biāo)统翩,接受兩個(gè)參數(shù)兼丰,target:觀察目標(biāo),options:通過對(duì)象成員來設(shè)置觀察選項(xiàng)
disconnect:阻止觀察者觀察任何改變
takeRecords:清空記錄隊(duì)列并返回里面的內(nèi)容
關(guān)于observe方法中options參數(shù)有已下幾個(gè)選項(xiàng):

childList:設(shè)置true唆缴,表示觀察目標(biāo)子節(jié)點(diǎn)的變化,比如添加或者刪除目標(biāo)子節(jié)點(diǎn)黍翎,不包括修改子節(jié)點(diǎn)以及子節(jié)點(diǎn)后代的變化
attributes:設(shè)置true面徽,表示觀察目標(biāo)屬性的改變
characterData:設(shè)置true,表示觀察目標(biāo)數(shù)據(jù)的改變
subtree:設(shè)置為true匣掸,目標(biāo)以及目標(biāo)的后代改變都會(huì)觀察
attributeOldValue:如果屬性為true或者省略趟紊,則相當(dāng)于設(shè)置為true,表示需要記錄改變前的目標(biāo)屬性值碰酝,設(shè)置了attributeOldValue可以省略attributes設(shè)置
characterDataOldValue:如果characterData為true或省略霎匈,則相當(dāng)于設(shè)置為true,表示需要記錄改變之前的目標(biāo)數(shù)據(jù),設(shè)置了characterDataOldValue可以省略characterData設(shè)置
attributeFilter:如果不是所有的屬性改變都需要被觀察送爸,并且attributes設(shè)置為true或者被忽略铛嘱,那么設(shè)置一個(gè)需要觀察的屬性本地名稱(不需要命名空間)的列表
下表描述了MutationObserver選項(xiàng)與MutationEvent名稱之間的對(duì)應(yīng)關(guān)系:

MutationEvent MutationObserver options
DOMNodeInserted { childList: true, subtree: true }
DOMNodeRemoved { childList: true, subtree: true }
DOMSubtreeModified { childList: true, subtree: true }
DOMAttrModified { attributes: true, subtree: true }
DOMCharacterDataModified { characterData: true, subtree: true }

從上表我們也可以看出相比與MutationEvent而言MutationObserver極大地增加了靈活性,可以設(shè)置各種各樣的選項(xiàng)來滿足程序員對(duì)目標(biāo)的觀察袭厂。

我們簡單看幾個(gè)例子:

<div id='target' class='block' name='target'>
    target的第一個(gè)子節(jié)點(diǎn)
    <p>
       <span>target的后代</span>
    </p>
</div>

1.callback的回調(diào)次數(shù)

var target=document.getElementById('target');
var i=0
var observe=new MutationObserver(function (mutations,observe) {
    i++   
});
observe.observe(target,{ childList: true});
target.appendChild(docuemnt.createTextNode('1'));
target.appendChild(docuemnt.createTextNode('2'));
target.appendChild(docuemnt.createTextNode('3'));
console.log(i)                //1

MutationObserver的callback回調(diào)函數(shù)是異步的墨吓,只有在全部DOM操作完成之后才會(huì)調(diào)用callback。

2.當(dāng)只設(shè)置{ childList: true}時(shí),表示觀察目標(biāo)子節(jié)點(diǎn)的變化

var observe=new MutationObserver(function (mutations,observe) {
    debugger;
    console.log(mutations);
    //observe.discount();     
});

observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text節(jié)點(diǎn)'));   //增加節(jié)點(diǎn)纹磺,觀察到變化
target.childNodes[0].remove();                                //刪除節(jié)點(diǎn)帖烘,可以觀察到
target.childNodes[0].textContent='改變子節(jié)點(diǎn)的后代';             //不會(huì)觀察到

如果想要觀察到子節(jié)點(diǎn)以及后代的變化需設(shè)置{childList: true, subtree: true}

attributes選項(xiàng)用來觀察目標(biāo)屬性的變化,用法類似與childList,目標(biāo)屬性的刪除添加以及修改都會(huì)被觀察到橄杨。

3.我們需要注意的是characterData這個(gè)選項(xiàng)秘症,它是用來觀察CharacterData類型的節(jié)點(diǎn)的,只有在改變節(jié)點(diǎn)數(shù)據(jù)時(shí)才會(huì)觀察到式矫,如果你刪除或者增加節(jié)點(diǎn)都不會(huì)進(jìn)行觀察乡摹,還有如果對(duì)不是CharacterData類型的節(jié)點(diǎn)的改變不會(huì)觀察到,比如:

observe.observe(target,{ characterData: true, subtree: true});
target.childNodes[0].textContent='改變Text節(jié)點(diǎn)';              //觀察到
target.childNodes[1].textContent='改變p元素內(nèi)容';              //不會(huì)觀察到
target.appendChild(document.createTextNode('新增Text節(jié)點(diǎn)'));  //不會(huì)觀察到
target.childNodes[0].remove();                               //刪除TEXT節(jié)點(diǎn)也不會(huì)觀察到
我們只需要記住只有對(duì)CharacterData類型的節(jié)點(diǎn)的數(shù)據(jù)改變才會(huì)被characterData為true的選項(xiàng)所觀察到采转。

4.最后關(guān)注一個(gè)特別有用的選項(xiàng)attributeFilter趟卸,這個(gè)選項(xiàng)主要是用來篩選要觀察的屬性,比如你只想觀察目標(biāo)style屬性的變化氏义,這時(shí)可以如下設(shè)置:

observe.observe(target,{ attributeFilter: ['style'], subtree: true});
target.style='color:red'; //可以觀察到
target.removeAttribute('name'); //刪除name屬性锄列,無法觀察到
disconnect方法是用來阻止觀察的,當(dāng)你不再想觀察目標(biāo)節(jié)點(diǎn)的變化時(shí)可以調(diào)用observe.disconnect()方法來取消觀察惯悠。

takeRecords方法是用來取出記錄隊(duì)列中的記錄邻邮。它的一個(gè)作用是,比如你對(duì)一個(gè)節(jié)點(diǎn)的操作你不想馬上就做出反應(yīng)克婶,過段時(shí)間在顯示改變了節(jié)點(diǎn)的內(nèi)容筒严。

var observe=new MutationObserver(function(){});
observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text節(jié)點(diǎn)'));
var record = observe.takeRecords();              //此時(shí)record保存了改變記錄列表  
//當(dāng)調(diào)用takeRecords方法時(shí)丹泉,記錄隊(duì)列被清空因此不會(huì)觸發(fā)MutationObserver中的callback回調(diào)方法。
target.appendChild(document.createElement('span'));
observe.disconnect();                            //停止對(duì)target的觀察鸭蛙。
//MutationObserver中的回調(diào)函數(shù)只有一個(gè)記錄摹恨,只記錄了新增span元素

//之后可以對(duì)record進(jìn)行操作
//...

MutationRecord
變動(dòng)記錄中的屬性如下:

1.type:如果是屬性變化,返回"attributes"娶视,如果是一個(gè)CharacterData節(jié)點(diǎn)(Text節(jié)點(diǎn)晒哄、Comment節(jié)點(diǎn))變化,返回"characterData"肪获,節(jié)點(diǎn)樹變化返回"childList"
2.target:返回影響改變的節(jié)點(diǎn)
3.addedNodes:返回添加的節(jié)點(diǎn)列表
4.removedNodes:返回刪除的節(jié)點(diǎn)列表
5.previousSibling:返回分別添加或刪除的節(jié)點(diǎn)的上一個(gè)兄弟節(jié)點(diǎn)寝凌,否則返回null
6.nextSibling:返回分別添加或刪除的節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn),否則返回null
7.attributeName:返回已更改屬性的本地名稱孝赫,否則返回null
8.attributeNamespace:返回已更改屬性的名稱空間较木,否則返回null
9.oldValue:返回值取決于type。對(duì)于"attributes"青柄,它是更改之前的屬性的值伐债。對(duì)于"characterData",它是改變之前節(jié)點(diǎn)的數(shù)據(jù)致开。對(duì)于"childList"泳赋,它是null
其中 type、target這兩個(gè)屬性不管是哪種觀察方式都會(huì)有返回值喇喉,其他屬性返回值與觀察方式有關(guān)祖今,比如只有當(dāng)attributeOldValue或者characterDataOldValue為true時(shí)oldValue才有返回值,只有改變屬性時(shí)拣技,attributeName才有返回值等千诬。

原作者:fanqifeng
原文地址:https://segmentfault.com/a/1190000012787829

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市膏斤,隨后出現(xiàn)的幾起案子徐绑,更是在濱河造成了極大的恐慌,老刑警劉巖莫辨,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傲茄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沮榜,警方通過查閱死者的電腦和手機(jī)盘榨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟆融,“玉大人草巡,你說我怎么就攤上這事⌒退郑” “怎么了山憨?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵查乒,是天一觀的道長。 經(jīng)常有香客問我郁竟,道長玛迄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任棚亩,我火速辦了婚禮蓖议,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔑舞。我一直安慰自己,他們只是感情好嘹屯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布攻询。 她就那樣靜靜地躺著,像睡著了一般州弟。 火紅的嫁衣襯著肌膚如雪钧栖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天婆翔,我揣著相機(jī)與錄音拯杠,去河邊找鬼。 笑死啃奴,一個(gè)胖子當(dāng)著我的面吹牛潭陪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播最蕾,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼依溯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了瘟则?” 一聲冷哼從身側(cè)響起黎炉,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎醋拧,沒想到半個(gè)月后慷嗜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丹壕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年庆械,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菌赖。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡干奢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盏袄,到底是詐尸還是另有隱情忿峻,我是刑警寧澤薄啥,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站逛尚,受9級(jí)特大地震影響垄惧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绰寞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一到逊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧滤钱,春花似錦觉壶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至他炊,卻和暖如春争剿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痊末。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工蚕苇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凿叠。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓涩笤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盒件。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辆它,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1、概述 Mutation Observer(變動(dòng)觀察器)是監(jiān)視DOM變動(dòng)的接口履恩。當(dāng)DOM對(duì)象樹發(fā)生任何變動(dòng)時(shí)锰茉,M...
    hukei閱讀 2,623評(píng)論 0 0
  • 1 概述 Mutation observer 是用于代替 Mutation events 作為觀察DOM樹結(jié)構(gòu)發(fā)生...
    falm閱讀 53,199評(píng)論 4 31
  • 5. DOM 5.1document對(duì)象 5.1.1DOM的含義 文檔對(duì)象模型(Document Object M...
    暗夜的怒吼閱讀 562評(píng)論 0 0
  • 超級(jí)個(gè)體 1,外向指從與外界接觸中一去能量切心,而內(nèi)向指更多從自我接觸中吸取能量飒筑。 2,內(nèi)外向人都能很好的溝通绽昏。外向的...
    默默地走下去閱讀 294評(píng)論 0 0
  • 輪播的實(shí)現(xiàn)原理是怎樣的协屡?如果讓你來實(shí)現(xiàn),你會(huì)抽象出哪些函數(shù)(or接口)供使用全谤?(比如 play()) 輪播的實(shí)現(xiàn)原...
    Schrodinger的貓閱讀 217評(píng)論 0 0