How JS Works: Tracking Changes in the DOM Using MutationObserver

Original author: Alexander Zlatkov
  • Transfer
Today, in the translation of the tenth material from the series devoted to the features of the work of JavaScript mechanisms, we will talk about how to track changes in the DOM using the MutationObserver API.

The client parts of web applications are becoming more complex, require more system resources. This happens for various reasons, in particular due to the fact that such applications need advanced interfaces, due to which their capabilities are revealed, and due to the fact that they have to perform complex calculations on the client side.



All this leads to a complication of the task of monitoring the state of application interfaces in the process of their life cycle. This task becomes even larger if we are talking about developing something like a framework or even a regular library, when, for example, you need to react to what is happening with the page and perform some actions that depend on the DOM.


Overview


MutationObserver is a Web API provided by modern browsers and designed to detect changes in the DOM. Using this API, you can observe the addition or removal of DOM nodes, the change in the attributes of elements, or, for example, the change in the text of text nodes. Why is this needed?

There are many situations in which an API MutationObservercan be very helpful. For instance:

  • You need to notify the user of the web application that some changes have occurred on the page with which he is working.
  • You are working on a new interesting JS framework that dynamically loads JavaScript modules based on DOM changes.
  • Perhaps you are working on a WYSIWYG editor and are trying to implement undo and redo functionality. Using the API MutationObserver, you will at any time know what changes have occurred on the page, which means that you can easily undo them.


Browser-based text editor The

above are just a few of the situations in which features MutationObservermay be useful. In fact, there are many more.

How to use MutationObserver


Use MutationObserverin web applications is quite simple. You need to create an instance MutationObserverby passing a function to the appropriate constructor, which will be called every time changes occur in the DOM. The first argument to the function is a collection of all the mutations that occurred in a single package. For each mutation, information is provided on its type and on the changes that it represents.

var mutationObserver = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation);
  });
});

The created object has three methods:

  • The method observestarts the DOM change tracking process. It takes two arguments - the DOM node to watch for, and the object with the parameters.
  • The method disconnectstops monitoring changes.
  • The method takeRecordsreturns the current queue of the instance MutationObserver, after which it clears it.

Here's how to enable change monitoring:

// Запускаем наблюдение за изменениями в корневом HTML-элементе страницы
mutationObserver.observe(document.documentElement, {
  attributes: true,
  characterData: true,
  childList: true,
  subtree: true,
  attributeOldValue: true,
  characterDataOldValue: true
});

Now suppose the DOM has the simplest element div:

Simple div

Using jQuery, you can remove the attribute classfrom this element:

$("#sample-div").removeAttr("class");

Due to the fact that we started monitoring changes by calling mutationObserver.observe(...)it first and the function that responds to the arrival of a new change package displays the received data to the console, we will see the contents of the corresponding MutationRecord object in the console :


MutationRecord Object

Here you can see mutations caused by the removal of the attribute class.

And finally, in order to stop monitoring the DOM after the work is completed, you can do the following:

// Прекратим наблюдение за изменениями
mutationObserver.disconnect();

MutationObserver support in various browsers


The API MutationObserveris widely supported in browsers:


MutationObserver Support

Alternatives to MutationObserver


It is worth noting that the DOM change observation mechanism that it offers MutationObserverwas not always available to developers. What did they use before they appeared MutationObserver? Here are a few options:

  • Polling.
  • The mechanism MutationEvents.
  • CSS animation.

▍ Survey


The simplest and most straightforward way to track DOM changes is to poll. Using the method, setIntervalyou can schedule periodic execution of a function that checks the DOM for changes. Naturally, using this method significantly reduces the performance of web applications.

▍MutationEvents


The MutationEvents API was introduced in 2000. Despite the fact that this API allows you to solve the tasks assigned to it, mutation events are raised after each DOM change, which, again, leads to performance problems. The API MutationEventsis now deprecated and soon modern browsers will no longer support it.


MutationEvents Support

▍CSS animation


In fact, an alternative MutationObserverin the form of CSS-animations can seem a bit strange. And here is the animation? In general, the idea here is to create an animation that will be called after the element is added to the DOM. When the animation starts, an event will be triggered animationstart. If you assign a handler for this event, you can find out the exact time the new item was added to the DOM. In this case, the animation execution time should be so short that it is almost invisible to the user.

In order to use this method, we first need the parent element, for which we want to observe the addition of new nodes:


To organize monitoring of adding nodes to it, you need to configure a sequence of key frames of CSS animation that will start when you add a node:

@keyframes nodeInserted { 
 from { opacity: 0.99; }
 to { opacity: 1; } 
}

After creating keyframes, the animation should be applied to the elements that need to be watched. Pay attention to the duration of the animation. It is very small, so the animation is almost invisible.

#container-element * {
 animation-duration: 0.001s;
 animation-name: nodeInserted;
}

Here we add animation to all descendant nodes of the element container-element. When the animation ends, the corresponding event is fired.

Now we need a JS function that will play the role of an event handler. Inside the function, first of all, it is necessary to perform a check event.animationNamein order to make sure that this is exactly the animation that interests us.

var insertionListener = function(event) {
  // Убедимся в том, что это именно та анимация, которая нас интересует
  if (event.animationName === "nodeInserted") {
    console.log("Node has been inserted: " + event.target);
  }
}

Now add an event handler to the parent element. Different browsers do this differently:

document.addEventListener("animationstart", insertionListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertionListener, false); // IE
document.addEventListener("webkitAnimationStart", insertionListener, false); // Chrome + Safari

This is how CSS animations work in various browsers.


Support for CSS animations in various browsers

Summary


We looked at APIs MutationObserverand alternative ways to monitor DOM changes. It should be noted that it MutationObserverhas many advantages over these alternatives. In general, we can say that this API is able to report any changes that may occur in the DOM, that it is well optimized, giving information about the changes collected in packages. In addition, the API MutationObserverenjoys the support of all major modern browsers, and there are polyfills for it based on MutationEvents.

The author of the material notes that it MutationObserveroccupies a central place in the SessionStack library , which is aimed at organizing the collection of data on what is happening with web pages.

Previous parts of the series of articles:

Part 1:How JS works: an overview of the engine, run-time mechanisms, call stack
Part 2: How JS works: about the V8 internal device and code optimization
Part 3: How JS works: memory management, four types of memory leaks and how to deal with them
Part 4: How it works JS: event loop, asynchrony, and five ways to improve your code with async / await
Part 5: How JS: WebSocket and HTTP / 2 + SSE work. What to choose?
Part 6: How JS Works: Features and Scope of WebAssembly
Part 7: How JS Works: Web Workers and Five Usage Scenarios
Part 8: How JS Works: Service Workers
Part 9: How JS Works: Web Push Notifications

Dear readers! Do you use MutationObserver in your projects?


Also popular now: