Listening to the DOM changes with MutationObserver

Abbeal
abbeal’s tech blog
6 min readFeb 19, 2019
Photo by Eric Perez on Unsplash

Until a few months ago, the word MutationObserver was still unknown to me. Through this article, I would like to share my discovery about this DOM API.

Why and what’s the use of the MutationObserver ?

Websites user interfaces evolved a lot. A fast, dynamic and personalized website has become essential to create unique value and give the best experience possible.

The feedback given to the user through interaction with our website became so important that we had to find clever ways to play with the DOM without destroying performance.

Yes, working with the DOM has a cost, so we have to be careful. One of the answers to ease the development of more dynamic website is the MutationObserver.

The MutationObserver is a solution born to meet the need of listening to changes in the DOM and react to them in a performant way.

Here’s the definition from MDN :

The MutationObserver interface provides the ability to watch for changes being made to the DOM tree.

Without virtual DOM or hacky setTimeout, we have a powerful way to interact with the DOM.

We can observe changes on a specific part of the DOM and be notified to changes such as adding/deleting nodes and do whatever we want.

Isn’t it wonderful ? 😃

Another cool thing is that it’s widely supported by browsers. 🙏

Browser compatibility from MDN

Ok, that’s great, but now how to use it ?

Here’s the interface from the DOM specification :

Interface MutationObserver

The interface is pretty straightforward and it’s very well explained in the DOM specification. I highly encourage you to take some time to take a look in order to better understand what’s coming next.

Now let’s see what the implementation looks like. You’ll see that setting it up is child’s play 👍.

MutationObserver template

What’s happening step by step :

  1. The first thing to do is to get the DOM node we want to observe.
  2. In the interface, we can see Constructor(MutationCallback callback). To create a new instance of MutationObserver we need a callback function. The callback will trigger when changes occurs to the target. We’ll go more in details later. 😃
  3. We can now create a new instance of MutationObserver and sets its callback. 🙌
  4. At this point, this is only setup and nothing will happen. To observe our target, MutationObserver expose this function : void observe (Node target, optional MutationObserverInit options); We have our target and an instance of MutationObserver. The last piece missing before calling the function is the second parameter, that we’ll also discover more in details later. As you might already guess, this parameter will allow us to customize more what changes we want to observe in our target.
  5. And now the last step ! We just need to call the function observe through our MutationObserver instance and sets its two parameters : the node target to observe and the options.

Callback function

Understanding the callback function is very important as it’s there that you will instruct what to do after changes happened.

Let’s take a look at the interface to get more information about MutationCallback.

callback MutationCallback = void (sequence<MutationRecord> mutations, Mutation observer);

The callback function has two parameters. The first parameter is an array of MutationRecord and the second is the observer instance. The first parameter is always an array even if there’s one change. So, we will have to loop on the array to work on it.

Huh ? Why is it an array even if there is only one change ?

We have an array of MutationRecord because MutationObserver works asynchronously as it’s more efficient this way. The callback will not be fired until the DOM has finished changing. So at a given time, all mutations will be “collected” in an array. So be careful, if the DOM change at different time, it can trigger the callback at each mutation.

What’s a MutationRecord ?

Interface MutationRecord

A MutationRecord is an object that represents an individual DOM mutation. The object has 9 readonly properties and their value can be null depending on your config object given as second parameter of the observe function we saw earlier.

More information about what the 9 properties here.

MutationObserverInit

MutationObserverInit is an object describing the configuration of a mutation observer. It mainly consist of boolean with one exception that is attributeFilter.

As we know now, when using the observe function, there’s a second parameter MutationObserverInit we need to set. With this second parameter, we have to specify what we want to look for like an attribute change. We will then retrieve it in the callback in the MutationRecord properties.

One of the properties childList, attributes and characterData must be true otherwise a TypeError exception will be thrown.

More information about the config properties here.

What about an example ?

Colors example HTML template
Colors example JS Template

Here’s the codesanbox. 👍 (don’t forget to display the console)

There’s 3 div elements : Green, Blue, Dynamic. Each div will be bind with click event.

In the config object, we want to look only for attributes changes and also get the attribueOldValue. Notice the usage of attributeFilter. 😃

When clicking on Green and Blue, we log their class color but the callback function from our MutationObserver instance do not trigger even when adding or removing class attribute value. It’s because the target we give to our observe function is the div with “Dynamic” class. 😏

When clicking on Dynamic, class and id attribute changes. The callback function is finally triggered BUT you can see that there’s only one mutation even though there’s is two attributes that changed. Remember our config object and the attributeFilter. Only a change in class attribute will be observed !

Try removing the attributeFilter from the code and try to guess what the console will display. 😉

This is only the beginning

MutationObservers are triggered by changes in the DOM itself, not by events generated either via JS or user interaction.

We just discovered the tip of the iceberg. I think there’s a lot more interesting things going behing the scene. I didn’t go that far in my research but I’ll be glad if there’s someone who knows more to share his/her knowledge.

Also, I didn’t talk about disconnect(); and takeRecords();

If possible, disconnect your observer anytime you can. As for takeRecords(), I didn’t find a situation where I needed it and to be honest I don’t know much about it. 😅

Here’s the MDN documentation if you want to take a look.

MutationObserver is really powerful and there’s is a lot of possibilities depending on your situation and creativity.

Now you have everything you need to play with the MutationObserver and go beyond what you learnt in this article ! 🚀

Thanks for reading and see you soon.

Alexandre LIM, developer at Abbeal.

--

--

Abbeal
abbeal’s tech blog

Abbeal est une communauté d’apicodeurs partageant leur savoir faire autour des stacks modernes.