1. 程式人生 > 實用技巧 >監視DOM元素變動:MutationObserver

監視DOM元素變動:MutationObserver

MutationObserver

MutationObserver:變動觀察器,監視DOM元素的所有變動,包括節點的增減、屬性的變動、文字內容的變動;
注:類似於事件,做了一個對應操作,觸發對應事件的執行,但是它們有著根本之間的區別:事件的執行時同步的,觸發的同時,每個事件依次執行,但是observer是非同步觸發的,等到所有dom操作結束,再執行。

MutationEvent

MutationObserver是在DOM4規範中定義的,它的前身是MutationEvent事件,該事件最初在DOM2事件規範中介紹,到來了DOM3事件規範中正式定義,但是由於該事件存在相容性以及效能上的問題被棄用。

MutationEvent總共有7種事件:DOMNodeInserted、DOMNodeRemoved、DOMSubtreeModified、DOMAttrModified、DOMCharacterDataModified、DOMNodeInsertedIntoDocument、DOMNodeRemovedFromDocument

MutationEvent的相容性:
  1. MutationEvent在IE瀏覽器中最低支援到IE9;
  2. 在webkit核心的瀏覽器中,不支援 DOMAttrModified 事件;
  3. IE,Edge以及Firefox瀏覽器下不支援 DOMNodeInsertedIntoDocument 和 DOMNodeRemovedFromDocument事件;
缺點:

1,MutationEvent中的所有事件都被設計成無法取消,如果可以取消MutationEvent事件則會導致現有的DOM介面無法對文件進行改變,比如appendChild,remove等新增和刪除節點的DOM操作。
2,document下的所有DOM新增操作都會觸發DOMNodeInserted方法,這時就會出現迴圈呼叫DOMNodeInserted方法,導致瀏覽器崩潰
3,還有就是MutationEvent是事件機制,因此會有一般事件都存在的捕獲和冒泡階段,此時如果在捕獲和冒泡階段又對DOM進行了操作會拖慢瀏覽器的執行。
4,MutationEvent事件機制是同步的,也就是說每次DOM修改就會觸發,修改幾次就觸發幾次,嚴重降低瀏覽器的執行,嚴重時甚至導致執行緒崩潰

目的:

應付 DOM 頻繁的變動

特點:

  • 非同步執行,它等待所有指令碼任務完成後,才會執行;
  • 它把 DOM 變動記錄封裝成一個數組進行處理,而不是一條條個別處理 DOM 變動;
  • 它既可以觀察 DOM 的所有型別變動,也可以指定只觀察某一類變動;

observe(DOM, options)

1, DOM 為要監聽的節點
2, options 可以配置要監聽的具體內容
3, 可以呼叫多次 observe ,同時監聽多個 DOM 的變動。

{ 
 attributeFilter: ['data-name'], // 要監聽的特定屬性陣列
attributeOldValue: true, // MutationRecords 中是否記錄屬性變動的舊值
 attributes: true, // 是否監聽屬性變化
characterData: true, // 是否監聽字元變化
characterDataOldValue: true, // MutationRecords 中是否記錄字串變動的舊值
childList: true, // 是否監聽子位元組點的新增刪除
subtree: true, // 監聽範圍擴充套件為節點樹中所有的節點,否則只監聽子節點的變動
}

disconnct()

取消當前的監聽,取消之後又可以通過 observe 再次監聽

takeRecords()

callback 的觸發是非同步的, 可能出現雖然監測到了變動記錄,但是還沒有呼叫 callback 處理, takeRecords 可以取出當前所有未處理的 MutationRcords 。一般情況用在要呼叫 disconnect 之前,一次取出所有的記錄,並且處理。

MutationRecord

MutationRecord 是一次變動的記錄;

MutationRecord {
	 type, // 變動型別,值有 attributes(屬性變動),characterData(字元變動),childList(子節點刪除,新增)
	 target, // 變動影響的 Node, 如果 type = attributes, 則是 屬性變動的 Node 
            // 如果 type = characterData, 則是 字元修改的 TextNode
            // 如果 type = childList, 則是 父節點
	 addedNodes, // 增加的節點,如果沒有則為空陣列
	 removedNodes, // 刪除的節點,如果沒有則為空陣列
	 previousSibling, // 刪除/增加節點的 前一個節點
	 nextSibling, // 刪除/增加節點的 前一個節點
	 attributeName, // 變動的屬性名
	 attributeNamespace, // 變動屬性的namespace
	oldValue, // 變動的老的值  如果 type = attributes, 則是 屬性修改前的值
            // 如果 type = characterData, 則是 字元修改前的值
            // 如果 type = childList, 則是 null            
}

優點

很方便的監聽節點所有或特定的變化

缺點

回撥的觸發時非同步的。click 等事件觸發是同步的。

應用

實際場景:使用vant 中fileld元件的多文字框屬性,進入頁面時隱藏,點選按鈕觸發顯示,此時多行文字框高度只有一行且超出有滑條,而不會自適應高度。
解決方案:顯示多行文字框時重新整理頁面

// 觀察dom物件變動
export function observeDom( el, cb, options = {
    attributes: true,
    subtree: true,
    attributeOldValue: true
  }) {
 	 const observer = new MutationObserver(cb);
 	 observer.observe(el, options);
}
// 監控變動的dom
observeDom(this.$refs.moreDom, () => {
    	this.$refs[i].adjustSize(); // adjustSize()方法是由vant提供的自適應高度方法
}, {
     attributes: true,
      subtree: false,
      attributeOldValue: true
    });