Sometimes, you don’t have much control over a site’s code. Maybe you’re working on a client site built with Squarespace, or with Wordpress + plugins + more plugins. When you want to make an enhancement or change a behavior in these situations, Javascript is usually the tool we turn to. But sometimes, even the usual Javascript hooks aren’t available.

For example, one common pattern is to wait for a page’s DOM content to be loaded, then access some element on the page, and make a change to it. But what if the element you want to access is loaded dynamically, after the page’s load event has fired?

Or perhaps you want to react to something that happens on a page, but the developer of whatever code is triggering that something hasn’t published a friendly list of events you can listen to. Maybe there isn’t even a proper event fired (shocking!).

Enter MutationObserver, stage left

We can turn to the well-supported MutationObserver for a little help. It’s easy to understand and implement, and is supported by modern browsers post-IE10. It’s pretty awesome, really.

The general pattern is to:

create a new observer tell it what to do when mutations happen direct it to observe the element you want, with certain options:

// DOM element we want to observe

var targetNode = ...; // Options for the observer

var config = { ...options... }; // Callback will execute when mutations are observed

var callback = function(mutationsList){ ...do stuff... }; // Create a new observer, passing in the callback function

var observer = new MutationObserver(callback); // Start observing the targetNode with the given configuration

observer.observe(targetNode, config);

The observer configuration has several options:

Most importantly, it describes what to observe : childList , attributes , characterData , or any combination.

: , , , or any combination. If you’re monitoring attributes, you can additionally specify an attributeFilter , which limits the observed attributes to those in the array you provide.

, which limits the observed attributes to those in the array you provide. If you’re watching attributes or character data, you can record the pre-mutation values by setting attributeOldValue or characterDataOldValue options to true , respectively.

or options to , respectively. By default, a mutation observer will only observe the node you specify; to watch the entire subtree it contains, set the subtree option to true .

So a configuration might look something like:

var config = {

attributes: true,

attributeOldValue: true,

attributeFilter: ['class'],

subtree: true

}

The above would configure our observer to watch for changes to the class attribute on our target node and any descendant nodes, and keep track of the pre-mutation value.

Manipulating dynamically-loaded DOM content