SHARE
Facebook X Pinterest WhatsApp

Respond to DOM Changes with Mutation Observers

Written By
thumbnail
Rob Gravelle
Rob Gravelle
Jun 23, 2015

Respond to DOM Changes with Mutation Observers

Earlier this year, the World Wide Web Consortium (W3C) introduced the DOM4 specification, which includes DOM Mutation Observers. These replace the long-standing DOM Mutation Events, which never really caught on due to their slowness, verbosity, and propensity to crash the browser! Unlike Mutation Events, which fired an event for every change to the DOM, the new Mutation Observers utilize a far superior asynchronous callback mechanism that can be invoked after multiple changes in the DOM. In today’s article, we’ll learn how to create and configure DOM Mutation Observers as well as interpret their results.

What Mutation Events/Observers Do

Both Mutation Events and Mutation Observers serve the same purpose, which is to inform your script of changes to the DOM structure. In fact, the word “mutation” is a synonym for “change”. As such, it closely resembles the “mutator” moniker ascribed to setter functions.

Mutation Events relied on special DOM-specific event identifiers that you could attach an event listener to:

var insertedNodes = [];
document.addEventListener("DOMNodeInserted", function(e) {
        insertedNodes.push(e.target);
    },
    false);
console.log(insertedNodes);

Mutation Observers have their own constructor that accepts a callback function. Once instantiated, the MutationObserver instance’s observe() method can be invoked a specific element, or on the entire document, as in the example below:

var insertedNodes = [];
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        for (var i = 0; i < mutation.addedNodes.length; i++) {
            insertedNodes.push(mutation.addedNodes[i]);
        })
});
observer.observe(document, { childList: true });
console.log(insertedNodes);

Mutation Event Types

The mutation event types of Mutation Events are conspicuously absent from the MutationObserver constructor. Instead of receiving the mutation event types up-front, the MutationObserver monitors a given node for all mutator events and passes them to the callback function via a property of the function argument called “type”. More on that in a bit…

The second argument is an options object of boolean attributes. Here is a list and description of each:

  • childList: Set to true if additions and removals of the target node’s child elements (including text nodes) are to be observed.
  • attributes: Set to true if mutations to target’s attributes are to be observed.
  • characterData Set: to true if mutations to target’s data are to be observed.
  • subtree: Set to true if mutations to not just target, but also target’s descendants are to be observed.
  • attributeOldValue: Set to true if attributes is set to true and target’s attribute value before the mutation needs to be recorded.
  • characterDataOldValue: Set to true if characterData is set to true and target’s data before the mutation needs to be recorded.
  • attributeFilter: Set to an array of attribute local names (without namespace) if not all attribute mutations need to be observed.

MutationObserver Results

Each MutationObserver instance maintains an array of MutationRecords which is passed to the observe() method’s callback function as the first parameter. Since the structure of the MutationRecord remains constant regardless of the mutation type reported, not all attributes apply to every event. In fact, the majority of them will be null or contain an empty object. For now, let’s take a look at each attribute and what kind of information they contain. In the next section, we’ll look at a real-life example of MutationRecords:

  • type (String): Returns attributes if the mutation was an attribute mutation, characterData if it was a mutation to a CharacterData node, and childList if it was a mutation to the tree of nodes.
  • target (Node): Returns the node the mutation affected, depending on the type. For attributes, it is the element whose attribute changed. For characterData, it is the CharacterData node. For childList, it is the node whose children changed.
  • addedNodes (NodeList): Return the nodes added. Will be an empty NodeList if no nodes were added.
  • removedNodes (NodeList): Return the nodes removed. Will be an empty NodeList if no nodes were removed.
  • previousSibling (Node): Return the previous sibling of the added or removed nodes, or null.
  • nextSibling (Node): Return the next sibling of the added or removed nodes, or null.
  • attributeName (String): Returns the local name of the changed attribute, or null.
  • attributeNamespace (String): Returns the namespace of the changed attribute, or null.
  • oldValue (String): The return value depends on the type. For attributes, it is the value of the changed attribute before the change. For characterData, it is the data of the changed node before the change. For childList, it is null.

MutationRecord Type Attribute

The first attribute that you’ll want to check is the type since it’s the one that determines what other attributes may be useful to you. There are three types to look for:

  • “attributes”: An attribute changed on the target node.
  • “characterData”: The text content (a child text node) value of the target node changed.
  • “childList”: A child (or subtree descendent) of the target node was added or removed.

A Working Example

Here is some code to help understand how all of the above components fit together. This MutationObserver monitors the entire document for mutations and outputs their details to console:

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log('Mutation type: ' + mutation.type);
    if ( mutation.type == 'childList' ) {
      if (mutation.addedNodes.length >= 1) {
        if (mutation.addedNodes[0].nodeName != '#text') {
           console.log('Added ' + mutation.addedNodes[0].tagName + ' tag.');
        }
      }
      else if (mutation.removedNodes.length >= 1) {
        console.log('Removed ' + mutation.removedNodes[0].tagName + ' tag.')
      }
    }
    else if (mutation.type == 'attributes') {
      console.log('Modified ' + mutation.attributeName + ' attribute.')
    }
        });   
});
 
var observerConfig = {
        attributes: true,
        childList: true,
        characterData: true
};
 
// Listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);

// Let's add a sample node to see what the MutationRecord looks like
document.body.appendChild(document.createElement('ol'));
document.body.removeChild(document.querySelector('ol'));
document.body.setAttribute('id', 'booooody');

Upon running the above code, you should see something like the following in the browser console:

Mutation type: childList
Added OL tag.
Mutation type: childList
Removed OL tag.
Mutation type: attributes
Modified id attribute.
Mutation type: childList

Conclusion

According to caniuse.com, the MutationObserver is supported by most major browsers, with the exception of Opera Mini. A couple of points to bear in mind: the WebKitMutationObserver of iOS 6 has a known bug that it doesn’t trigger “childList” changes until you bind one of the deprecated mutation events such as “DOMNodeInserted” to the node which is watched by observer. Also, IE 11 does not include the child nodes when they are removed by setting the innerHTML property of an element.

Recommended for you...

The Revolutionary ES6 Rest and Spread Operators
Rob Gravelle
Aug 23, 2022
Ahead of Time (AOT) Compilation in Angular
Tariq Siddiqui
Aug 16, 2022
Converting a JavaScript Object to a String
Rob Gravelle
Aug 14, 2022
Understanding Primitive Type Coercion in JavaScript
Rob Gravelle
Jul 28, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.