摘要:是用于代替作為觀察樹結(jié)構(gòu)發(fā)生變化時(shí),做出相應(yīng)處理的。觸發(fā)回調(diào)前返回最新的批量變化。發(fā)生相應(yīng)變動(dòng)時(shí),不再調(diào)用回調(diào)函數(shù)。其中數(shù)組也會(huì)作為,觀察者初始化時(shí)的回調(diào)函數(shù)的第一個(gè)參數(shù)。如果為,則表示需要記錄變動(dòng)前的屬性值。
MutationObserver 是用于代替 MutationEvents 作為觀察 DOM 樹結(jié)構(gòu)發(fā)生變化時(shí),做出相應(yīng)處理的 API 。為什么要使用 MutationObserver 去代替 MutationEvents 呢,我們先了解一下 MutationEvents
MutationEvents它簡(jiǎn)單的用法如下:
document.getElementById("list").addEventListener( "DOMSubtreeModified", () => { console.log("列表中子元素被修改") }, false )
// Mutation 事件列表 DOMAttrModified // 監(jiān)聽元素的修改 DOMAttributeNameChanged DOMCharacterDataModified DOMElementNameChanged DOMNodeInserted // 監(jiān)聽新增 DOMNodeRemoved // 監(jiān)聽刪除 DOMNodeInsertedIntoDocument DOMSubtreeModified // 監(jiān)聽子元素的修改
其中 DOMNodeRemoved ,DOMNodeInserted 和 DOMSubtreeModified分別用于監(jiān)聽元素子項(xiàng)的刪除,新增,修改(包括刪除和新增),DOMAttrModified 是監(jiān)聽元素屬性的修改,并且能夠提供具體的修改動(dòng)作。
Mutation Events 遇到的問題
IE9 不支持 MutationEvents。Webkit 內(nèi)核不支持 DOMAttrModified 特性,DOMElementNameChanged 和 DOMAttributeNameChanged 在 Firefox 上不被支持。
性能問題 1. MutationEvents 是同步執(zhí)行的,它的每次調(diào)用,都需要從事件隊(duì)列中取出事件,執(zhí)行,然后事件隊(duì)列中移除,期間需要移動(dòng)隊(duì)列元素。如果事件觸發(fā)的較為頻繁的話,每一次都需要執(zhí)行上面的這些步驟,那么瀏覽器會(huì)被拖慢。 2. MutationEvents 本身是事件,所以捕獲是采用的是事件冒泡的形式,如果冒泡捕獲期間又觸發(fā)了其他的 MutationEvents 的話,很有可能就會(huì)導(dǎo)致阻塞 Javascript 線程,甚至導(dǎo)致瀏覽器崩潰。
Mutation ObserverMutationObserver 是在 DOM4 中定義的,用于替代 MutationEvents 的新 API,它的不同于 events 的是,所有監(jiān)聽操作以及相應(yīng)處理都是在其他腳本執(zhí)行完成之后異步執(zhí)行的,并且是所以變動(dòng)觸發(fā)之后,將變得記錄在數(shù)組中,統(tǒng)一進(jìn)行回調(diào)的,也就是說,當(dāng)你使用 observer 監(jiān)聽多個(gè) DOM 變化時(shí),并且這若干個(gè) DOM 發(fā)生了變化,那么 observer 會(huì)將變化記錄到變化數(shù)組中,等待一起都結(jié)束了,然后一次性的從變化數(shù)組中執(zhí)行其對(duì)應(yīng)的回調(diào)函數(shù)。
特點(diǎn)
所有腳本任務(wù)完成后,才會(huì)運(yùn)行,即采用異步方式
DOM 變動(dòng)記錄封裝成一個(gè)數(shù)組進(jìn)行處理,而不是一條條地個(gè)別處理 DOM 變動(dòng)。
可以觀察發(fā)生在 DOM 節(jié)點(diǎn)的所有變動(dòng),也可以觀察某一類變動(dòng)
目前,F(xiàn)irefox(14+)、Chrome(26+)、Opera(15+)、IE(11+) 和 Safari(6.1+) 支持這個(gè) API。 Safari 6.0 和 Chrome 18-25 使用這個(gè) API 的時(shí)候,需要加上 WebKit 前綴(WebKitMutationObserver)??梢允褂孟旅娴谋磉_(dá)式檢查瀏覽器是否支持這個(gè) API。
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver // 監(jiān)測(cè)瀏覽器是否支持 const observeMutationSupport = !!MutationObserver如何使用 MutationObserver
在應(yīng)用中集成 MutationObserver 是相當(dāng)簡(jiǎn)單的。通過往構(gòu)造函數(shù) MutationObserver 中傳入一個(gè)函數(shù)作為參數(shù)來初始化一個(gè) MutationObserver 實(shí)例,該函數(shù)會(huì)在每次發(fā)生 DOM 發(fā)生變化的時(shí)候調(diào)用。MutationObserver 的函數(shù)的第一個(gè)參數(shù)即為單個(gè)批處理中的 DOM 變化集。每個(gè)變化包含了變化的類型和所發(fā)生的更改。
const mutationObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { console.log(mutation) }) })
創(chuàng)建的實(shí)例對(duì)象擁有三個(gè)方法:
observe -開始進(jìn)行監(jiān)聽。接收兩個(gè)參數(shù)-要觀察的 DOM 節(jié)點(diǎn)以及一個(gè)配置對(duì)象。
disconnect -停止監(jiān)聽變化。
takeRecords -觸發(fā)回調(diào)前返回最新的批量 DOM 變化。
observer 方法observer 方法指定所要觀察的 DOM 元素,以及要觀察的特定變動(dòng)。
const article = document.querySelector("article") observer.observer(article, { childList: true, arrtibutes: true })
上面代碼分析:
指定所要觀察的 DOM 元素 article
指定所要觀察的變動(dòng)是子元素的變動(dòng)和屬性變動(dòng)。
將這兩個(gè)限定條件作為參數(shù),傳入observer 對(duì)象 observer方法。
disconnect 方法disconnect 方法用來停止觀察。發(fā)生相應(yīng)變動(dòng)時(shí),不再調(diào)用回調(diào)函數(shù)。
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver // 選擇目標(biāo)節(jié)點(diǎn) const target = document.querySelector("#some-id") // 創(chuàng)建觀察者對(duì)象 const observer = new MutationObserver(mutation => { mutations.forEach(function(mutation) { console.log(mutation.type) }) }) // 配置觀察選項(xiàng): const config = { attributes: true, childList: true, characterData: true } // 傳入目標(biāo)節(jié)點(diǎn)和觀察選項(xiàng) observer.observe(target, config) // 隨后,你還可以停止觀察 observer.disconnect()takeRecord 方法
takeRecord 方法用來清除變動(dòng)記錄,即不再處理未處理的變動(dòng)。
在觀察者對(duì)象上調(diào)用 takeRecords 會(huì)返回 其觀察節(jié)點(diǎn)上的變化記錄(MutationRecord)數(shù)組。其中 MutationRecord 數(shù)組也會(huì)作為,觀察者初始化時(shí)的回調(diào)函數(shù)的第一個(gè)參數(shù)。
其包含的屬性如下:
type 如果是屬性發(fā)生變化,則返回 attributes.如果是一個(gè)CharacterData 節(jié)點(diǎn)發(fā)生變化,則返回 characterData ,如果是目標(biāo)節(jié)點(diǎn)的某個(gè)子節(jié)點(diǎn)發(fā)生了變化,則返回 childList .
target 返回此次變化影響到的節(jié)點(diǎn),具體返回那種節(jié)點(diǎn)類型是根據(jù) type 值的不同而不同的,如果 type 為 attributes ,則返回發(fā)生變化的屬性節(jié)點(diǎn)所在的元素節(jié)點(diǎn),如果 type 值為 characterData ,則返回發(fā)生變化的這個(gè) characterData 節(jié)點(diǎn).如果 type 為 childList ,則返回發(fā)生變化的子節(jié)點(diǎn)的父節(jié)點(diǎn).
addedNodes 返回被添加的節(jié)點(diǎn)
removedNodes 返回被刪除的節(jié)點(diǎn)
previousSibling 返回被添加或被刪除的節(jié)點(diǎn)的前一個(gè)兄弟節(jié)點(diǎn)
nextSibling 返回被添加或被刪除的節(jié)點(diǎn)的后一個(gè)兄弟節(jié)點(diǎn)
attributeName 返回變更屬性的本地名稱
oldValue 根據(jù) type 值的不同,返回的值也會(huì)不同.如果 type 為 attributes,則返回該屬性變化之前的屬性值.如果 type 為 characterData,則返回該節(jié)點(diǎn)變化之前的文本數(shù)據(jù).如果 type 為 childList,則返回 null
observer.takeRecord()MutationObserver 類型
MutationObserver 所觀察的 DOM 變動(dòng)(即上面代碼的 option 對(duì)象),包含以下類型:
childList:子元素的變動(dòng)
attributes:屬性的變動(dòng)
characterData:節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動(dòng)
subtree:所有下屬節(jié)點(diǎn)(包括子節(jié)點(diǎn)和子節(jié)點(diǎn)的子節(jié)點(diǎn))的變動(dòng)
想要觀察哪一種變動(dòng)類型,就在 option 對(duì)象中指定它的值為 true。
需要注意的是,不能多帶帶觀察 subtree 變動(dòng),必須同時(shí)指定 childList、attributes 和 characterData 中的一種或多種。
除了變動(dòng)類型,option 對(duì)象還可以設(shè)定以下屬性:
attributeOldValue:值為 true 或者為 false。如果為 true,則表示需要記錄變動(dòng)前的屬性值。
characterDataOldValue:值為 true 或者為 false。如果為 true,則表示需要記錄變動(dòng)前的數(shù)據(jù)值。
attributesFilter:值為一個(gè)數(shù)組,表示需要觀察的特定屬性(比如["class", "str"])。
創(chuàng)建 MutationObserver 并 獲取 dom 元素,定義回調(diào)數(shù)據(jù)。
// 獲取MutationObserver,兼容低版本的瀏覽器 const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver // 獲取dom元素 const list = document.querySelector("ol") // 創(chuàng)建Observer const Observer = new MutationObserver((mutations, instance) => { console.log(mutations) console.log(instance) mutations.forEach(mutation => { console.log(mutation) }) })
子元素的變動(dòng)
Observer.observe(list, { childList: true, subtree: true }) // 追加div標(biāo)簽 list.appendChild(document.createElement("div")) // 追加文本 list.appendChild(document.createTextNode("foo")) // 移除第一個(gè)節(jié)點(diǎn) list.removeChild(list.childNodes[0]) // 子節(jié)點(diǎn)移除創(chuàng)建的div list.childNodes[0].appendChild(document.createElement("div"))
監(jiān)測(cè) characterData 的變動(dòng)
Observer.observe(list, { childList: true, characterData: true, subtree: true }) // 將第一個(gè)子節(jié)點(diǎn)的數(shù)據(jù)改為cha list.childNodes[0].data = "cha"
監(jiān)測(cè)屬性的變動(dòng)
Observer.observe(list, { attributes: true }) // 設(shè)置節(jié)點(diǎn)的屬性 會(huì)觸發(fā)回調(diào)函數(shù) list.setAttribute("data-value", "111") // 重新設(shè)置屬性 會(huì)觸發(fā)回調(diào) list.setAttribute("data-value", "2222") // 刪除屬性 也會(huì)觸發(fā)回調(diào) list.removeAttribute("data-value")
屬性變動(dòng)前,記錄變動(dòng)之前的值
Observer.observe(list, { attributes: true, attributeOldValue: true }) // 設(shè)置節(jié)點(diǎn)的屬性 會(huì)觸發(fā)回調(diào)函數(shù) list.setAttribute("data-value", "111") // 刪除屬性 list.setAttribute("data-value", "2222")
characterData 變動(dòng)時(shí),記錄變動(dòng)前的值。
Observer.observe(list, { childList: true, characterData: true, subtree: true, characterDataOldValue: true }) // 設(shè)置數(shù)據(jù) 觸發(fā)回調(diào) list.childNodes[0].data = "aaa" // 重新設(shè)置數(shù)據(jù) 重新觸發(fā)回調(diào) list.childNodes[0].data = "bbbb"
attributeFilter {Array} 表示需要觀察的特定屬性 比如 ["class", "src"];
Observer.observe(list, { attributes: true, attributeFilter: ["data-value"] }) // 第一次設(shè)置屬性 data-key 不會(huì)觸發(fā)的,因?yàn)閐ata-value 不存在 list.setAttribute("data-key", 1) // 第二次會(huì)觸發(fā) list.setAttribute("data-value", 1)案例分析—demo 編輯器
下面我們做一個(gè)簡(jiǎn)單的 demo 編輯器:
首先給父級(jí)元素 ol 設(shè)置 contenteditable 讓容器可編輯;
然后構(gòu)造一個(gè) observer 監(jiān)聽子元素的變化;
每次回車的時(shí)候,控制臺(tái)輸出它的內(nèi)容;
- 111111
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver const list = document.querySelector("ol") const Observer = new MutationObserver((mutations, instance) => { mutations.forEach(mutation => { if (mutation.type === "childList") { const list_values = [].slice .call(list.children) .map(node => node.innerHTML) .filter(s => s !== "
") console.log(list_values) } }) }) Observer.observe(list, { childList: true })
現(xiàn)在我們繼續(xù)可以做一個(gè)類似于 input 和 textarea 中的 valueChange 的事件一樣的,監(jiān)聽值變化,之前的值和之后的值,如下代碼:
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver const list = document.querySelector("ol") const Observer = new MutationObserver((mutations, instance) => { mutations.forEach(mutation => { const enter = { mutation: mutation, el: mutation.target, newValue: mutation.target.textContent, oldValue: mutation.oldValue } console.log(enter) }) }) Observer.observe(list, { childList: true, attributes: true, characterData: true, subtree: true, characterDataOldValue: true })
注意: 對(duì) input 和 textarea 不起作用的。案例分析—編輯器統(tǒng)計(jì)字?jǐn)?shù)
還可以輸入100字
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver const editor = document.querySelector("#editor") const textInput = document.querySelector("#textInput") const observer = new MutationObserver(mutations => { mutations.forEach(function(mutation) { if (mutation.type === "characterData") { const newValue = mutation.target.textContent textInput.innerHTML = `還可以輸入${1000 - newValue.length}字` } }) }) observer.observe(editor, { childList: true, attributes: true, characterData: true, subtree: true, characterDataOldValue: true })
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100827.html
摘要:倡導(dǎo)開發(fā)者盡量不直接操作,但有的時(shí)候由于各種需求讓開發(fā)者不得不這樣做,于是的實(shí)現(xiàn)就是讓開發(fā)者在修改數(shù)據(jù)后,能夠在數(shù)據(jù)更新到后才執(zhí)行對(duì)應(yīng)的函數(shù),從而獲取最新的數(shù)據(jù)。 Vue 倡導(dǎo)開發(fā)者盡量不直接操作 DOM,但有的時(shí)候由于各種需求讓開發(fā)者不得不這樣做,于是 nextTick 的實(shí)現(xiàn)就是讓開發(fā)者在修改數(shù)據(jù)后,能夠在數(shù)據(jù)更新到 DOM 后才執(zhí)行對(duì)應(yīng)的函數(shù),從而獲取最新的 DON 數(shù)據(jù)。 原文...
摘要:概述是現(xiàn)代瀏覽器提供的用來檢測(cè)變化的網(wǎng)頁(yè)接口。比如通知用戶當(dāng)前所在的頁(yè)面所發(fā)生的一些變化。觸發(fā)回調(diào)前返回最新的批量變化。在函數(shù)內(nèi)部,開始必須使用代碼進(jìn)行檢查,確保是我們所監(jiān)聽的動(dòng)畫。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第十章。 網(wǎng)絡(luò)應(yīng)用...
摘要:概述是現(xiàn)代瀏覽器提供的用來檢測(cè)變化的網(wǎng)頁(yè)接口。比如通知用戶當(dāng)前所在的頁(yè)面所發(fā)生的一些變化。觸發(fā)回調(diào)前返回最新的批量變化。在函數(shù)內(nèi)部,開始必須使用代碼進(jìn)行檢查,確保是我們所監(jiān)聽的動(dòng)畫。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第十章。 網(wǎng)絡(luò)應(yīng)用...
摘要:概述是現(xiàn)代瀏覽器提供的用來檢測(cè)變化的網(wǎng)頁(yè)接口。比如通知用戶當(dāng)前所在的頁(yè)面所發(fā)生的一些變化。觸發(fā)回調(diào)前返回最新的批量變化。在函數(shù)內(nèi)部,開始必須使用代碼進(jìn)行檢查,確保是我們所監(jiān)聽的動(dòng)畫。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第十章。 網(wǎng)絡(luò)應(yīng)用...
摘要:哈哈哈哈,以上純屬虛構(gòu),不過在最近項(xiàng)目中還真遇到過對(duì)容器監(jiān)聽高寬變化在使用或滾動(dòng)插件,如果容器內(nèi)部元素有高度變化要去及時(shí)更新外部包裹容器,即調(diào)用方法。處理很簡(jiǎn)單,只需在動(dòng)畫停止事件觸發(fā)時(shí)監(jiān)聽高寬變化即可。 前言 老鳥:怎樣去監(jiān)聽 DOM 元素的高度變化呢?菜鳥:哈哈哈哈哈,這都不知道哦,用 onresize 事件鴨!老鳥扶了扶眼睛,空氣安靜幾秒鐘,菜鳥才晃過神來。對(duì)鴨,普通 DOM 元...
閱讀 1196·2021-11-24 09:38
閱讀 2604·2021-09-27 14:00
閱讀 1165·2019-08-30 15:55
閱讀 1340·2019-08-30 14:16
閱讀 1491·2019-08-30 10:54
閱讀 2865·2019-08-28 17:58
閱讀 758·2019-08-26 13:22
閱讀 1234·2019-08-26 12:01