偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一文讀懂 MutationObserver 的基本原理和應(yīng)用場(chǎng)景

開(kāi)發(fā) 前端
MutationObserver 的優(yōu)點(diǎn)在于它可以捕獲多種類型的 DOM 變化,包括元素的添加、刪除、屬性更改、文本內(nèi)容變化等,而不需要顯式地監(jiān)聽(tīng)每一種變化類型,這意味著它更靈活、更可靠,并且可以適應(yīng)各種應(yīng)用場(chǎng)景。

MutationObserver 是用于監(jiān)視 DOM 樹(shù)內(nèi)的特定節(jié)點(diǎn)的 Web API 接口,一旦監(jiān)測(cè)到節(jié)點(diǎn)發(fā)生變化,就會(huì)通知回調(diào)函數(shù)執(zhí)行相應(yīng)的邏輯。該 API 的兼容性很好,但由于如今流行的 JS 框架都旨在“數(shù)據(jù)驅(qū)動(dòng)視圖”,使得這個(gè) API 容易被大眾遺忘。

本文將介紹 MutationObserver 的基本原理、使用方法和應(yīng)用場(chǎng)景,幫助讀者更好地理解和應(yīng)用這個(gè)靈活且強(qiáng)大的 API。

前言

事情是這樣的,某天我想給文檔網(wǎng)站加個(gè)訪問(wèn)量統(tǒng)計(jì)的插件,這個(gè)插件是第三方的,工作原理是將數(shù)據(jù)填充到頁(yè)面中特定 id 的節(jié)點(diǎn)上,例如有一個(gè) <span id="pv"></span> 的節(jié)點(diǎn),插件加載完成后就會(huì)通過(guò) dqS (document.querySelect) 找到 id 為 pv 的節(jié)點(diǎn)然后把 pv 數(shù)據(jù)渲染上去。問(wèn)題就在于,我這個(gè)文檔網(wǎng)站并不是靜態(tài)的,所有工作都是在運(yùn)行時(shí)完成,類似一個(gè)用 Vue 驅(qū)動(dòng)的網(wǎng)頁(yè),一開(kāi)始只有 #app 節(jié)點(diǎn),所以這個(gè)第三方腳本不能直接放在 index.html 文檔中加載,否則可能頁(yè)面還沒(méi)渲染完,腳本就已經(jīng)開(kāi)始 dqS 了,結(jié)果肯定是沒(méi)有數(shù)據(jù)顯示。而頁(yè)面真正渲染完成并不在 DOMContentLoaded 階段,使得 defer 異步加載也失去用處。

如果你的網(wǎng)站是自己用例如 Vue 這樣的框架編寫(xiě)的,那你自然會(huì)想到在 onMounted 生命周期里加載腳本,但在這個(gè)場(chǎng)景下頁(yè)面真正渲染完成是在一個(gè)黑盒當(dāng)中,那么我要如何才能獲知這個(gè)本“不存在”的 DOM 節(jié)點(diǎn)出現(xiàn)的時(shí)機(jī)呢?

起初我想到的是一個(gè)笨拙但有用的解決方案,那就是使用定時(shí)器函數(shù),我們只需要輪詢節(jié)點(diǎn)是否存在,等到它出現(xiàn)的時(shí)候,便可以開(kāi)始加載第三方腳本:

const timer = setInterval(() => {
    if (document.getElementById('xxxx')) {
      // TODO: 此時(shí)開(kāi)始加載第三方腳本
      clearInterval(timer) // 清除定時(shí)器
    }
}, 1000)

效果如下所示:

這種方式雖然可以有效運(yùn)作,但是很明顯的缺陷是不夠靈活,定時(shí)器的間隔時(shí)間難以定義,設(shè)置長(zhǎng)了生效慢,設(shè)置短了又產(chǎn)生太多不必要的開(kāi)銷(xiāo)。

有沒(méi)有什么方法可以避免無(wú)意義的輪詢,又能在渲染完成第一時(shí)間加載腳本呢?這就要提到 MutationObserver 這個(gè)瀏覽器 API 了。

變動(dòng)觀察器

MutationObserver 是Web API 中的一個(gè)接口,用于監(jiān)測(cè) DOM 樹(shù)中的變化。

它可以觀察特定節(jié)點(diǎn)或其子節(jié)點(diǎn)的任何更改,例如添加、刪除或修改子節(jié)點(diǎn)、屬性變化、文本變化等等。

當(dāng) MutationObserver 綁定到一個(gè)節(jié)點(diǎn)上時(shí),它會(huì)創(chuàng)建一個(gè)觀察器實(shí)例,該實(shí)例會(huì)監(jiān)聽(tīng)其綁定的節(jié)點(diǎn)及其子節(jié)點(diǎn)的變化,并在發(fā)生變化時(shí)觸發(fā)一個(gè)回調(diào)函數(shù)。

這個(gè) API 的使用非常簡(jiǎn)單,我們以上面的場(chǎng)景為例,只需要監(jiān)聽(tīng)文檔樹(shù)的根節(jié)點(diǎn),然后在其子節(jié)點(diǎn)每次發(fā)生變化時(shí)進(jìn)行 dqS 就可以了,代碼如下:

// 選擇一個(gè)要監(jiān)聽(tīng)的節(jié)點(diǎn)
const targetNode = document.body

// 創(chuàng)建一個(gè)新的 MutationObserver
const observer = new MutationObserver(() => {
  if (document.getElementById('xxx')) {
    // TODO: 此時(shí)開(kāi)始加載第三方腳本
    observer.disconnect(); // 銷(xiāo)毀監(jiān)視者
  }
})

const config = { childList: true, subtree: true } // 對(duì)哪些更改做出反應(yīng)

// 綁定目標(biāo)節(jié)點(diǎn)并啟動(dòng)監(jiān)視者
observer.observe(targetNode, config)

在完成對(duì)應(yīng)邏輯后應(yīng)該及時(shí)調(diào)用 observer.disconnect() 銷(xiāo)毀監(jiān)視者,否則第三方腳本里如果也操作了 DOM 就會(huì)不斷遞歸。

在上面代碼的回調(diào)函數(shù)中打印 dqS 信息,這里前三次 DOM 發(fā)生變動(dòng)時(shí)特定節(jié)點(diǎn)還不存在,直到第四次變動(dòng)出現(xiàn)了特定節(jié)點(diǎn),于是加載第三方腳本,渲染數(shù)據(jù),并關(guān)閉監(jiān)視者。

config 對(duì)象有如下這些值,這些布爾選項(xiàng)表示會(huì)“對(duì)哪些更改做出反應(yīng)”:

  • ? childList:監(jiān)聽(tīng)子節(jié)點(diǎn)變動(dòng)
  • ? subtree:監(jiān)聽(tīng)所有后代節(jié)點(diǎn)的變動(dòng)
  • ? attributes:監(jiān)聽(tīng)節(jié)點(diǎn)的特性變化
  • ? attributeFilter:特性名稱數(shù)組,只觀察選定的特性
  • ? characterData:是否觀察文本內(nèi)容
  • ? attributeOldValue:是否將特性的舊值和新值都傳遞給回調(diào)
  • ? characterDataOldValue:是否將 node.data 的舊值和新值都傳遞給回調(diào)

應(yīng)用場(chǎng)景

除了上文的第三方腳本場(chǎng)景,還有哪些場(chǎng)景可以使用呢?

編輯器自動(dòng)保存

當(dāng)我們給一個(gè)普通的 div 添加 contentEditable 屬性時(shí),它便具有了可編輯的能力,這時(shí)我們可以通過(guò) MutationObserver 來(lái)監(jiān)聽(tīng)文本內(nèi)容的變動(dòng),并執(zhí)行某些邏輯,例如在發(fā)生改動(dòng)時(shí)觸發(fā)自動(dòng)保存等。

// 使用以下簡(jiǎn)單的代碼片段,修改文本并觀察控制臺(tái)的輸出:
<div contentEditable id="editor">
  點(diǎn)擊開(kāi)始 <b>編輯文本</b>。  
</div>

<style>
  #editor {
    height: 200px;
    padding: 1rem;
  }
</style>

<script>
const observer = new MutationObserver(mutationRecords => {
  for (mutation of mutationRecords) {
    console.log(mutation.type); // 輸出屬性變化的類型 
    console.log('oldValue: '+mutation.oldValue)
    console.log(mutation.target.data) // 文本變動(dòng)
  }
});

// 觀察除了特性之外的所有變動(dòng)
observer.observe(editor, {
  childList: true, // 觀察直接子節(jié)點(diǎn)
  subtree: true, // 及其更低的后代節(jié)點(diǎn)
  characterData: true,
  characterDataOldValue: true // 將舊的數(shù)據(jù)傳遞給回調(diào)
});  
</script>

MutationRecord 對(duì)象有如下一些屬性:

  • ? type:變動(dòng)類型,attributes:特性被修改了,characterData:數(shù)據(jù)被修改了(文本),childList:添加/刪除了子元素
  • ? target:更改發(fā)生在何處
  • ? addedNodes / removedNodes:添加/刪除的節(jié)點(diǎn),數(shù)組格式
  • ? previousSibling / nextSibling:添加/刪除的節(jié)點(diǎn)的上一個(gè)/下一個(gè)兄弟節(jié)點(diǎn)
  • ? attributeName / attributeNamespace:被更改的特性的名稱/命名空間(XML)
  • ? oldValue:修改前的值,僅適用于特性或文本更改(需設(shè)置相應(yīng)選項(xiàng) attributeOldValue / characterDataOldValue)

Div 水印

在上面的文本編輯器例子中,除了在文本框修改會(huì)觸發(fā)監(jiān)聽(tīng)回調(diào),打開(kāi)控制臺(tái)在文檔樹(shù)中直接修改也能觸發(fā)回調(diào):

這就給我們提供了一種保護(hù) DOM 結(jié)構(gòu)的思路:例如在頁(yè)面中打水印的場(chǎng)景,只需要用最簡(jiǎn)單的 div 覆蓋最上層實(shí)現(xiàn),然后監(jiān)聽(tīng)這些水印節(jié)點(diǎn),無(wú)論水印被何種方式刪除,都可以監(jiān)聽(tīng)到然后把它還原回去~

同理,如果頁(yè)面中插入第三方廣告,也可以用來(lái)檢查廣告是否被屏蔽等。

總結(jié)

MutationObserver 的優(yōu)點(diǎn)在于它可以捕獲多種類型的 DOM 變化,包括元素的添加、刪除、屬性更改、文本內(nèi)容變化等,而不需要顯式地監(jiān)聽(tīng)每一種變化類型,這意味著它更靈活、更可靠,并且可以適應(yīng)各種應(yīng)用場(chǎng)景。

從架構(gòu)的角度上來(lái)看,MutationObserver 可以構(gòu)建更高效、更靈活的代碼,因?yàn)樗显O(shè)計(jì)模式中最基本的“開(kāi)閉原則”,即對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。該原則提倡當(dāng)需要添加新的功能時(shí),不應(yīng)修改已有的代碼,而是應(yīng)該通過(guò)擴(kuò)展已有的代碼來(lái)實(shí)現(xiàn)新的功能。當(dāng)已存在的代碼成為黑盒時(shí),有效地監(jiān)聽(tīng) DOM 變化并做出相應(yīng)的擴(kuò)展邏輯,可以更優(yōu)雅地完成需求。

以上就是文章的全部?jī)?nèi)容,感謝看到這里!本人知識(shí)水平有限,如有錯(cuò)誤望不吝指正,如果覺(jué)得寫(xiě)得不錯(cuò),對(duì)你有所幫助或啟發(fā),可以點(diǎn)贊收藏支持一下,也歡迎關(guān)注,我會(huì)更新更多實(shí)用的前端知識(shí)與技巧。我是茶無(wú)味de一天(公眾號(hào):品味前端),希望與你共同成長(zhǎng)~

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2021-04-27 19:21:48

HBase原理開(kāi)源

2020-12-29 07:32:59

Redis 列表數(shù)據(jù)

2019-09-12 09:56:33

TCPUDPHTTP

2024-02-23 10:10:00

List接口Java

2017-09-11 17:16:35

2013-04-07 14:09:55

Android應(yīng)用基本

2012-01-12 14:37:34

jQuery

2016-08-17 23:53:29

網(wǎng)絡(luò)爬蟲(chóng)抓取系統(tǒng)

2020-05-20 22:28:10

JVM運(yùn)行機(jī)制

2010-06-18 17:28:37

Linux Anacr

2010-08-20 13:29:33

OFDM

2020-03-21 14:57:14

手機(jī)定位智能手機(jī)APP

2009-02-24 09:43:00

IP電話原理

2013-07-05 14:41:27

Android

2011-11-29 12:17:00

2022-10-12 07:24:18

大文件哈希算法Hash

2022-05-12 10:53:42

keepalivevrrp協(xié)議

2021-12-16 14:45:09

https架構(gòu)服務(wù)端

2010-09-15 15:48:09

CSS Hack

2020-08-20 07:41:52

Git原理版本
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)