Mutation Observer
JavaScript Mutation Observer (JavaScript突变观察者)
A built-in object that observes a DOM element, firing a callback in case of modifications is known as MutationObserver. (观察DOM元素并在修改时触发回调的内置对象称为MutationObserver。)
Let’s start at the syntax and then cover use cases to show where it can be especially handy. (让我们从语法开始,然后介绍用例,以展示它在哪里特别方便。)
The Mutation Observer Syntax
The Mutation Observer Syntax (突变观察者语法)
MutationObserveris simple in usage. (MutationObserver用法简单。)
The first step should be creating an observer using a callback-function, like this:
let observer = new MutationObserver(callback);
The next step is attaching it to a DOM node, as follows:
observer.observe(node, config);
Config is an object that has options specifying the changes to respond to:
childList: modifications in the direct children of the node.
subtree: inside all the node descendants.
attributes: the node attributes.
attributeFilter: an array of attribute names for observing only the selected ones.
characterData: to observe the node.data or not.
The callback is run after any changes. The changes are transferred to the first argument as a list of MutationRecord objects, and the observer becomes the second object. (任何更改后运行回调。更改作为MutationRecord对象列表传输到第一个参数,观察者成为第二个对象。)
The MutationRecord object includes the following properties:
type: the type of mutation. It is one of “attributes”, “characterData”, and “childList”.
target: that’s where the change happens.
addedNodes/removedNodes: the added or removed nodes.
previousSibling/nextSibling: the previous/next sibling to added or removed nodes.
attributeName/attributeNamespace: the changed attribute name or namespace.
oldValue: the previous value merely for text or attribute changes, in case the matching option is set attributeOldValue/characterDataOldValue.
Let’s check out an example, including <div> with the contentEditable attribute, which allows focusing on it and editing:
<!DOCTYPE html>
<html>
<body>
<div contentEditable id="elemId">Click and <b>edit</b>...</div>
<script>
let observer = new MutationObserver(mutationRecords => {
alert(mutationRecords); // alert(the changes)
});
// observe everything except attributes
(//观察除属性之外的所有内容)
observer.observe(elemId, {
childList: true, // observe direct children
subtree: true, // lower descendants too
characterDataOldValue: true, // pass old data to callback
});
</script>
</body>
</html>
Running this code in the browser will demonstrate one mutation, like here:
mutationRecords = [{
type: "characterData",
oldValue: "edit",
target: <textNode> ,
// other properties empty
}];
After making more complicated editing operations, the mutation event can contain several mutation records, like this:
mutationRecords = [{
type: "childList",
target: <div# elem> ,
removedNodes: [ <b> ],
nextSibling: <textNode> ,
previousSibling: <textNode>
// other properties empty
}, {
type: "characterData"
target: <textNode>
// the mutation details depend on how the browser handles such deletion
(//变异细节取决于浏览器如何处理此类删除)
// it can combine two adjacent text nodes "edit " and ", please" into one node
(//它可以将两个相邻的文本节点“edit”和“, please”组合成一个节点)
//... or it may leave them separate text nodes
}];
So, with MutationObserver, you can respond to any changes inside the DOM tree. (因此,使用MutationObserver ,您可以响应DOM树内的任何更改。)
Using for Integration
Using for Integration (用于集成)
Now, let’s see when it can be especially useful. (现在,让我们看看它什么时候特别有用。)
MutationObserver can help you detect the unwanted element in the DOM and remove it. (MutationObserver可以帮助您检测DOM中不需要的元素并将其删除。)
In other situations when a third-party script adds something in the document, and you want to detect it for adapting the page. (在其他情况下,当第三方脚本在文档中添加内容时,您希望检测它以调整页面。)
With MutationObserver, you can do that. (使用MutationObserver ,您可以做到这一点。)
Using for Architecture
Using for Architecture (用于架构)
In some circumstances, MutationObserver is handy from architectural standpoint. (在某些情况下,从架构的角度来看, MutationObserver很方便。)
Imagine creating a programming website. (想象一下创建一个编程网站。)
A snippet in an HTML markup will look as follows:
...
<pre class="language-javascript"><code>
// here's the code
(//这是代码)
let welcome = "w3cdoc";
</code></pre>
...
There is a method of Prism.highlightElem(pre) that examines the contents of pre elements, adding into them particular tags and styles for colored syntax highlighting. This method can be run on DOMContentLoaded or at the bottom of the page. At that moment, the DOM is ready, and you can look for elements pre[class*=“language”] and call sm.highlightElem like this:
// highlight the all code snippets on the page
document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightEl);
Now, imagine that you need to dynamically fetch materials from a server:
let article = /* fetch a new content from server */
articleEl.innerHTML = article;
The new article HTML may include code snippets. The Prism.highlightEl should be called to highlight them. (新文章HTML可能包含代码段。应调用Prism.highlightEl以突出显示它们。)
That call can be appended to the code that loads an article, as follows:
let article = /* fetch new content from server */
articleEl.innerHTML = article;
let snippets = articleEl.querySelectorAll('pre[class*="language-"]');
snippets.forEach(Prism.highlightEl);
However, for the places in the code where contents such as quizzes, articles, it’s not convenient and can be forgotten. (但是,对于代码中测验、文章等内容的地方,这不方便,可能会被遗忘。)
There is another option, as well. (还有另一种选择。)
The MutationObserver can be used to automatically detect once code snippets are placed in the page highlighting them. (一旦代码片段被放置在突出显示它们的页面中, MutationObserver可用于自动检测。)
Dynamic Highlight
Dynamic Highlight (动态高亮)
Now let’s get to a working example. (现在让我们来看一个工作示例。)
Running the code below will invoke observing the element below, as well as highlighting the code snippets emerging there, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<div id="highlightDemo">Example</div>
<script>
let observer = new MutationObserver(mutations => {
for(let mutation of mutations) {
// examine new nodes, there is something to highlight
(//检查新节点,有一些要突出显示的内容)
for(let node of mutation.addedNodes) {
// we only track elements, skip other nodes
(//我们只跟踪元素,跳过其他节点)
if(!(node instanceof HTMLElement)) continue;
// check the inserted element for being a code snippets
(//检查插入的元素是否为代码段)
if(node.matches('pre[class*="language-"]')) {
Prism.highlightElement(node);
}
// or maybe there is a code snippet somewhere in its subtree
(//或者在其子树的某个地方有一个代码段)
for(let elem of node.querySelectorAll('pre[class*="language-"]')) {
Prism.highlightElement(elem);
}
}
}
});
let demoEl = document.getElementById('highlightDemo');
observer.observe(demoEl, {
childList: true,
subtree: true
});
</script>
</body>
</html>
Below, you can find an HTML-element and JavaScript that can dynamically fill it with innerHTML. (下面,您可以找到一个HTML元素和JavaScript ,可以用innerHTML动态填充它。)
If you execute the code above, then the one below, MutationObserver will find and highlight it. (如果您执行上面的代码,那么下面的代码, MutationObserver将找到并突出显示它。)
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<div id="highlight-demo">Example</div>
<script>
let demoElem = document.getElementById('highlight-demo');
// dynamically insert content with code snippets
(//使用代码段动态插入内容)
demoElem.innerHTML = `A code snippet is below:
<pre class="language-javascript"><code> let welcome= "w3cdoc"; </code></pre>
<div>Another one:</div>
<div>
<pre class="language-css"><code>.class { margin: 25px; } </code></pre>
</div>
`;
</script>
</body>
</html>
Summary
Summary (概要)
MutationObserver can respond to changes inside DOM: added and removed elements, text content, and attributes.
It can be used for tracking changes represented by other parts of the code, as well as for integrating with third-party scripts. (它可用于跟踪代码其他部分表示的更改,以及与第三方脚本集成。)
MutationObserver is capable of tracking any changes. (MutationObserver能够跟踪任何更改。)