MutationObserver æ¯ä¸ä¸ªå
建对象ï¼å®è§å¯ DOM å
ç´ ï¼å¹¶å¨æ£æµå°æ´æ¹æ¶è§¦ååè°ã
æä»¬å°é¦å çä¸ä¸è¯æ³ï¼ç¶åæ¢ç©¶ä¸ä¸ªå®é çç¨ä¾ï¼ä»¥äºè§£å®å¨ä»ä¹å°æ¹æç¨ã
è¯æ³
MutationObserver 使ç¨ç®åã
é¦å ï¼æä»¬å建ä¸ä¸ªå¸¦æåè°å½æ°çè§å¯å¨ï¼
let observer = new MutationObserver(callback);
ç¶åå°å ¶éå å°ä¸ä¸ª DOM èç¹ï¼
observer.observe(node, config);
config æ¯ä¸ä¸ªå
·æå¸å°é项ç对象ï¼è¯¥å¸å°é项表示âå°å¯¹åªäºæ´æ¹ååºååºâï¼
childListâânodeçç´æ¥åèç¹çæ´æ¹ï¼subtreeâânodeçææåä»£çæ´æ¹ï¼attributesâânodeçç¹æ§ï¼attributeï¼ï¼attributeFilterââ ç¹æ§åç§°æ°ç»ï¼åªè§å¯éå®çç¹æ§ãcharacterDataââ æ¯å¦è§å¯node.dataï¼ææ¬å 容ï¼ï¼
å ¶ä»å 个é项ï¼
attributeOldValueââ å¦æä¸ºtrueï¼åå°ç¹æ§çæ§å¼åæ°å¼é½ä¼ éç»åè°ï¼åè§ä¸æï¼ï¼å¦ååªä¼ æ°å¼ï¼éè¦attributesé项ï¼ï¼characterDataOldValueââ å¦æä¸ºtrueï¼åå°node.dataçæ§å¼åæ°å¼é½ä¼ éç»åè°ï¼åè§ä¸æï¼ï¼å¦ååªä¼ æ°å¼ï¼éè¦characterDataé项ï¼ã
ç¶åï¼å¨åç任使´æ¹åï¼å°æ§è¡âåè°âï¼æ´æ¹è¢«ä½ä¸ºä¸ä¸ª MutationRecord 对象åè¡¨ä¼ å ¥ç¬¬ä¸ä¸ªåæ°ï¼èè§å¯å¨èªèº«ä½ä¸ºç¬¬äºä¸ªåæ°ã
MutationRecord å¯¹è±¡å ·æä»¥ä¸å±æ§ï¼
typeââ åå¨ç±»åï¼ä»¥ä¸ç±»åä¹ä¸ï¼"attributes"ï¼ç¹æ§è¢«ä¿®æ¹äºï¼"characterData"ï¼æ°æ®è¢«ä¿®æ¹äºï¼ç¨äºææ¬èç¹ï¼"childList"ï¼æ·»å /å é¤äºåå ç´ ã
targetââ æ´æ¹åçå¨ä½å¤ï¼"attributes"æå¨çå ç´ ï¼æ"characterData"æå¨çææ¬èç¹ï¼æ"childList"å卿å¨çå ç´ ï¼addedNodes/removedNodesââ æ·»å /å é¤çèç¹ï¼previousSibling/nextSiblingââ æ·»å /å é¤çèç¹çä¸ä¸ä¸ª/ä¸ä¸ä¸ªå å¼èç¹ï¼attributeName/attributeNamespaceââ è¢«æ´æ¹çç¹æ§çåç§°/å½å空é´ï¼ç¨äº XMLï¼ï¼oldValueââ ä¹åçå¼ï¼ä» éç¨äºç¹æ§æææ¬æ´æ¹ï¼å¦æè®¾ç½®äºç¸åºé项attributeOldValue/characterDataOldValueã
ä¾å¦ï¼è¿éæä¸ä¸ª <div>ï¼å®å
·æ contentEditable ç¹æ§ãè¯¥ç¹æ§ä½¿æä»¬å¯ä»¥èç¦åç¼è¾å
ç´ ã
<div contentEditable id="elem">Click and <b>edit</b>, please</div>
<script>
let observer = new MutationObserver(mutationRecords => {
console.log(mutationRecords); // console.log(the changes)
});
// è§å¯é¤äºç¹æ§ä¹å¤çææåå¨
observer.observe(elem, {
childList: true, // è§å¯ç´æ¥åèç¹
subtree: true, // åå
¶æ´ä½çå代èç¹
characterDataOldValue: true // å°æ§çæ°æ®ä¼ éç»åè°
});
</script>
妿æä»¬å¨æµè§å¨ä¸è¿è¡ä¸é¢è¿æ®µä»£ç ï¼å¹¶èç¦å°ç»å®ç <div> ä¸ï¼ç¶åæ´æ¹ <b>edit</b> ä¸çææ¬ï¼console.log å°æ¾ç¤ºä¸ä¸ªåå¨ï¼
mutationRecords = [{
type: "characterData",
oldValue: "edit",
target: <text node>,
// å
¶ä»å±æ§ä¸ºç©º
}];
妿æä»¬è¿è¡æ´å¤æçç¼è¾æä½ï¼ä¾å¦å é¤ <b>edit</b>ï¼é£ä¹åå¨äºä»¶å¯è½ä¼å
å«å¤ä¸ªåå¨è®°å½ï¼
mutationRecords = [{
type: "childList",
target: <div#elem>,
removedNodes: [<b>],
nextSibling: <text node>,
previousSibling: <text node>
// å
¶ä»å±æ§ä¸ºç©º
}, {
type: "characterData"
target: <text node>
// ...åå¨ç详ç»ä¿¡æ¯åå³äºæµè§å¨å¦ä½å¤çæ¤ç±»å é¤
// å®å¯è½æ¯å°ä¸¤ä¸ªç¸é»çææ¬èç¹ "edit " å ", please" åå¹¶æä¸ä¸ªèç¹ï¼
// æè
å¯è½å°å®ä»¬çå¨åç¬çææ¬èç¹ä¸
}];
å æ¤ï¼MutationObserver å
许对 DOM åæ ä¸ç任使´æ¹ä½åºååºã
ç¨äºéæ
å¨ä»ä¹æ¶åå¯è½æç¨ï¼
æ³è±¡ä¸ä¸ï¼ä½ éè¦æ·»å ä¸ä¸ªç¬¬ä¸æ¹èæ¬ï¼è¯¥èæ¬ä¸ä»
å
嫿ç¨çåè½ï¼è¿ä¼æ§è¡ä¸äºæä»¬ä¸æ³è¦çæä½ï¼ä¾å¦æ¾ç¤ºå¹¿å <div class="ads">Unwanted ads</div>ã
å½ç¶ï¼ç¬¬ä¸æ¹èæ¬æ²¡ææä¾å é¤å®çæºå¶ã
ä½¿ç¨ MutationObserverï¼æä»¬å¯ä»¥çæµå°æä»¬ä¸éè¦çå
ç´ ä½æ¶åºç°å¨æä»¬ç DOM ä¸ï¼å¹¶å°å
¶å é¤ã
è¿æä¸äºå ¶ä»æ åµï¼ä¾å¦ç¬¬ä¸æ¹èæ¬ä¼å°æäºå 容添å å°æä»¬çææ¡£ä¸ï¼å¹¶ä¸æä»¬å¸ææ£æµåºè¿ç§æ åµä½æ¶åçï¼ä»¥è°æ´é¡µé¢ï¼å¨æè°æ´æäºå 容ç大å°çã
MutationObserver 使æä»¬è½å¤å®ç°è¿ç§éæ±ã
ç¨äºæ¶æ
仿¶æçè§åº¦æ¥çï¼å¨æäºæ
åµä¸ï¼MutationObserver æä¸éçä½ç¨ã
å设æä»¬æ£å¨å»ºç«ä¸ä¸ªæå ³ç¼ç¨çç½ç«ãèªç¶å°ï¼æç« åå ¶ä»ææä¸å¯è½å 嫿ºä»£ç 段ã
å¨ HTML æ è®°ï¼markupï¼ä¸çæ¤ç±»ç段å¦ä¸æç¤ºï¼
...
<pre class="language-javascript"><code>
// è¿éæ¯ä»£ç
let hello = "world";
</code></pre>
...
ä¸ºäºæé«å¯è¯»æ§ï¼åæ¶å¯¹å
¶è¿è¡ç¾åï¼æä»¬å°å¨æä»¬çç½ç«ä¸ä½¿ç¨ JavaScript è¯æ³é«äº®æ¾ç¤ºåºï¼ä¾å¦ Prism.jsã为äºä½¿ç¨ Prism 对以ä¸ä»£ç çæ®µè¿è¡è¯æ³é«äº®æ¾ç¤ºï¼æä»¬è°ç¨äº Prism.highlightElem(pre)ï¼å®ä¼æ£æ¥æ¤ç±» pre å
ç´ çå
容ï¼å¹¶ä¸ºè¿äºå
ç´ æ·»å ç¹æ®çæ ç¾ï¼tagï¼åæ ·å¼ï¼ä»¥è¿è¡å½©è²è¯æ³é«äº®æ¾ç¤ºï¼ç±»ä¼¼äºä½ 卿¬æç示ä¾ä¸çå°ç飿 ·ã
é£ä¹ï¼æä»¬åºè¯¥å¨ä»ä¹æ¶åæ§è¡è¯¥é«äº®æ¾ç¤ºæ¹æ³å¢ï¼æä»¬å¯ä»¥å¨ DOMContentLoaded äºä»¶ä¸æ§è¡ï¼æè
å°èæ¬æ¾å¨é¡µé¢çåºé¨ãDOM 就绪åï¼æä»¬å¯ä»¥æç´¢å
ç´ pre[class*="language"] 并对å
¶è°ç¨ Prism.highlightElemï¼
// é«äº®æ¾ç¤ºé¡µé¢ä¸çææä»£ç 段
document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem);
å°ç®å为æ¢ï¼ä¸åé½å¾ç®åï¼å¯¹å§ï¼æä»¬æ¾å° HTML ä¸ç代ç çæ®µå¹¶é«äº®æ¾ç¤ºå®ä»¬ã
ç°å¨è®©æä»¬ç»§ç»ãå设æä»¬è¦ä»æå¡å¨å¨æè·åèµæãæä»¬å° 卿¬æç¨çåç»ç« è ä¸å¦ä¹ è¿è¡æ¤æä½çæ¹æ³ãç®åï¼åªéè¦å ³å¿æä»¬ä»ç½ç»æå¡å¨è·å HTML æç« å¹¶æéæ¾ç¤ºï¼
let article = /* 仿å¡å¨è·åæ°å
容 */
articleElem.innerHTML = article;
æ°ç article HTML å¯è½å
å«ä»£ç 段ãæä»¬éè¦å¯¹å
¶è°ç¨ Prism.highlightElemï¼å¦åå®ä»¬å°ä¸ä¼è¢«é«äº®æ¾ç¤ºã
对äºå¨æå è½½çæç« ï¼åºè¯¥å¨ä½å¤ä½æ¶è°ç¨ Prism.highlightElemï¼
æä»¬å¯ä»¥å°è¯¥è°ç¨éå å°å è½½æç« ç代ç ä¸ï¼å¦ä¸æç¤ºï¼
let article = /* 仿å¡å¨è·åæ°å
容 */
articleElem.innerHTML = article;
let snippets = articleElem.querySelectorAll('pre[class*="language-"]');
snippets.forEach(Prism.highlightElem);
â¦â¦ä½æ¯ï¼æ³è±¡ä¸ä¸ï¼å¦æä»£ç 䏿å¾å¤å°æ¹é½æ¯å¨å è½½å å®¹ï¼æç« ï¼æµéªå论åå¸åçãæä»¬æ¯å¦éè¦å¨æ¯ä¸ªå°æ¹é½éå ä¸ä¸ªé«äº®æ¾ç¤ºè°ç¨ï¼ä»¥å¨å 容å è½½å®æåï¼é«äº®å 容ä¸ç代ç ãé£å¾ä¸æ¹ä¾¿ã
å¹¶ä¸ï¼å¦æå 容æ¯ç±ç¬¬ä¸æ¹æ¨¡åå è½½çï¼è¯¥æä¹åï¼ä¾å¦ï¼æä»¬æä¸ä¸ªç±å ¶ä»äººç¼åç论åï¼è¯¥è®ºåå¯ä»¥å¨æå è½½å 容ï¼å¹¶ä¸æä»¬æ³ä¸ºå ¶æ·»å è¯æ³é«äº®æ¾ç¤ºã没æäººåæ¬¢ä¿®è¡¥ç¬¬ä¸æ¹èæ¬ã
幸è¿çæ¯ï¼è¿æå¦ä¸ç§éæ©ã
æä»¬å¯ä»¥ä½¿ç¨ MutationObserver æ¥èªå¨æ£æµä½æ¶å¨é¡µé¢ä¸æå
¥äºä»£ç 段ï¼å¹¶é«äº®æ¾ç¤ºå®ä»¬ã
å æ¤ï¼æä»¬å¨ä¸ä¸ªå°æ¹å¤çé«äº®æ¾ç¤ºåè½ï¼ä»è使æä»¬æ ééæå®ã
卿é«äº®æ¾ç¤ºç¤ºä¾
è¿æ¯ä¸ä¸ªå·¥ä½ç¤ºä¾ã
å¦æä½ è¿è¡è¿æ®µä»£ç ï¼å®å°å¼å§è§å¯ä¸é¢çå ç´ ï¼å¹¶é«äº®æ¾ç¤ºç°å¨æ¤å¤çææä»£ç 段ï¼
let observer = new MutationObserver(mutations => {
for(let mutation of mutations) {
// æ£æ¥æ°èç¹ï¼æä»ä¹éè¦é«äº®æ¾ç¤ºçåï¼
for(let node of mutation.addedNodes) {
// æä»¬åªè·è¸ªå
ç´ ï¼è·³è¿å
¶ä»èç¹ï¼ä¾å¦ææ¬èç¹ï¼
if (!(node instanceof HTMLElement)) continue;
// æ£æ¥æå
¥çå
ç´ æ¯å¦ä¸ºä»£ç 段
if (node.matches('pre[class*="language-"]')) {
Prism.highlightElement(node);
}
// æè
å¯è½å¨åæ çæä¸ªå°æ¹æä¸ä¸ªä»£ç 段ï¼
for(let elem of node.querySelectorAll('pre[class*="language-"]')) {
Prism.highlightElement(elem);
}
}
}
});
let demoElem = document.getElementById('highlight-demo');
observer.observe(demoElem, {childList: true, subtree: true});
ä¸é¢æä¸ä¸ª HTML å
ç´ ï¼ä»¥åä½¿ç¨ innerHTML 卿填å
å®ç JavaScriptã
请å
è¿è¡åé¢é£æ®µä»£ç ï¼ä¸é¢é£æ®µï¼è§å¯å
ç´ ï¼ï¼ç¶åè¿è¡ä¸é¢è¿æ®µä»£ç ãä½ å°çå° MutationObserver æ¯å¦ä½æ£æµå¹¶é«äº®æ¾ç¤ºä»£ç 段çã
ä¸ä¸ªå
·æ id="highlight-demo" ç示ä¾å
ç´ ï¼è¿è¡ä¸é¢é£æ®µä»£ç æ¥è§å¯å®ã
ä¸é¢è¿æ®µä»£ç å¡«å
äºå
¶ innerHTMLï¼è¿å¯¼è´ MutationObserver ä½åºååºï¼å¹¶çªåºæ¾ç¤ºå
¶å
容ï¼
let demoElem = document.getElementById('highlight-demo');
// 卿æå
¥å¸¦æä»£ç 段çå
容
demoElem.innerHTML = `ä¸é¢æ¯ä¸ä¸ªä»£ç 段ï¼
<pre class="language-javascript"><code> let hello = "world!"; </code></pre>
<div>å¦ä¸ä¸ªä»£ç 段ï¼</div>
<div>
<pre class="language-css"><code>.class { margin: 5px; } </code></pre>
</div>
`;
ç°å¨æä»¬æäº MutationObserverï¼å®å¯ä»¥è·è¸ªè§å¯å°çå
ç´ ä¸çï¼æè
æ´ä¸ª document ä¸çææé«äº®æ¾ç¤ºãæä»¬å¯ä»¥å¨ HTML 䏿·»å /å é¤ä»£ç 段ï¼èæ éèèé«äº®é®é¢ã
å ¶ä»æ¹æ³
æä¸ä¸ªæ¹æ³å¯ä»¥åæ¢è§å¯èç¹ï¼
observer.disconnect()ââ 忢è§å¯ã
彿们忢è§å¯æ¶ï¼è§å¯å¨å¯è½å°æªå¤çæäºæ´æ¹ãå¨ç§æ åµä¸ï¼æä»¬ä½¿ç¨ï¼
observer.takeRecords()ââ è·åå°æªå¤ççåå¨è®°å½å表ï¼è¡¨ä¸è®°å½çæ¯å·²ç»åçï¼ä½åè°ææªå¤ççåå¨ã
è¿äºæ¹æ³å¯ä»¥ä¸èµ·ä½¿ç¨ï¼å¦ä¸æç¤ºï¼
// å¦æä½ å
³å¿å¯è½æªå¤ççè¿æçåå¨
// é£ä¹ï¼åºè¯¥å¨ disconnect åè°ç¨è·åæªå¤ççåå¨å表
let mutationRecords = observer.takeRecords();
// 忢è·è¸ªåå¨
observer.disconnect();
...
observer.takeRecords() è¿åçè®°å½è¢«ä»å¤çéåä¸ç§»é¤åè°å½æ°ä¸ä¼è¢« observer.takeRecords() è¿åçè®°å½è°ç¨ã
è§å¯å¨å¨å é¨å¯¹èç¹ä½¿ç¨å¼±å¼ç¨ãä¹å°±æ¯è¯´ï¼å¦æä¸ä¸ªèç¹è¢«ä» DOM ä¸ç§»é¤äºï¼å¹¶ä¸è¯¥èç¹åå¾ä¸å¯è®¿é®ï¼é£ä¹å®å°±å¯ä»¥è¢«åå¾åæ¶ã
è§å¯å° DOM èç¹è¿ä¸äºå®å¹¶ä¸è½é»æ¢åå¾åæ¶ã
æ»ç»
MutationObserver å¯ä»¥å¯¹ DOM çååä½åºååº ââ ç¹æ§ï¼attributeï¼ï¼ææ¬å
å®¹ï¼æ·»å /å é¤å
ç´ ã
æä»¬å¯ä»¥ç¨å®æ¥è·è¸ªä»£ç å ¶ä»é¨åå¼å ¥çæ´æ¹ï¼ä»¥åä¸ç¬¬ä¸æ¹èæ¬éæã
MutationObserver å¯ä»¥è·è¸ªä»»ä½æ´æ¹ãconfig âè¦è§å¯çå
容âé项ç¨äºä¼åï¼é¿å
ä¸å¿
è¦çåè°è°ç¨ä»¥èçèµæºã
è¯è®º
<code>æ ç¾æå ¥åªæå 个è¯ç代ç ï¼æå ¥å¤è¡ä»£ç å¯ä»¥ä½¿ç¨<pre>æ ç¾ï¼å¯¹äºè¶ è¿ 10 è¡ç代ç ï¼å»ºè®®ä½ ä½¿ç¨æ²ç®±ï¼plnkrï¼JSBinï¼codepenâ¦ï¼