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 theMutationObserver
. - 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
Reference
- “W3C DOM4 Mutation Observer.” W3C. Web. 19 June 2015
- “MutationObserver.” Mozilla Developer Network. Web. 19 June 2015.