瞭解HTML5中的MutationObserver

MutationObserver翻譯過來就是變更觀察器,字面上就能夠理解這是用來觀察Node(節點)變化的。MutationObserver是在DOM4規範中定義的,它的前身是MutationEvent事件,該事件最初在DOM2事件規範中介紹,到來了DOM3事件規範中正式定義,可是因爲該事件存在兼容性以及性能上的問題被棄用。程序員

MutationEvent

雖然MutationEvent已經被棄用,可是咱們仍是須要了解它,可能你會爲了瀏覽器兼容性的問題而遇到它(萬惡的瀏覽器兼容性)。web

MutationEvent總共有7種事件:DOMNodeInsertedDOMNodeRemovedDOMSubtreeModifiedDOMAttrModified
DOMCharacterDataModifiedDOMNodeInsertedIntoDocumentDOMNodeRemovedFromDocument瀏覽器

MutationEvent的兼容性:安全

  1. MutationEvent在IE瀏覽器中最低支持到IE9
  2. 在webkit內核的瀏覽器中,不支持DOMAttrModified事件
  3. IE,Edge以及Firefox瀏覽器下不支持DOMNodeInsertedIntoDocumentDOMNodeRemovedFromDocument事件

MutationEvent中的全部事件都被設計成沒法取消,若是能夠取消MutationEvent事件則會致使現有的DOM接口沒法對文檔進行改變,好比appendChild,remove等添加和刪除節點的DOM操做。
MutationEvent中最使人詬病的就是性能以及安全性的問題,好比下面這個例子:app

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

document下的全部DOM添加操做都會觸發DOMNodeInserted方法,這時就會出現循環調用DOMNodeInserted方法,致使瀏覽器崩潰。還有就是MutationEvent是事件機制,所以會有通常事件都存在的捕獲和冒泡階段,此時若是在捕獲和冒泡階段又對DOM進行了操做會拖慢瀏覽器的運行。異步

另外一點就是MutationEvent事件機制是同步的,也就是說每次DOM修改就會觸發,修改幾回就觸發幾回,嚴重下降瀏覽器的運行,嚴重時甚至致使線程崩潰函數

<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

再看個例子:性能

<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元素中添加節點會觸發block中的DOMNodeInserted事件,但是你只想觀察block的變化,不想觀察block中子節點的變化,這時你不得不在DOMNodeInserted事件中進行過濾,把對span的操做忽略掉,這無疑增長了操做的複雜性。網站

MutationObserver

MutationObserver的出現就是爲了解決MutationEvent帶來的問題。
先看一下MutationObserver的瀏覽器兼容性:spa

MutationObserver瀏覽器兼容性

咱們能夠看到MutationObserver在IE中最低要就是IE11,若是你的網站不須要支持IE或者只支持到IE11,那麼你能夠放心的使用MutationObserver,不然你可能須要用到上面提到的MutationEvent事件,固然若是你的網站還要支持IE8及如下版本,那麼你只能和Mutation說拜拜了。

MutationObserver是一個構造器,接受一個callback參數,用來處理節點變化的回調函數,返回兩個參數,mutations:節點變化記錄列表(sequence<MutationRecord>),observer:構造MutationObserver對象。

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

MutationObserver對象有三個方法,分別以下:

  1. observe:設置觀察目標,接受兩個參數,target:觀察目標,options:經過對象成員來設置觀察選項
  2. disconnect:阻止觀察者觀察任何改變
  3. takeRecords:清空記錄隊列並返回裏面的內容

關於observe方法中options參數有已下幾個選項:

  1. childList:設置true,表示觀察目標子節點的變化,好比添加或者刪除目標子節點,不包括修改子節點以及子節點後代的變化
  2. attributes:設置true,表示觀察目標屬性的改變
  3. characterData:設置true,表示觀察目標數據的改變
  4. subtree:設置爲true,目標以及目標的後代改變都會觀察
  5. attributeOldValue:若是屬性爲true或者省略,則至關於設置爲true,表示須要記錄改變前的目標屬性值,設置了attributeOldValue能夠省略attributes設置
  6. characterDataOldValue:若是characterData爲true或省略,則至關於設置爲true,表示須要記錄改變以前的目標數據,設置了characterDataOldValue能夠省略characterData設置
  7. attributeFilter:若是不是全部的屬性改變都須要被觀察,而且attributes設置爲true或者被忽略,那麼設置一個須要觀察的屬性本地名稱(不須要命名空間)的列表

下表描述了MutationObserver選項與MutationEvent名稱之間的對應關係:

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極大地增長了靈活性,能夠設置各類各樣的選項來知足程序員對目標的觀察。

咱們簡單看幾個例子:

<div id='target' class='block' name='target'>
    target的第一個子節點
    <p>
       <span>target的後代</span>
    </p>
</div>

1.callback的回調次數

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回調函數是異步的,只有在所有DOM操做完成以後纔會調用callback。

2.當只設置{ childList: true}時,表示觀察目標子節點的變化

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

observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text節點'));   //增長節點,觀察到變化
target.childNodes[0].remove();                                //刪除節點,能夠觀察到
target.childNodes[0].textContent='改變子節點的後代';             //不會觀察到

若是想要觀察到子節點以及後代的變化需設置{childList: true, subtree: true}

attributes選項用來觀察目標屬性的變化,用法相似與childList,目標屬性的刪除添加以及修改都會被觀察到。

3.咱們須要注意的是characterData這個選項,它是用來觀察CharacterData類型的節點的,只有在改變節點數據時纔會觀察到,若是你刪除或者增長節點都不會進行觀察,還有若是對不是CharacterData類型的節點的改變不會觀察到,好比:

observe.observe(target,{ characterData: true, subtree: true});
target.childNodes[0].textContent='改變Text節點';              //觀察到
target.childNodes[1].textContent='改變p元素內容';              //不會觀察到
target.appendChild(document.createTextNode('新增Text節點'));  //不會觀察到
target.childNodes[0].remove();                               //刪除TEXT節點也不會觀察到

咱們只須要記住只有對CharacterData類型的節點的數據改變纔會被characterData爲true的選項所觀察到。

4.最後關注一個特別有用的選項attributeFilter,這個選項主要是用來篩選要觀察的屬性,好比你只想觀察目標style屬性的變化,這時能夠以下設置:

observe.observe(target,{ attributeFilter: ['style'], subtree: true});
target.style='color:red';                      //能夠觀察到
target.removeAttribute('name');                //刪除name屬性,沒法觀察到

disconnect方法是用來阻止觀察的,當你再也不想觀察目標節點的變化時能夠調用observe.disconnect()方法來取消觀察。

takeRecords方法是用來取出記錄隊列中的記錄。它的一個做用是,好比你對一個節點的操做你不想立刻就作出反應,過段時間在顯示改變了節點的內容。

var observe=new MutationObserver(function(){});
observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text節點'));
var record = observe.takeRecords();              //此時record保存了改變記錄列表  
//當調用takeRecords方法時,記錄隊列被清空所以不會觸發MutationObserver中的callback回調方法。
target.appendChild(document.createElement('span'));
observe.disconnect();                            //中止對target的觀察。
//MutationObserver中的回調函數只有一個記錄,只記錄了新增span元素

//以後能夠對record進行操做
//...

MutationRecord
變更記錄中的屬性以下:

  1. type:若是是屬性變化,返回"attributes",若是是一個CharacterData節點(Text節點、Comment節點)變化,返回"characterData",節點樹變化返回"childList"
  2. target:返回影響改變的節點
  3. addedNodes:返回添加的節點列表
  4. removedNodes:返回刪除的節點列表
  5. previousSibling:返回分別添加或刪除的節點的上一個兄弟節點,不然返回null
  6. nextSibling:返回分別添加或刪除的節點的下一個兄弟節點,不然返回null
  7. attributeName:返回已更改屬性的本地名稱,不然返回null
  8. attributeNamespace:返回已更改屬性的名稱空間,不然返回null
  9. oldValue:返回值取決於type。對於"attributes",它是更改以前的屬性的值。對於"characterData",它是改變以前節點的數據。對於"childList",它是null

其中 typetarget這兩個屬性不論是哪一種觀察方式都會有返回值,其餘屬性返回值與觀察方式有關,好比只有當attributeOldValue或者characterDataOldValue爲true時oldValue纔有返回值,只有改變屬性時,attributeName纔有返回值等。

相關文章
相關標籤/搜索