MutationObserverë DOM ìì를 ê´ì°°íë¤ê° ë³ê²½ì´ ê°ì§ëë©´ ì½ë°±ì ì¤ííë ë´ì¥ ê°ì²´ì
ëë¤.
먼ì 문ë²ì ìì본 ë¤ì ì¤ì ì¬ë¡ë¥¼ ë³´ë©´ì 뮤í ì´ì ìµì ë²ê° ì´ëì ì ì©íì§ ì´í´ë³´ê² ìµëë¤.
문ë²
MutationObserver ì¬ì©ë²ì ê°ë¨í©ëë¤.
먼ì ì½ë°± í¨ì를 ë겨 ìµì ë²ë¥¼ ë§ëëë¤.
let observer = new MutationObserver(callback);
ê·¸ë¦¬ê³ ë§ë ìµì ë²ë¥¼ DOM ë ¸ëì ë¶ì ëë¤.
observer.observe(node, config);
configë 'ì´ë¤ ì¢
ë¥ì ë³ê²½ì ë°ìí ì§â를 ëíë´ë ë¶ë¦°ê° ìµì
ì´ ë´ê¸´ ê°ì²´ì
ëë¤.
childListânodeì ì§ê³ ìììì ì¼ì´ëë ë³ê²½subtreeânodeì 모ë ìììì ì¼ì´ëë ë³ê²½attributesânodeì ìì± ë³ê²½attributeFilterâ ì íí ìì±ë§ ê´ì°°íëë¡ ìì± ì´ë¦ì ë´ì ë°°ì´characterDataânode.data(í ì¤í¸ ì½í ì¸ )를 ê´ì°°í ì§ ì¬ë¶
ì´ ì¸ì ë¤ì ìµì ë ììµëë¤.
attributeOldValueâtrueì´ë©´ ìì±ì ì´ì ê°ê³¼ ì ê°ì 모ë ì½ë°±ì ì ë¬íê³ (ìë ì°¸ê³ ) ê·¸ë ì§ ìì¼ë©´ ì ê°ë§ ì ë¬í¨(attributesìµì íì)characterDataOldValueâtrueì´ë©´node.dataì ì´ì ê°ê³¼ ì ê°ì 모ë ì½ë°±ì ì ë¬íê³ (ìë ì°¸ê³ ) ê·¸ë ì§ ìì¼ë©´ ì ê°ë§ ì ë¬í¨(characterDataìµì íì)
ìµì ë²ë¥¼ ë¶ì¸ DOM ë
¸ëì ì¤ì ë³ê²½ì´ ì길 ëë§ë¤ callbackì´ ì¤íë©ëë¤. ì´ë ì½ë°± í¨ìì 첫 ë²ì§¸ ì¸ìë¡ 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">ì¬ê¸°ë¥¼ í´ë¦í´ <b>ìì </b>í´ ë³´ì¸ì</div>
<script>
let observer = new MutationObserver(mutationRecords => {
console.log(mutationRecords); // ë³ê²½ ì¬íì´ ì¶ë ¥ë©ëë¤.
});
// ìì±ì ì ì¸í 모ë ë³ê²½ì ê´ì°°í©ëë¤.
observer.observe(elem, {
childList: true, // ì§ê³ ììì ê´ì°°í¨
subtree: true, // íì ììë ê´ì°°í¨
characterDataOldValue: true // ì´ì ë°ì´í°ë ì½ë°±ì ì ë¬í¨
});
</script>
ì°ì¸¡ ìë¨ ì¬ì ë²í¼ì ëë¬ ì½ë를 ë¸ë¼ì°ì ìì ì¤ííê³ <div>ì í¬ì»¤ì¤ë¥¼ ì¤ ë¤ì <b>ìì </b> ìì í
ì¤í¸ë¥¼ ë°ê¾¸ë©´ console.log ë문ì ê°ë°ì ë구ì 뮤í
ì´ì
íëê° ì¶ë ¥ë©ëë¤.
mutationRecords = [{
type: "characterData",
oldValue: "ìì ",
target: <text node>,
// ë¤ë¥¸ íë¡í¼í°ë ë¹ì´ ìì
}];
<b>ìì </b>ì íµì§¸ë¡ ì§ì°ë ë± ë ë³µì¡í ë°©ìì¼ë¡ ìì ì íë©´ 뮤í
ì´ì
ì´ë²¤í¸ì 뮤í
ì´ì
ë ì½ëê° ì¬ë¬ ê° ë´ê¸¸ ì ììµëë¤.
mutationRecords = [{
type: "childList",
target: <div#elem>,
removedNodes: [<b>],
nextSibling: <text node>,
previousSibling: <text node>,
// ë¤ë¥¸ íë¡í¼í°ë ë¹ì´ ìì
}, {
type: "characterData",
target: <text node>,
// ...뮤í
ì´ì
ì ì¸ë¶ ë´ì©ì ë¸ë¼ì°ì ê° ì´ë° ìì 를 ì´ë»ê² ì²ë¦¬íëì§ì ë°ë¼ ë¤ë¦
ëë¤.
// ì¸ì í ë í
ì¤í¸ ë
¸ë 'ì¬ê¸°ë¥¼ í´ë¦í´ 'ì 'í´ ë³´ì¸ì'를 íëë¡ í©ì¹ ìë ìê³
// ë³ê°ì í
ì¤í¸ ë
¸ëë¡ ë¨ê²¨ë ìë ììµëë¤.
}];
ì´ì²ë¼ MutationObserver를 ì¬ì©íë©´ DOM ìë¸í¸ë¦¬ìì ì¼ì´ëë 모ë ë³ê²½ì ë°ìí ì ììµëë¤.
ìëíí° ì¤í¬ë¦½í¸ íµí©ì íì©í기
뮤í ì´ì ìµì ë²ë ì´ë¤ ê²½ì°ì ì ì©í ê¹ì?
ì ì©í 기ë¥ì´ ë¤ì´ ìì§ë§ ìì¹ ìë ëìë íë ìëíí° ì¤í¬ë¦½í¸ë¥¼ ì¶ê°í´ì¼ íë ìí©ì ììí´ ë´
ìë¤. ì´ ì¤í¬ë¦½í¸ë <div class="ads">ìì¹ ìë ê´ê³ </div>ê°ì´ ê´ê³ 를 ë
¸ì¶í©ëë¤.
ë¹ì°íê²ë ìëíí° ì¤í¬ë¦½í¸ë ê´ê³ 를 ì ê±°í ìë¨ì ì ê³µíì§ ììµëë¤.
ì´ë´ ë MutationObserver를 ì¬ì©íë©´ ìì¹ ìë ììê° DOMì ëíëë ìê°ì ê°ì§í´ ì ê±°í ì ììµëë¤.
ì´ ì¸ìë ìëíí° ì¤í¬ë¦½í¸ê° 문ìì 무ì¸ê°ë¥¼ ì¶ê°íë ìì ì ê°ì§í´ì íì´ì§ë¥¼ ì¡°ì íê±°ë í¹ì ììì í¬ê¸°ë¥¼ ëì ì¼ë¡ ì¡°ì íê³ ì¶ì ê²½ì°ë ììµëë¤.
ì´ë° ìì
ìì MutationObserverë¡ êµ¬íí ì ììµëë¤.
ìí¤í ì² ê´ì ìì íì©í기
ìí¤í
ì² ê´ì ìì MutationObserverê° ì¢ì ì íì´ ëë ê²½ì°ë ììµëë¤.
íë¡ê·¸ëë°ì ë¤ë£¨ë ì¹ì¬ì´í¸ë¥¼ ë§ë¤ê³ ìë¤ê³ ê°ì í´ ë´ ìë¤. ì´ë° ì¬ì´í¸ì ê¸ì´ë ìë£ì ìì¤ ì½ë ììê° ë¤ì´ê°ê¸° ë§ë ¨ì ëë¤.
ì½ë ììë HTML ë§í¬ì ìì ë¤ìê³¼ ê°ì 모ìµì ëë¤.
...
<pre class="language-javascript"><code>
// ì¬ê¸°ì ì½ëê° ë¤ì´ê°ëë¤.
let hello = "world";
</code></pre>
...
ê°ë
ì±ì ëì´ë ëìì 보기 ì¢ê² 꾸미기 ìí´ Prism.js ê°ì ìë°ì¤í¬ë¦½í¸ 구문 ê°ì¡°(syntax highlighting) ë¼ì´ë¸ë¬ë¦¬ë¥¼ ì¬ì´í¸ì ì¬ì©í´ 본ë¤ê³ ê°ì íê² ìµëë¤. Prismìì ì ììì 구문 ê°ì¡°ë¥¼ ì ì©íë ¤ë©´ Prism.highlightElement(pre)를 í¸ì¶í©ëë¤. ì´ ë©ìëë pre ììì ë´ì©ì ê²ì¬í´ì ìì ì
íë í¹ë³í íê·¸ì ì¤íì¼ì í´ë¹ ìì ìì ì¶ê°í©ëë¤. ì§ê¸ ë³´ê³ ìë ì´ íì´ì§ì ìì ì½ëì ë¹ì·í ê²°ê³¼ë¬¼ì´ ë§ë¤ì´ì§ì£ .
ê·¸ë ë¤ë©´ 구문 ê°ì¡° ë©ìëë ì íí ì¸ì ì¤íí´ì¼ í ê¹ì? DOMContentLoaded ì´ë²¤í¸ìì ì¤íí ìë ìê³ ì¤í¬ë¦½í¸ë¥¼ íì´ì§ 맨 ìëì ë£ì ìë ììµëë¤. DOMì´ ì¤ë¹ëë©´ pre[class*="language"]ì í´ë¹íë ìì를 ì°¾ì Prism.highlightElement를 í¸ì¶íë©´ ë©ëë¤.
// íì´ì§ì ìë ì½ë ìì를 ì ë¶ ê°ì¡° íìí©ëë¤.
document.querySelectorAll('pre[class*="language"]').forEach(elem => Prism.highlightElement(elem));
ì¬ê¸°ê¹ì§ 문ì ë ê² ììµëë¤. HTMLìì ì½ë ìì를 ì°¾ì ê°ì¡° íìíë©´ ëì´ì£ .
ì´ì í 걸ì ë ëìê° ë´ ìë¤. ìë²ìì ìë£ë¥¼ ëì ì¼ë¡ ê°ì ¸ìì¼ íë¤ê³ í´ë´ ìë¤. ìë£ë¥¼ ê°ì ¸ì¤ë ë©ìëë íí ë¦¬ì¼ ë·ë¶ë¶ìì ë¤ë£° ìì ì´ë¼ ì§ê¸ì ì¹ ìë²ìì HTMLë¡ ë ê¸ì ë°ìì ìì²ì´ ìì ë íë©´ì íìíë¤ë ì ë§ ìë©´ ë©ëë¤.
let article = /* ìë²ìì ì ì½í
ì¸ ë¥¼ ê°ì ¸ì´ */
articleElem.innerHTML = article;
ìë¡ ë°ìì¨ article HTMLì ì½ë ììê° ë¤ì´ ìì ì ììµëë¤. Prism.highlightElement를 í¸ì¶íì§ ìì¼ë©´ ì´ ì½ëë ê°ì¡° íìëì§ ììµëë¤.
ê·¸ë¼ ëì ì¼ë¡ ë¶ë¬ì¨ ê¸ì Prism.highlightElement를 ì´ëì, ì¸ì í¸ì¶í´ì¼ í ê¹ì?
ë¤ìì²ë¼ ê¸ì ë¶ë¬ì¤ë ì½ë ë¤ì í¸ì¶ì ë§ë¶ì¼ ì ìì ê²ëë¤.
let article = /* ìë²ìì ì ì½í
ì¸ ë¥¼ ê°ì ¸ì´ */
articleElem.innerHTML = article;
let snippets = articleElem.querySelectorAll('pre[class*="language-"]');
snippets.forEach(elem => Prism.highlightElement(elem));
ê·¸ë°ë° ê¸, í´ì¦, ê²ìí ê¸ì²ë¼ ì½í ì¸ ë¥¼ ë¶ë¬ì¤ë ê³³ì´ ì½ë 곳곳ì ë§ë¤ê³ ììí´ ë´ ìë¤. ë¶ë¬ì¨ ì½í ì¸ ì ì½ë를 ê°ì¡° íìíë ¤ê³ ìì ê°ì ì½ë를 모ë ê³³ì ë¶ì¬ì¼ í ê¹ì? ê·¸ë¤ì§ í¸ë¦¬í ë°©ë²ì´ ìëëë¤.
ì½í ì¸ ë¥¼ ìëíí° ëª¨ëì´ ë¶ë¬ì¨ë¤ë©´ ì´ë¨ê¹ì? ì를 ë¤ì´ ë¤ë¥¸ ì¬ëì´ ë§ë ê²ìíì´ ì½í ì¸ ë¥¼ ëì ì¼ë¡ ë¶ë¬ì¤ëë° ì¬ê¸°ì 구문 ê°ì¡°ë¥¼ ì ì©íê³ ì¶ì ì ììµëë¤. ìëíí° ì¤í¬ë¦½í¸ì í¨ì¹ë¥¼ ë§ëë ì¼ì ë구ë ë°ê¸°ì§ ììµëë¤.
ë¤íí ë¤ë¥¸ ë°©ë²ì´ ììµëë¤.
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ì ì¬ì©í´ ì´ ìì를 ëì ì¼ë¡ ì±ì°ë ìë°ì¤í¬ë¦½í¸ê° ììµëë¤.
먼ì ì ì½ë(í´ë¹ ìì를 ê´ì°°í¨)를 ì¤íí ë¤ì ìë쪽 ì½ë를 ì¤íí´ ë³´ì¸ì. 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>
`;
ì´ì ê´ì°° ëì ììë document ì ì²´ìì ì¼ì´ëë ê°ì¡° íì를 MutationObserverê° ì ë¶ ì¶ì í ì ìê² ëììµëë¤. ë³ë¤ë¥¸ ì ê²½ì ì°ì§ ììë HTMLì ì½ë ììê° ì¶ê°ëê±°ë ìì ëì´ë ììì ê°ì¡° íìê° ë©ëë¤.
기í ë©ìë
ë ¸ë ê´ì°°ì ì¤ë¨íë ë©ìëë ì¡´ì¬í©ëë¤.
observer.disconnect()â ê´ì°°ì ì¤ë¨í¨
ê´ì°°ì ì¤ë¨íë ìì ì ìµì ë²ê° ìì§ ì²ë¦¬íì§ ìì ë³ê²½ì´ ë¨ì ìì ì ììµëë¤. ì´ë´ ë ë¤ì ë©ìë를 ì¬ì©í©ëë¤.
observer.takeRecords()â ì²ë¦¬ëì§ ìì 뮤í ì´ì ë ì½ë 목ë¡ì ê°ì ¸ì´. ë³ê²½ì ì¼ì´ë¬ì§ë§ ì½ë°±ì´ ìì§ ì²ë¦¬íì§ ìì ë ì½ëê° ëìì
ë ë©ìëë ë¤ìì²ë¼ í¨ê» ì¸ ì ììµëë¤.
// ì²ë¦¬ëì§ ìì 뮤í
ì´ì
목ë¡ì ê°ì ¸ìµëë¤.
// ìµê·¼ ì¼ì´ë ë³ê²½ ì¤ ë¯¸ì²ë¦¬ë¶ê¹ì§ ì±ê¸°ë ¤ë©´
// ê´ì°°ì ì¤ë¨í기 ì ì takeRecords를 í¸ì¶í´ì¼ í©ëë¤.
let mutationRecords = observer.takeRecords();
// ë³ê²½ ì¶ì ì ì¤ë¨í©ëë¤.
observer.disconnect();
...
observer.takeRecords()ê° ë°íí ë ì½ëë ì²ë¦¬ ë기ì´ìì ì ê±°ë©ëë¤observer.takeRecords()ê° ë°íí ë ì½ëì ëí´ì ì½ë°±ì´ í¸ì¶ëì§ ììµëë¤.
ìµì ë²ë ë´ë¶ì ì¼ë¡ ë ¸ëì ëí´ ì½í 참조(weak reference)를 ì¬ì©í©ëë¤. ë°ë¼ì ë ¸ëê° DOMìì ì ê±°ëì´ ëë¬í ì ìê² ëë©´ ê°ë¹ì§ 컬ë ì ì ëìì´ ë©ëë¤.
DOM ë ¸ëê° ê´ì°°ëê³ ìë¤ë ì¬ì¤ë§ì¼ë¡ë ê°ë¹ì§ 컬ë ì ì´ ë§íì§ ììµëë¤.
ìì½
MutationObserverë DOMìì ì¼ì´ëë ë³ê²½(ìì±, í
ì¤í¸ ì½í
ì¸ , ìì ì¶ê°Â·ìì )ì ë°ìí ì ììµëë¤.
ì½ëì ë¤ë¥¸ ë¶ë¶ìì ì¼ì´ë ë³ê²½ì ì¶ì í ëë ë¬¼ë¡ ìëíí° ì¤í¬ë¦½í¸ì íµí©í ëë ì¬ì©í ì ììµëë¤.
MutationObserverë ì´ë¤ ë³ê²½ì´ë ì¶ì í ì ììµëë¤. â무ìì ê´ì°°í ì§â ì íë config ìµì
ì ìµì í ì©ëë¡, ë¶íìí ì½ë°± í¸ì¶ì ììì ëë¹íì§ ì기 ìí´ ì¬ì©í©ëë¤.
ëê¸
<code>í그를, ì¬ë¬ ì¤ë¡ 구ì±ë ì½ë를 ì½ì íê³ ì¶ë¤ë©´<pre>í그를 ì´ì©íì¸ì. 10ì¤ ì´ìì ì½ëë plnkr, JSBin, codepen ë±ì ìëë°ì¤ë¥¼ ì¬ì©íì¸ì.