Voyeurism: Mutation and Object Observers

Warning This article was written over six months ago, and may contain outdated information.

I don’t write much in the way of pro­duc­tion-ready code at the moment, so some of the cool­er recent devel­op­ments in JavaScript have passed me by. In this post I want to address that with a look at a cou­ple of nice new(-ish) fea­tures: muta­tion observers and object observers.

I remem­ber read­ing about muta­tion observers a lit­tle while ago, but didn’t pay them too much atten­tion as they didn’t have broad brows­er sup­port and weren’t imme­di­ate­ly use­ful to me. When I recent­ly saw object observers land in Chrome (36) Beta, I realised that I should go back and learn about them. So I did.

Mutation Observers

Muta­tion observers watch for changes in an ele­ment — changes to an attribute, to char­ac­ter data, or to child nodes — then report back on the change. I’m sure I’ll get told off for say­ing this, but they’re kind of like event lis­ten­ers, only specif­i­cal­ly for changes to an element.

There are two steps to using muta­tion observers: first, cre­ate the observ­er and define what it should do when it observes a change; and sec­ond, ini­ti­ate the observ­er on the ele­ment that might change. The observ­er will then mon­i­tor the DOM for changes to the ele­ment, and fire the call­back func­tion when the change is observed. 

You cre­ate a new observ­er using the MutationObserver con­struct, which requires a call­back func­tion as an argu­ment. The call­back returns an array of objects with infor­ma­tion about the muta­tion. This code exam­ple shows each object in the array logged to the console:

var observer = new MutationObserver( function (mutations) {
  mutations.forEach( function (mutation) {
    console.log(mutation);
  });
});

You can inform the observ­er which muta­tion types it should watch out for; in this exam­ple, I’m con­fig­ur­ing the observ­er to observe changes in attrib­ut­es, to report the val­ues of those attrib­ut­es before they changed, and to watch all child elements:

var config = {
  attributes: true,
  attributeOldValue: true,
  subtree: true
};

The next step is to ini­ti­ate the obser­va­tion with the observe() method, pass­ing in two argu­ments: the ele­ment to be watched, and the con­fig­u­ra­tion para­me­ters defined above. It looks like this:

observer.observe(el, config);

Now the observ­er will fire the call­back when­ev­er I make a change to the attribute of my tar­get ele­ment. For exam­ple, if I update the classList of the ele­ment, like so:

el.classList.add('bar');

The observ­er will note this change to the class attribute and, when it’s able (it runs asyn­chro­nous­ly), run the call­back func­tion. This will, in turn, return a result that looks some­thing like this:

{
  type: "attributes",
  attributeName: "class",
  oldValue: "foo"
}

This tells me the type of muta­tion (attrib­ut­es), the attribute that was changed (class) and the val­ue of the attribute before the change (foo). Depend­ing on the type of change you want to observe, these results will be quite different.

I’ve made a sim­ple muta­tion observers demo on JSFid­dle. Results are logged to the con­sole. You’ll need IE11 or any oth­er mod­ern browser.

Muta­tion observers are super-use­ful if you’re writ­ing script that makes fre­quent changes to the DOM, and they aren’t oner­ous to per­for­mance as they run asyn­chro­nous­ly. There’s a more com­plete expla­na­tion of muta­tion observers on MDN. Imple­men­ta­tion is sol­id and sta­ble in Chrome and Fire­fox, and in Safari 6+ and IE11+.

By pure serendip­i­ty, Addy Osmani wrote a great intro­duc­tion to muta­tion observers last week.

Object Observers

Like muta­tion observers, object observers watch for changes; but where the for­mer watch ele­ments in the DOM, the lat­ter are focused sole­ly on JavaScript objects. The basic syn­tax is quite sim­i­lar, using the observe() method, but this time applied to the Object wrap­per. The method requires two argu­ments: the object to be observed, and the call­back func­tion that runs when a change to the object takes place.

Object.observe(myObj, function (changes) {
  changes.forEach( function (change) {
    console.log(change);
  });
});

Now it watch­es for changes. For exam­ple, you might add a new prop­er­ty to the object:

myObj.foo = 'bar';

When this hap­pens, the call­back func­tion is fired and returns an object that tells you the name of the prop­er­ty that changed, the type of change (add, update, etc), the pre­vi­ous val­ue if a val­ue were updat­ed, and an object con­tain­ing the updat­ed object.

I’ve put an object observ­er demo on JSFid­dle — the results are logged to the con­sole. As of right now, you’ll need Chrome Beta (36) to see this working.

This might be handy for reg­u­lar use, but it becomes down­right essen­tial for two-way data bind­ing — if you use MVC frame­works such as Angu­lar­JS or Back­bone, native data bind­ing with Object.observe() is going to give you a huge per­for­mance boost.

Addy Osmani (again) wrote a great intro­duc­tion to Object.observe() which helps to put it into full con­text, and is high­ly rec­om­mend­ed. Sup­port should land in Chrome 36; there is an open issue for Fire­fox sup­port, and IE says Object.observe() is ‘under con­sid­er­a­tion’.

Comments are closed.