Table of contents
attributes: Whether to monitor the label attribute changes
childList: Whether to monitor child node changes
characterData: Whether to monitor the change of the content of the text node
attributeOldValue: Whether to record the value before the attribute change
characterDataOldValue: Whether to record the value before the content of the text node changes
subtree: Whether to monitor the changes of descendant nodes
attributeFilter: filter attribute name
Application scenarios of MutationObserver
Detect and respond to DOM changes
Second is operational conflict
Finally, it is impossible to monitor changes in IFrame
foreword
MutationObserver may not be commonly used in development, but it can indeed solve some problems in special cases. It is somewhat similar to addEventListener, when the user triggers certain event operations, the corresponding callback will be called
A few days ago, I used MutationObserver in the requirement iteration. Since the pop-up window in the early version of Antd did not respond, and many pop-up windows in the code were not re-encapsulated, it was impossible to know when the pop-up window appeared and disappeared. So I used the front-end Hack to take advantage of the way to monitor element changes to solve such problems. Here is a knowledge sharing
So what exactly is a MutationObserver? how to use? What role does it play in development? What are the hidden dangers of using this API? please watch the following part
Evolution
Before Mutation was standardized, the unofficial way for developers to monitor DOM changes was to use the timer (polling) mechanism to create macro tasks through setTimeout or setInterval to observe changes in nodes;
In addition, some scenarios can also monitor operations and changes through the event delegation mechanism addEventListener
Later, the emergence of MutationEvent enhanced the scalability and limitations of DOM monitoring, and standardized Mutation. However, MutationEvent adopts a synchronous method and triggers callbacks in real time, that is, each change will trigger the monitoring callback function, which is very lossy in performance.
So there is the current MutationObserver . MutationObserver, like Promise, belongs to the microtask queue. It uses an asynchronous monitoring method. All operations will be placed in the callback. When there is an operation, the monitoring callback will be triggered when the next microtask is executed; or it can be understood as: when a node performs multiple operations at the same time, its changes will be recorded in an asynchronous queue and finally displayed at one time. This will not affect page loading, but also ensures the monitoring of DOM changes
basic concept
MutationObserver is a JS API that can be used to observe changes in the DOM tree in a document and execute specific callback functions when these changes occur.
Introduce the basic usage. The MutationObserver class receives a callback function, which is triggered when the label changes. The parameter mutationsList is an array of MutationRecord objects (more on this later), and the parameter observer is the instance object of the current MutationObserver; there is a function observe in the observer instance, and two parameters are passed in. The first is the label to be monitored, and the second is the configuration item that mainly declares which attributes to monitor, such as childList, attributes, etc.
const elem = document.querySelector("#elem");
// 创建观察者实例
const observer = new MutationObserver((mutationsList, observer) => {
// 监听回调
console.log(mutationsList, observer);
});
observer.observe(elem, {
//至少要传一个配置
attributes: true,
});
// 元素发生改变
elem.hidden = true;
MutationObserver
Instances of the MutationObserver class have the following functions
observe(target, options)
Observe the specified target element. The second parameter passes in a configuration object to specify the type of event to listen to and other options
The configuration can pass in the following options:
attributes: Whether to monitor the label attribute changes
When introducing the basic usage, we exemplified the attributes configuration. When the hidden attribute changes, the monitoring callback will be triggered
childList: Whether to monitor child node changes
Following the sample code above, we change the configuration of observe to childList: true, so that we can monitor the changes of child nodes
<body>
<div id="elem"></div>
<div id="son"></div>
<script type="text/javascript">
const elem = document.querySelector("#elem");
const son = document.querySelector("#son");
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
childList: true,
});
elem.textContent = "小黑";
elem.appendChild(son);
elem.removeChild(son);
</script>
</body>
characterData: Whether to monitor the change of the content of the text node
It is worth noting that the text node is a child node of the label, so first we need to monitor the child nodes of the label to change, such as
const elem = document.querySelector("#elem");
const textElem = elem.firstChild; // 这里获取标签的文本节点
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(textElem, {
characterData: true,
});
textElem.textContent = "小黑";
attributeOldValue: Whether to record the value before the attribute change
attributeOldValue must be used in conjunction with attribute, we first monitor the attribute change of the label.
<body>
<div id="elem" name="阿黄"></div>
<script type="text/javascript">
const elem = document.querySelector("#elem");
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
attributes: true,
});
elem.setAttribute("name", "小黑");
</script>
</body>
When we listen to the attributes property, we will find that oldValue is null
if we add
observer.observe(elem, {
attributes: true,
attributeOldValue: true,
});
will store the original attribute value
characterDataOldValue: Whether to record the value before the content of the text node changes
Similar to attributeOldValue, characterDataOldValue is used to record and store the original text value. We change the text to black, and you can see that the value of oldValue in the callback is the previous A Huang
const elem = document.querySelector("#elem");
const textElem = elem.firstChild;
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(textElem, {
characterData: true,
characterDataOldValue: true,
});
textElem.textContent = "小黑";
subtree: Whether to monitor the changes of descendant nodes
Let's still take the above code as an example. If there are two divs nested and you want to monitor the bottom div change, you can add the attribute subtree and the attribute to be monitored, such as monitoring the attribute changes of all descendant nodes.
<body>
<div id="elem">
<div>
<div></div>
</div>
</div>
<script type="text/javascript">
const elem = document.querySelector("#elem");
const child = elem.firstElementChild.firstElementChild;
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
subtree: true,
attributes: true,
});
child.setAttribute("name", "阿黄");
</script>
</body>
attributeFilter: filter attribute name
While configuring attributes to monitor attribute changes, you can use the attributeFilter configuration item to filter attribute names. AttributeFilter filters by passing in a string array [ "class","name" ]. For example, I only want to monitor changes in class names
<body>
<div id="elem"></div>
<script type="text/javascript">
const elem = document.querySelector("#elem");
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
attributes: true,
attributeFilter: ["class"],
});
elem.setAttribute("name", "阿黄");
elem.setAttribute("class", "elem");
elem.hidden = true;
</script>
</body>
At this time, only the callback after the class is modified will be displayed
disconnect()
When we need to cancel the monitoring of label changes, we can use the disconnect() function of the instantiated object MutationObserver to interrupt. Since the Dom tree changes are asynchronous, we use a delay to trigger the cancellation of monitoring.
const elem = document.querySelector("#elem");
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
attributes: true,
});
elem.setAttribute("name", "阿黄");
setTimeout(() => {
observer.disconnect();
elem.hidden = true;
});
takeRecords()
The first parameter in the callback function is the mutationsList array. At this time, if we want to clear this array, we can use the takeRecords function to achieve the reset effect.
const elem = document.querySelector("#elem");
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList, observer);
});
observer.observe(elem, {
attributes: true,
});
elem.setAttribute("name", "阿黄");
elem.hidden = true;
observer.takeRecords();
elem.setAttribute("name", "小黑");
After the above code is run, only the operation whose name is set to black will be printed
MutationRecord[]
When the MutationObserver class is instantiated, an observer callback function is passed in. Its first parameter is a MutationRecord array, which receives the changed element information
Properties of MutationRecord
target: the changed node
type: the type of change
- attributes: attributes are added, modified or deleted
- characterData: the text of the label changes
- childList: child nodes are added, modified order or deleted
nextSibling: a sibling node after the child node of the parent node (insertBefore, removeChild)
previousSibling: the previous sibling node of the child node of the parent node (appendChild)
attributeName: When type is attributes, it indicates the changed attribute name (setAttribute)
attributeNamespace: When type is attributes, it indicates the changed attribute namespace name (setAttributeNS)
addedNodes: added nodes
removedNodes: removed nodes
oldValue: the old value recorded when attributeOldValue or characterDataOldValue is true
The following piece of code covers almost all the above attributes, you can refer to it
<body>
<div id="elem" name="阿黄">elem</div>
<div id="son">son</div>
<div id="prev">prev</div>
<div id="next">next</div>
<script type="text/javascript">
const elem = document.querySelector("#elem");
const son = document.querySelector("#son");
const prev = document.querySelector("#prev");
const next = document.querySelector("#next");
const elemText = elem.firstChild;
const observer = new MutationObserver((mutationsList, observer) => {
console.log(mutationsList);
});
observer.observe(elem, {
attributes: true,
attributeOldValue: true,
characterData: true,
characterDataOldValue: true,
subtree: true,
childList: true,
});
// type: "characterData", oldValue: "elem"
elemText.textContent = "阿黄";
// oldValue: "阿黄", type: "attributes", attributeName : "name"
elem.setAttribute("name", "小黑");
// attributeName: "name", attributeNamespace: "ns", type: "attributes"
elem.setAttributeNS("ns", "NS:name", "阿黄");
// type: "childList", removedNodes: NodeList[text], addedNodes: NodeList[text]
elem.textContent = "小黑";
// addedNodes:NodeList[div#prev], type: "childList"
elem.appendChild(prev);
// addedNodes:NodeList[div#next], type: "childList", previousSibling: div#prev
elem.appendChild(next);
// addedNodes: NodeList[div#son], type: "childList", previousSibling: div#prev, nextSibling: div#next
elem.insertBefore(son, next);
// removedNodes: NodeList[div#son], type: "childList", previousSibling: div#prev, nextSibling: div#next
elem.removeChild(son);
</script>
</body>
Application scenarios of MutationObserver
Here are some common scenarios
Detect and respond to DOM changes
For example, use MutationObserver to implement lazy loading of images, monitor the visibilitychange event of the img tag, and respond; or load images when the offset top of the element is in the window
Dynamic Style Changes
Monitor style or class changes to respond, such as my previous application: monitor antd's modal window changes, and make follow-up operations
Communication between tags
Send and receive messages by listening to changes in the data-key attribute
shortcoming
MutationObserver is easy to use, but its shortcomings are also obvious
The first is performance loss
Although many optimizations have been made on the basis of MutationEvent, the operation of monitoring the body still has a great impact on performance, and all user operations may cause the function to be called back frequently.
The solution is to monitor a small range of nodes as much as possible, or limit the type of monitoring
Second is operational conflict
Due to the non-uniqueness of the callback function, if there is a dependency between two observers listening to the changed operation, it may cause errors or conflicts
The solution can be to use the lock mechanism, when the two conditions are met to enter the function or thread
Finally, it is impossible to monitor changes in IFrame
The MutationObserver operation is monitored based on the current DOM, so it cannot cross threads and windows
You can use postmessage for communication operations, you can refer to a previous article about window and thread communication
Summarize
This article introduces the basic concept and use of the MutationObserver class. The way to monitor the DOM is from the earliest timer and event delegation to MutationEvent and finally to the MutationObserver introduced in this article; it uses an asynchronous non-real-time monitoring method, and the monitoring callback returns a MutationRecord list to record the operation changes of the Dom; in addition, we can monitor a node through the observe of the instance. The types of monitoring mainly include attributes (attributes), childList (child node changes), characterData ( Text node changes), other configuration items include attributeOldValue (record attribute old value), characterDataOldValue (record text old value), subtree (monitor descendant nodes), attributeFilter (attribute name filtering); finally introduces the application scenarios and disadvantages of MutationObserver. The application scenario is mainly to monitor DOM changes and take corresponding operations. The disadvantages are mainly: performance loss, operation conflicts, and thread restrictions;
The above is the entire content of the article, I hope it will be helpful to you, if you think the article is good, please support the author three times, thank you very much!