Saturday, June 29, 2013

DOM MutationObserver Performance

TL;DR: jsPerf tests: DOM MutationObserver vs. CSS Animation vs. Mutation Events and DOM MutationObserver vs. CSS Animation vs. Mutation Events 2. The results are not as expected.

In developing a browser extension, I discovered that AJAX on modern web sites can sometimes make things tricky. I figured it would be nice to detect when certain parts of the page (DOM) were updated in order to interact with those parts.

The first thing I found was the DOMNodeInserted event (one of the DOM Mutation Events). But several articles mentioned that it kills page performance. And, according to Mozilla, it's deprecated. So I tried to find another solution.

The next thing I found was a way to detect node insertions using CSS animations, strangely enough. According to the article it performs better than DOMNodeInserted. But, it still seemed a bit hackish to me, so I kept searching.

Then, I discovered Mutation Observers. According to the few articles I read, it should have better performance than the deprecated mutation events. And to me it seems like a more consistent way of doing things than using CSS animation detection. Now, browser support is still limited, but since I would be using it for a browser extension, that was ok with me.

So I was curious, how does the performance of these three ways of detecting changes to the DOM actually compare? I tried a quick search but didn't find anything. Then I remembered I had recently stumbled on to some jsPerf tests that compared performance of different ways of doing things in JavaScript.

So I created my own jsPerf test: DOM MutationObserver vs. CSS Animation vs. Mutation Events

It ought to work in browsers that support MutationObserver or WebKitMutationObserver (Chrome 18+, Firefox 14+, IE 11+, Safari 6+ as of when this article was written).

MutationObserver seems to be fastest in my test runs. I expected that CSS animation detection would perform better. I don't know how closely this test correlates to real-world performance, but it's a start, and I'm open to suggestions to improve it.

On a side node, with the browser extension I was developing, I ended up going with a different strategy altogether (at least for now), which avoided the need to detect DOM changes at all. I may write another article about that at some point. But I may need mutation observers sometime in the future.

Update: I fixed an issue in my first jsPerf teardown methods and added a second jsPerf test case and now the results are different and not what I expected. They seem to vary a lot, and surprisingly, DOMNodeInserted is actually fastest in several cases. In most cases, CSS animation events are slowest. Does that mean there's something wrong with the way I'm testing? Could mutation observers and CSS animation events really be slower than advertised relative to mutation events? I doubt that's the case and I'm not convinced my two jsPerf test cases prove anything. But I would be interested to know what I am missing and to see a more accurate performance comparison between these means of detecting DOM updates.

Update 2: I think that the main performance difference between mutation observers vs. mutation events is not so much the performance of the events themselves, but the effect on available CPU for other tasks that need to run in the meantime. So perhaps a better test might be to measure the performance impact on a secondary task that runs at the same time. But I have not yet attempted such a test.