Skip to content

MutationObserver & Monitoring Changes to the DOM

Posted on:November 10, 2017 at 08:00 AM

Contents

What is a MutationObserver in JavaScript?

As the name suggests, the purpose of mutation observers is, well, to observe. Specifically, DOM elements. And look for mutations. In the DOM elements.

i am shocked and i want details

More specifically, it’s a built-in object type that’s intended to handle changes performed on the DOM. When an element under observation changes in the DOM, a new MutationRecord is created which carries with it data about the nature of that change. In practice, I only used it to get information on what had been changed via console log.

let theShadowKnows = new MutationObserver(mutation => {
  mutation.forEach(mutant => console.log(mutant));
});

We first have to initialize a new instance of the MutationObserver class. That requires one argument - a callback function. In the above case, it’s just a console log of a MutationRecord object which is generated when the conditions are met for the observation.

<body markdown="0">
  <div class="no" id="test">
    <h1>Our glorious H1 tag</h1>
    <p id="fun-text">some fun text here.</p>
    <p>some not so fun text here.</p>
    <p>a third paragraph element.</p>
    <p id="pro-seo-text" class="super-valuable">
      optimizing for the term 'glorious H1 tag' and will hopefully rank by 2019.
    </p>
  </div>
</body>

Let’s say that we are given the above HTML. In order to start observing, we need a target and a configuration object (MutationObserverInit) that tells the Observer what should be generated in the MutationRecord instance when something changes in the DOM target.

let t = document.getElementById('pro-seo-text');
let conf = {
  attributes: true,
  childList: true,
  characterData: true,
  attributeOldValue: true,
};

The MDN documentation on MutationObserver indicates that you need to have one of the first three keys present and set to true in the “conf” (MutationObserverInit) object above set to true or else it won’t be valid. Without one of those tree, a TypeError is thrown.

To start observing, we pass in the target and conf to the .observe method of the instance we created. Once that has happened without incident, we can see the MutationRecords as they are logged to the browser console (at least in my current callback function, that is).

If we wanted to unbind the observer, effectively killing it, we would only need to call:

theShadowKnows.disconnect();

Okay, but WHY Use a MutationObserver?

For a little while, I struggled to find real-world use cases for MutationObservers until I found this article on Eager.io which details (and even demos one) real world value. In particular, observers can capture element as they hit the DOM before they are rendered to make changes that a user wouldn’t see but would have a great impact, like optimizing images or a similar value-add situation.

In essence, it seems that a big part of the value of a MutationObserver is that it monitors changes in the DOM itself like attribute changes/additions, new children, new nodes, etc. It’s less driven (out of the box, at least) by interaction with those elements on the page.

One other important factor reported in this post on the Opera dev blog is that an observers are more efficient in some respect than event handlers. In an example given to add 2500 document fragments to a document, the claim is that the event handler is invoked 2500 times but the observer’s callback only once. Sadly, the test that was originally used to demonstrate that fact no longer works to confirm.

MutationObserver Performance: May Not Be Great for Large Applications?

Last thing I wanted to talk about, particularly in the context I mentioned earlier (monitoring all nodes as added to DOM), observing mutations is potentially very expensive as it could happen hundreds of times a second in the correct context. The expense will be dependent on the complexity and the size of the node tree we’re observing. The accepted answer in this StackOverflow question has some pretty rigorous suggestions for optimization, but for my purposes, this topic requires more study and some benchmarking on an actual application to better determine its viability.

That said, it seems that there is occasionally some confusion between MutationObserver and the now-deprecated Mutation event. Performance of the Mutation event was not great; MutationObserver performance, according to a Chrome blog post from way back in 2012, suggests they MutationObservers are, at a minimum, “faster” than Mutation Events.