How to Use MutationObserver API for DOM Node Changes

Here’s a scenario: Rita, a magazine writer is editing an article of hers online. She saves her changes, and sees the message “changes saved!” Just then, she notices a typo she missed. She fixes it and is about to click “save”, when she gets an angry phone call from her boss.

After the call is over, she turns back to the screen, sees “changes saved!” shutdowns her computer and storms out of the office.

Apart from my ineptitude for story-telling, we noticed from that short scenario what trouble that persistent message brewed. So, in future we decide to avoid it when possible and use one that either prompts user to acknowledge by clicking it – or vanishes on its own. Using the second one for a quick messages is a good idea.

We already know how to make an element disappear from a page, so that shouldn’t be a problem. What we need to know is when did it appear? So we can make it disappear after a plausible time.

MutationObserver API

Overall, when a DOM element (like a message div) or any other node changes, we should be able to know it. For a long time developers had to rely on hacks and frameworks due to the lack of a native API. But that had changed.

We now have MutationObserver (previously Mutation Events). MutationObserver is a JavaScript native object with a set of properties and methods. It lets us observe a change on any node like DOM Element, Document, Text, etc. By mutation, we mean the addition or removal of a node and changes to a node’s attribute & data.

Let’s see an example for better understanding. We’ll first make a set up where a message appears upon button click, like the one Rita saw. Then we’ll create and link a mutation observer to that message box and code the logic to auto hide the message. Savvy?

Note: You may at some point or have already asked me in your head “Why not just hide the message from inside the button click event itself in JavaScript?” In my example, I’m not working with a server, so of course the client is responsible to show the message and can hide it too easily. But like in Rita’s editing tool if the server is the one which decides to change the DOM content, the client can only stay alert and wait.

First, we create the setup to show the message on button click.

<div id="msg"></div><br />
<button>Show Message</button>
var msg = document.querySelector('#msg'),
  SUCCESSMSG = "Changes Saved!";

/* Add button click event */
document
.querySelector('button')
.addEventListener('click', showMsg);

function showMsg() {
  msg.textContent = SUCCESSMSG;
  msg.style.background = 'teal';
}

Once we got the initial setup running, lets do the following;

  • Create a MutationObserver object with a user-defined callback function (the function is defined later in the post). The function will execute on every mutation observed by the MutationObserver.
  • Create a config object to specify the kind of mutations to be observed by the MutationObserver.
  • Bind the MutationObserver to the target, which is the ‘msg’ div in our example.
(function startObservation() {
  var
    /* 1) Create a MutationObserver object*/
    observer = new MutationObserver(
      function(mutations) {      
      mutationObserverCallback(mutations);
    }),  
    /* 2) Create a config object */
    config = {childList: true};

  /* 3) Glue'em all */
  observer.observe(msg, config);
})();

Below is a list of properties for the config object identifying the different kinds of mutations. Since in our example we only deal with a child text node created for the message text, we’ve used the childList property.

Kinds of mutations observed

Property When set to true
childList Insertion and removal of the target’s child nodes are observed.
attributes Changes in target’s attributes are observed.
characterData Changes in target’s data are observed.

Now, to that callback function which gets executed on every observed mutation.

function mutationObserverCallback(mutations) {
  /* Grab the first mutation */
  var mutationRecord = mutations[0];
  /* If a child node was added, 
     hide the msg after 2s  */
  if (mutationRecord.addedNodes[0] !== undefined) 
    setTimeout(hideMsg, 2000);
}
function hideMsg() {
  msg.textContent = '';
  msg.style.background = 'none';
}

Since we’re only adding a message to the div, we’ll just grab the first mutation observed on it and check if a text node was inserted. If we got more than one change happening, we can just loop through the mutations array.

Every mutation in the mutations array is represented by the object MutationRecord with the following properties.

Properties of MutationRecord

Property Returns
addedNodes Empty array or array of nodes added.
attributeName Null or name of the attribute that was added, removed, or changed.
attributeNamespace Null or namespace of the attribute that was added, removed, or changed.
nextSibling Null or next sibling of the node that was added or removed.
oldValue Null or previous value of the attribute or data changed.
previousSibling Null or previous sibling of the node that was added or removed.
removedNodes Empty array or array of nodes that removed.
target Node targeted by the MutationObserver
type Type of mutation observed.

And… that’s it. we just have to put the code together for the final step.

Browser Support

Can I use Mutation Observer Chart
IMAGE: Can I Use.Web. 19 June 2015

Reference

FacebookTwitterInstagramPinterestLinkedInGoogle+YoutubeRedditDribbbleBehanceGithubCodePenEmail