Mutation observer is an built-in JavaScript API that watch for changes made to the DOM tree and fires a callback function when it detects a change.
Syntax is very similar to other JavaScript observer APIs - Intersection observer API and Resize observer API.
let observer = new MutationObserver(callback);
observer.observe(targetElement, options);
targetElement
is DOM element that you want to observe.options
is an obejct with multiple coonfig options.const options ={
childList: true, //changes in the direct children of node,
subtree: true, //in all descendants of node,
attributes: true, //attributes of node,
attributeFilter: [] // an array of attribute names, to observe only selected ones.
characterData: true, // whether to observe node.data (text content),
attributeOldValue true,//if true, pass both the old and the new value of attribute to callback (see below), otherwise only the new one (needs attributes option),
characterDataOldValue: true, //if true, pass both the old and the new value of node.data to callback (see below), otherwise only the new one (needs characterData option).
}
Changes are passed using callback
function as first arguments as a list of MutationRecord objects.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mutation observer</title>
</head>
<div class="target-element" contenteditable>abc</div>
<button onclick="clickHandler100()">100</button>
<button onclick="clickHandler200()">200</button>
<button onclick="clickHandler300()">300</button>
<button onclick="clickHandler400()">400</button>
<body>
<script>
const targetElement = document.querySelector(".target-element");
const clickHandler100 = () => {
targetElement.style.width = "100px";
targetElement.style.height = "100px";
}
const clickHandler200 = () => {
targetElement.style.width = "200px";
targetElement.style.height = "200px";
}
const clickHandler300 = () => {
targetElement.style.width = "300px";
targetElement.style.height = "300px";
}
const clickHandler400 = () => {
targetElement.style.width = "400px";
targetElement.style.height = "400px";
}
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "attributes" && mutation.attributeName === "style") {
switch (mutation.target.clientHeight) {
case 100:
targetElement.textContent = "New content in red";
targetElement.style.color = 'red';
targetElement.style.backgroundColor = 'black';
break;
case 200:
targetElement.textContent = "New content in blue";
targetElement.style.color = 'blue';
targetElement.style.backgroundColor = 'red';
break;
default:
break;
}
}
}
});
observer.observe(targetElement, { attributes: true });
</script>
</body>
</html>
observer.disconnect()
: stops the observation and tracking changes in the target element.
takeRecords()
: takeRecords() returns a list of all matching DOM changes that have been detected but not yet processed by the observer's callback function, leaving the mutation queue empty.
Records returned by observer.takeRecords() are removed from the processing queue The callback won’t be called for records, returned for takeRecords().
const observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
/* later, when it's time to stop observing… */
/* handle any still-pending mutations */
let mutations = observer.takeRecords();
observer.disconnect();
if (mutations.length > 0) {
callback(mutations);
}