誰動了個人 DOM??!

元素的樣式爲什麼頻頻被改?前端

DOM 的屬性爲什麼離奇失蹤??數組

消失的 DOM 到底是何人所爲???瀏覽器

出現的陌生 DOM 到底是人是鬼????微信

這一切的背後是人性的扭曲仍是道德的淪喪?????異步

是無心寫的 Bug 仍是有人故意而爲之??????函數

讓咱們跟隨鏡頭,探尋變幻無窮的 DOM。工具

起源

最近在作項目的時候,遇到一個問題:組件化

就是一個好久之前寫的頁面,裏面的代碼很亂。而個人任務是將頁面的高度與屏幕適配。在部分頁面存在着幾個 <iframe>,我須要調整樣式使其高度與屏幕適配,可是不管我怎麼調整,總會有一個 JavaScript 在不停地修改 <iframe> 的高度,使得它的高度超出屏幕而出現兩個滾動條。性能

因爲代碼很亂,因此很難找到到底是哪一個代碼在搗亂。spa

Chrome 中的解決方法

在 Chrome 中開發前端是很是開心的一件事情,由於瀏覽器提供了很是多的調試工具。而我常常用的一個就是:斷點調試。

Chrome 提供了一個 DOM 監視的功能,當 DOM 發生變化的時候,自動暫停,這樣就能很快定位是誰修改了 <iframe> 的高度了!

在 Elements 頁面,選中指定的 DOM 節點,點擊最前面的「...」符號(或者使用右鍵點擊),在彈出的菜單中選擇「Break On」便可,能夠選擇多個。

  • Subtree Modifications: 當節點樹發生變化時
  • Attribute Modifications: 當節點屬性發生變化時
  • Node Removal: 當節點被刪除時(包括下級節點)

只要啓用其中一個,在具體事件發生的時候,Chrome 就會自動中斷到當前執行的腳本代碼處。

因爲 JavaScript 修改樣式無非就是增長/刪除 class,或是修改 style 屬性,因此,使用 Attribute Modifications 便可找到具體是哪一個腳本在修改 DOM 了。

新的問題

在作前端開發的時候,常常會想要監聽 DOM 節點的變化。當 DOM 變化的時候,觸發一系列事件。

通常來講,使用輪詢的方式能夠很是簡單地解決這個問題,就是使用 setInterval 來不斷地檢查 DOM 是否發生了變化。這種方式簡單粗暴,可是會遇到兩個問題:時間間隔設置過長,DOM 變化響應不夠及時;時間間隔設置太短,不只浪費 CPU,並且可能出現卡頓。

固然,也可使用 requestAnimationFrame 來作,原理其實和 setInterval 同樣,只不過在必定程度上能夠獲得 DOM 變化實時響應,可是依舊是致使 CPU 運行時間片的浪費。

有沒有更好的辦法呢?

在舊版 DOM Events 標準中,有一個 Mutation events,能夠用來監聽 DOM 的變化,在 DOM 變化的時候觸發事件。

在 DOM3 中定義了 9 種 Mutation 事件:DOMAttrModifiedDOMAttributeNameChangedDOMCharacterDataModifiedDOMElementNameChangedDOMNodeInsertedDOMNodeInsertedIntoDocumentDOMNodeRemovedDOMNodeRemovedFromDocumentDOMSubtreeModified

這 9 種事件能夠直接經過 element.addEventListener 添加到 DOM 元素上。

可是,Mutation 事件已經被反對使用!而且從 Web 標準事件中刪除了!

因爲性能問題,Mutation 事件會致使 DOM 修改的性能下降 1.5~7 倍,而且不能經過移除事件來恢復性能。

而且這個事件在各個瀏覽器上的實現也存在差別。

因此,DOM4 開始,推薦使用 Mutation Observers 來代替 Mutation events

Mutation Observers

Mutation Observer API 能夠用來監視 DOM 的變化,包括屬性的變化、節點的增減、內容的變化等。

比 Mutation Events 高在哪裏?

爲何 Mutation Observers 要比 Mutation Events 好?

因爲 Mutation Events 是監視到 DOM 發生變化時產生的事件,它會在任何一個 DOM 發生變化的時候馬上被觸發。而且,因爲事件是同步進行的,因此若是 DOM 的變化較多,就會產生大量的事件回調,致使嚴重的性能問題。

Mutation Observers 雖然和 Mutation Events 很像,可是 Mutation Observers 不是事件,它是異步觸發的,而且不是每次 DOM 變更都會觸發,而是會等待屢次 DOM 變更完成後一次性觸發,使用一個數組來記錄 DOM 變更的步驟。這樣一來,即便是頻繁的 DOM 操做,對性能的影響也不會有多明顯。

舉個例子,我如今須要將一篇包含 1000 個段落的文章顯示到頁面上,也就是要往頁面中插入 1000 個 <p></p>

若是使用 Mutation Events 的話,這時就會產生 1000 個 DOMNodeInserted 事件;而若是使用 Mutation Observers 就不同了,它只會觸發一次,獲得一個數組,包含了 1000 個插入節點的信息。

怎麼用呢?

MutationObserver 是一個構造函數,可使用 new 來建立一個 MutationObserver 的實例。這個構造函數接受一個回調函數做爲參數,也就是每次 Mutation Observers 觸發時調用的函數,函數接受兩個參數,第一個參數是 MutationRecord 數組,用於存儲 DOM 的變化記錄,第二個參數是 MutationObserver 實例自己。

MutationObserver 的實例有 3 個成員方法:observedisconnecttakeRecords

observe 用於註冊監聽器,接受兩個參數,第一個參數是要監聽的節點,第二個參數是監聽的配置。

監聽配置是一個對象,能夠有 childListattributescharacterDatasubtreeattributeOldValuecharacterDataOldValueattributeFilter,要監聽哪一種變化,只須要將對應的屬性設置爲 true 便可,其中 childListattributescharacterData 三者必須至少出現一個。

屬性 數據類型 描述
childList boolean 觀察目標增長或移除了子節點
attributes boolean 觀察目標增長、刪除或修改了某個屬性
characterData boolean (目標爲 characterData 節點時有效,包括文本節點、註釋節點、處理指令節點等)文本內容發生了變化
subtree boolean 不只監視 ovserve 第一個參數指定的觀察目標,同時監視全部的下級節點
attributeOldValue boolean 在監視 attributes 的時候,屬性發生變化後是否要記錄變化前的內容
characterDataOldValue boolean 在監視 characterData 的時候,文本內容發生變化後是否要記錄變化前的內容
attributeFilter Array<string> 一個屬性名數組,能夠用於過濾 attributes 的變化

註冊成功後,構造函數裏提供的回調函數將會被調用,第一個參數就獲得了變化數組。變化對象的結構包含如下屬性:

屬性 數據類型 描述
type String 變化類型,對應監聽配置對象中的 childListattributescharacterData
target Node 變化的目標節點,若是 typeattributescharacterData,則 target 爲變化節點,不然爲變化節點的父節點
addedNodes NodeList 被添加的節點列表(可能爲 null
removedNodes NodeList 被刪除的節點列表(可能爲 null
previousSibling Node 被添加或被刪除的節點的前一個兄弟節點(可能爲 null
nextSibling Node 被添加或被刪除的節點的後一個兄弟節點(可能爲 null
attributeName String 變化的屬性名稱(可能爲 null
attributeNamespace String 變化的屬性所在的 XML 命名空間(可能爲 null
oldValue String 若是 typeattributescharacterData,則 oldValue 爲變化前的值,不然爲 null

disconnect 用於中止監聽。

takeRecords 用於清空並返回當前 MutationObserver 記錄的 DOM 變化步驟。

瀏覽器兼容性

能夠看到,兼容性仍是很是好的,能夠放心使用。

總結

MutationObserver 提供了比 Mutation Events 更高效、更靈活的 DOM 監視方案,能夠根據本身的須要自定義監視對象,在組件化項目中能夠發揮更大的價值——不須要組件內部提供接口,就能夠收到組件內容變化的通知。

可是,MutationObserver 雖好,可不要濫用哦!

Chrome 提供的 Break On 功能看起來就像是 MutationObserver 的精簡版,很是實用。


關注微信公衆號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!

相關文章
相關標籤/搜索