Techcookies

Mutation observer API

JavaScript | Sat Sep 28 2024 | 3 min read

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.

Syntax

javascript
let observer = new MutationObserver(callback);
observer.observe(targetElement, options);
  1. targetElement is DOM element that you want to observe.
  2. options is an obejct with multiple coonfig options.
javascript
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.

Example:

javascript
<!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>
alt text

Important methods to consider

  • 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().

javascript
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);
}