MutationObserver API

By  on  

One of my favorite web tricks was using CSS and JavaScript to detect insertion and removal of a DOM node, detailed in Detect DOM Node Insertions with JavaScript and CSS Animations.  The technique and blog post were published during a time when we didn't have a reasonable API for detecting such events.  Nowadays we have MutationObserver, an API made to efficiently detect loads of node operations.  Let's have a look!

Basic MutationObserver API

The MutationObserver API is a bit complicated for me but here's the basic setup:

var observer = new MutationObserver(function(mutations) {
	// For the sake of...observation...let's output the mutation to console to see how this all works
	mutations.forEach(function(mutation) {
// Notify me of everything!
var observerConfig = {
	attributes: true, 
	childList: true, 
	characterData: true 
// Node, config
// In this case we'll listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);

There's a lot to using the MutationObserver, but the breakdown is:

  • Create an instance of MutationObserver with a callback to handle any event thrown its way
  • Create a set of options for the MutationObserver
  • Call the observe method of the MutationObserver instance, passing it the node to listen to (..and its children) and the option list.
  • At the time you want to stop observing, call disconnect

MutationObserver Options

MDN provides detail on the options for MutationObserver:

  • 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.

That's a lot to be aware of when listening to one node and/or child nodes!

MutationRecord:  MutationObserver Handler Results

The resulting object when a mutation is observed is detailed as well:

  • 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.

Whew.  So let's take look at some realistic use cases of MutationObserver.

Detect When a Node is Inserted

The use case in my Detect DOM Node Insertions with JavaScript and CSS Animations post was detecting node insertion, so let's create a snippet which detects node insertion:

// Let's add a sample node to see what the MutationRecord looks like
// document.body.appendChild(document.createElement('li'));

	addedNodes: NodeList[1], // The added node is in this NodeList
	attributeName: null,
	attributeNamespace: null,
	nextSibling: null,
	oldValue: null,
	previousSibling: text,
	removedNodes: NodeList[0],
	target: body.document,
	type: "childList"

The resulting MutationRecord shows addedNodes: NodeList[1], meaning that a node has been added somewhere lower in the tree.  The type is childList.

Detect When a Node is Removed

Removing a node shows the following MutationRecord:

// Now let's explore the MutationRecord when a node is removed
// document.body.removeChild(document.querySelector('div'))

	addedNodes: NodeList[0],
	attributeName: null,
	attributeNamespace: null,
	nextSibling: text,
	oldValue: null,
	previousSibling: null,
	removedNodes: NodeList[1], // The removed node is in this NodeList
	target: body.document,
	type: "childList"

This action also shows a type of childList but now removeNodes now has the NodeList[1], a NodeList with the removed node.

Detect Attribute Changes

If an attribute is changed on any element, you'll be quick to know about it; the MutationRecord will show:

// What do attribute changes look like?
// document.body.setAttribute('id', 'booooody');

	addedNodes: NodeList[0],
	attributeName: "id",
	attributeNamespace: null,
	nextSibling: null,
	oldValue: null,
	previousSibling: null,
	removedNodes: NodeList[0],
	target: body#booooody.document,
	type: "attributes"

Also note that the target will show the node for which the attributes was changed.  The oldValue will display its former value and while a normal getAttribute check give you the new attributes value.

Stop Listening!

If you're looking to write the ultimate efficient app, you'll only be adding listeners for the period you need them and then removing them when you're done:


The MutationObserver instance has a disconnect method to stop listening.  Since your app may have many, many DOM operations, you may want the power to disconnect your listener for the duration of the time your user interacts with the page.

The MutationObserver API seems a tad verbose but it's powerful, informative, and ultimately hack free.  Daniel Buchner's brilliant original "hack" provides better support for node addition and removal but MutationObserver should probably be used if possible.

Recent Features

  • By
    9 Mind-Blowing Canvas Demos

    The <canvas> element has been a revelation for the visual experts among our ranks.  Canvas provides the means for incredible and efficient animations with the added bonus of no Flash; these developers can flash their awesome JavaScript skills instead.  Here are nine unbelievable canvas demos that...

  • By
    7 Essential JavaScript Functions

    I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should...

Incredible Demos

  • By
    Select Dropdowns, MooTools, and CSS Print

    I know I've harped on this over and over again but it's important to enhance pages for print. You can do some things using simple CSS but today's post features MooTools and jQuery. We'll be taking the options of a SELECT element and generating...

  • By
    MooTools dwCheckboxes Plugin

    Update / Fix: The checkboxes will no longer toggle when the "mouseup" event doesn't occur on a checkbox. Every morning I wake up to a bunch of emails in my Gmail inbox that I delete without reading. I end up clicking so many damn checkboxes...


  1. Do you know of a way to use MutationObserver to detect when an element’s dimension (width or height) changes? The best cross-browser solution I’ve seen so far is Cross-Browser, Event-based, Element Resize Detection, but that was published two years ago and I wonder if someone has come up with a better solution since.

  2. dharini

    I am using mutation observer on a target node. It fires if any of the child nodes or properties changes.But, I have a case where i had to handle if the target node itself is removed. This is not working.Is there a way to handle this?

  3. Guy Thomas

    I wondered what the performance difference would be between the CSS animation trick and the MutationObserver API.

    So I created a rough jsbin to test the two approaches:

    MutationObserver is consistently faster.

  4. your overview regarding MutationObserver is pretty neat, thanks for that!

    one thing though bugs me: why does the property change “checked” on an input field not get tracked?

    i’ve created a fiddle to illustrate this: enabling/disabling the input field gets tracked fine but checking/unchecking the input field does not.

    this makes no sense to me, maybe someone can explain what is going on here…thanks!

    • Stuart Simon

      You don’t use MutationObservers for that purpose. You use the onchange event.

    • the onchange event does not get triggered though when the state of the input (checked/unchecked) changes programmatically. it only works if a user clicks on the input…

  5. Nice example code, just about the only example online I could find that actually worked correctly. Thanks.

  6. Paul

    Thanks so much for this post!

  7. Very helpful post that actually helps me understand what’s going on with MutationObserver! The only hiccup is that after executing the code provided, I was only seeing the text “childList” in the console. It didn’t take any effort at all to realize that the sample was logging “mutation.type” instead of “mutation”. Made the quick change (in order to observe the records!) and I was off to the races.

    Thanks for the article, David!

  8. oliver

    Maybe i’m being dense…but in what sort of situations would you actually need to use this? Won’t it be my own functions that are changing the DOM in the first place?

  9. bojan

    **Oliver** , you would use this constructor if you are adding your project to some 3rd party library or some already made site/app and you want to know when something changes in DOM. Knowing this can help you to trigger your functions and add your changes.


    you are waiting for clients page to make some change (lets say function that waits for XHR response in form of JSON that adds some new piece to DOM structure) , by knowing when it was triggered you are able to append your changes after knowing it will not be overwritten.

    Lets, say you want to add a ‘click’ event to some button, but that button gets changed in process of loading because some other library is making changes after page load, so you can wait for this change and on it add your code.

    P.S. this is how I see usage of observer

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!