javascript
JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化
摘要: 掌握MutationObserver。
- 原文:JavaScript是如何工作的:使用 MutationObserver 跟蹤 DOM 的變化
- 作者:前端小智
Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。
這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第10篇。
如果你錯過了前面的章節(jié),可以在這里找到它們:
- JavaScript 是如何工作的:引擎,運行時和調(diào)用堆棧的概述!
- JavaScript 是如何工作的:深入V8引擎&編寫優(yōu)化代碼的5個技巧!
- JavaScript 是如何工作的:內(nèi)存管理+如何處理4個常見的內(nèi)存泄漏 !
- JavaScript 是如何工作的:事件循環(huán)和異步編程的崛起+ 5種使用 async/await 更好地編碼方式!
- JavaScript 是如何工作的:深入探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!
- JavaScript 是如何工作的:與 WebAssembly比較 及其使用場景 !
- JavaScript 是如何工作的:Web Workers的構(gòu)建塊+ 5個使用他們的場景!
- JavaScript 是如何工作的:Service Worker 的生命周期及使用場景!
- JavaScript 是如何工作的:Web 推送通知的機制!
Web 應用程序在客戶端變得越來越重,原因很多,例如需要更豐富的 UI 來容納更復雜的應用程序提供的內(nèi)容,實時計算等等。復雜性的增加使得在 Web 應用程序生命周期的每個給定時刻都很難知道 UI 的確切狀態(tài)。
而當你在搭建某些框架或者庫的時候,甚至會更加困難,例如,前者需要根據(jù) DOM 來作出反應并執(zhí)行特定的動作。
概述
Mutation Observer API 用來監(jiān)視 DOM 變動。DOM 的任何變動,比如節(jié)點的增減、屬性的變動、文本內(nèi)容的變動,這個 API 都可以得到通知。
概念上,它很接近事件,可以理解為 DOM 發(fā)生變動就會觸發(fā) Mutation Observer 事件。但是,它與事件有一個本質(zhì)不同:事件是同步觸發(fā),也就是說,DOM 的變動立刻會觸發(fā)相應的事件;Mutation Observer 則是異步觸發(fā),DOM 的變動并不會馬上觸發(fā),而是要等到當前所有 DOM 操作都結(jié)束才觸發(fā)。
這樣設計是為了應付 DOM 變動頻繁的特點。舉例來說,如果文檔中連續(xù)插入1000個 <li>元素,就會連續(xù)觸發(fā)1000個插入事件,執(zhí)行每個事件的回調(diào)函數(shù),這很可能造成瀏覽器的卡頓;而 Mutation Observer 完全不同,只在 1000 個段落都插入結(jié)束后才會觸發(fā),而且只觸發(fā)一次。
Mutation Observer有以下特點:
- 它等待所有腳本任務完成后,才會運行,即采用異步方式
- 它把 DOM 變動記錄封裝成一個數(shù)組進行處理,而不是一條條地個別處理 DOM 變動
- 它即可以觀察發(fā)生在 DOM 節(jié)點的所有變動,也可以觀察某一類變動
為什么要要監(jiān)聽 DOM?
在很多情況下,MutationObserver API 都可以派上用場。例如:
- 你希望通知 Web 應用程序訪問者,他當前所在的頁面發(fā)生了一些更改。
- 你正在開發(fā)一個新的 JavaScript 框架,需要根據(jù) DOM 的變化動態(tài)加載 JavaScript 模塊。
- 也許你正在開發(fā)一個所見即所得(WYSIWYG) 編輯器,試圖實現(xiàn)撤消/重做功能。通過利用 MutationObserver API,你可以知道在任何給定的點上進行了哪些更改,因此可以輕松地撤消這些更改。
這些只是 MutationObserver 可以提供幫助的幾個例子。
MutationObserver 用法
在應用程序中實現(xiàn) MutationObserver 相當簡單。你需要通過傳入一個函數(shù)來創(chuàng)建一個 MutationObserver 實例,每當有變化發(fā)生,這個函數(shù)將會被調(diào)用。函數(shù)的第一個參數(shù)是變動數(shù)組,每個變化都會提供它的類型和已經(jīng)發(fā)生的變化的信息。
var mutationObserver = new MutationObserver(function(mutations) {mutations.forEach(function(mutation) {console.log(mutation);}); });這個被創(chuàng)建的對象有三個方法:
- observe??— 啟動監(jiān)聽
- disconnect — 用來停止觀察
- takeRecords?— 返用來清除變動記錄,即不再處理未處理的變動。
observe()
observe 方法用來啟動監(jiān)聽,它接受兩個參數(shù)。
- 第一個參數(shù):所要觀察的 DOM 節(jié)點
- 第二個參數(shù):一個配置對象,指定所要觀察的特定變
下面的片段展示了如何開始啟動監(jiān)聽(observe??):
// 開始偵聽頁面的根 HTML 元素中的更改。 mutationObserver.observe(document.documentElement, {attributes: true,characterData: true,childList: true,subtree: true,attributeOldValue: true,characterDataOldValue: true });現(xiàn)在,假設 DOM 中有一些非常簡單的 div:
<div id="sample-div" class="test"> Simple div </div>使用 JQuery 來移除這個 div 上的 class:
$("#sample-div").removeAttr("class");正如我們已經(jīng)開始觀察到的,在調(diào)用 mutationObserver.observe(…) 之后,將在控制臺中看到相應 MutationRecord 的日志:
這個是由移除 class 屬性導致的變化。
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ā)生變動的屬性。如果設置了attributeFilter,則只返回預先指定的屬性。
oldValue:變動前的值。這個屬性只對 attribute 和 characterData 變動有效,如果發(fā)生 childList 變動,則返回 null
最后,為了在任務完成后停止觀察 DOM,可以執(zhí)行以下操作:
mutationObserver.disconnect();現(xiàn)在,MutationObserver 已經(jīng)被廣泛支持:
備擇方案
MutationObserver 在之前還沒有的,那么在 MutationObserver 還沒出現(xiàn)之前,開發(fā)者采用什么方案呢?
這是幾個可用的其他選項:
- 輪詢(Polling)
- MutationEvents
- CSS animations
輪詢(Polling)
最簡單和最簡單的方法是輪詢。使用瀏覽器 setInterval 方法,可以設置一個任務,定期檢查是否發(fā)生了任何更改。當然,這種方法會顯著降低web 應用程序/網(wǎng)站的性能。
MutationEvents
在2000年,MutationEvents API 被引入。雖然很有用,但在 DOM中 的每一次更改都會觸發(fā)改變事件,這同樣會導致性能問題。現(xiàn)在 MutationEvents API 已經(jīng)被棄用,很快現(xiàn)代瀏覽器將完全停止支持它。
CSS animations
另一個有點奇怪的選擇是依賴 CSS 動畫。這聽起來可能有點令人困惑。基本上,我們的想法是創(chuàng)建一個動畫,一旦元素被添加到 DOM 中,動畫就會被觸發(fā)。動畫開始的那一刻,animationstart 事件將被觸發(fā):如果已經(jīng)將事件處理程序附加到該事件,那么你將確切地知道元素何時被添加到 DOM 中。動畫的執(zhí)行時間周期應該很小,用戶幾乎看不到它。
首先,需要一個父級元素,我們在它的內(nèi)部監(jiān)聽節(jié)點的插入:
<div id=”container-element”></div>為了得到節(jié)點插入的處理器,需要設置一系列的 keyframe 動畫,當節(jié)點插入的時候,動畫將會開始。
@keyframes nodeInserted { from { opacity: 0.99; }to { opacity: 1; } }創(chuàng)建 keyfram 后,還需要把它放入你想監(jiān)聽的元素上,注意應設置很小的 duration 值 —— 它們將會減弱動畫在瀏覽器上留下的痕跡。
#container-element * {animation-duration: 0.001s;animation-name: nodeInserted; }這會將動畫添加到 container-element 的所有子節(jié)點。 動畫結(jié)束時,將觸發(fā)插入事件。
我們需要一個 JavaScript 函數(shù)作為事件監(jiān)聽器。在函數(shù)中,必須進行初始的 event.animationName 檢查以確保它是我們想要的動畫。
var insertionListener = function(event) {// Making sure that this is the animation we want.if (event.animationName === "nodeInserted") {console.log("Node has been inserted: " + event.target);} }現(xiàn)在是時候為父級元素添加事件監(jiān)聽了:
document.addEventListener(“animationstart”, insertionListener, false); // standard + firefox document.addEventListener(“MSAnimationStart”, insertionListener, false); // IE document.addEventListener(“webkitAnimationStart”, insertionListener, false); // Chrome + Safari瀏覽器對CSS動畫的支持情況:
MutationObserver 比上述解決方案有許多優(yōu)點。本質(zhì)上,它涵蓋了 DOM 中可能發(fā)生的每一個更改,并且在批量觸發(fā)更改時,它的優(yōu)化程度更高。最重要的是,所有主要的現(xiàn)代瀏覽器都支持 MutationObserver,還有一些使用引擎下 MutationEvents 的 polyfill。
原文:
https://blog.sessionstack.com...
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行l(wèi)og 調(diào)試,這邊順便給大家推薦一個好用的BUG監(jiān)控工具Fundebug。
你的點贊是我持續(xù)分享好東西的動力,歡迎點贊!
一個笨笨的碼農(nóng),我的世界只能終身學習!
更多內(nèi)容請關(guān)注公眾號《大遷世界》!
關(guān)于Fundebug
Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監(jiān)控。 自從2016年雙十一正式上線,Fundebug累計處理了9億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網(wǎng)等眾多品牌企業(yè)。歡迎大家[免費試用]
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从搭建脚手架到在npm上发布react组
- 下一篇: [Java] SpringMVC工作原理