Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are DOM Mutation Observers slower than DOM Mutation Events?

The following code utilize DOM Mutation Event DOMNodeInserted to detect the existence of the body element and wrap its innerHTML into a wrapper.

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        function DOMmanipulation() {
            if (document.body) {
                document.removeEventListener('DOMNodeInserted', DOMmanipulation);
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        }
        document.addEventListener('DOMNodeInserted', DOMmanipulation);
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

And despite the success of the wrapping, there is an error shows that a node was not found. This answer of a question explained that it is because when jQuery had been loaded, it added a div element into the body to do some tests, but it failed to remove that div element because that element has been wrapped into the wrapper so that it's not a child element of body anymore.

The above experiment tells us that DOMNodeInserted event is faster than jQuery's tests because jQuery's test element (div) got wrapped before it can be removed by jQuery.




Now the following code can achieve the same manipulation, and it's using the newly introduced DOM Mutation Observers. As of this time (2012-07-11), it works only on Chrome 18 and higher.

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        var observer = new WebKitMutationObserver(function() {  
            if (document.body) {
                observer.disconnect();
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        });
        observer.observe(document, { subtree: true, childList: true });
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

This codes didn't produce any error. That means jQuery is faster than DOM Mutation Observers, so it was able to remove its test element (div) before that element can be wrapped into the wrapper.




From the above two experiments, we find that when it comes to execution speed:

  • DOM Mutation Events > jQuery's tests
  • jQuery's tests > DOM Mutation Observers

Can this result appropriately prove that DOM Mutation Observers is slower than DOM Mutation Events?

like image 578
Ian Y. Avatar asked Jul 11 '12 03:07

Ian Y.


People also ask

When would you use a mutation observer?

MutationObserver can react to changes in DOM – attributes, text content and adding/removing elements. We can use it to track changes introduced by other parts of our code, as well as to integrate with third-party scripts.

How does mutation observer work?

MutationObserver is a Web API provided by modern browsers for detecting changes in the DOM. With this API one can listen to newly added or removed nodes, attribute changes or changes in the text content of text nodes.

How do I track changes in DOM?

“MutationObserver” is a Web API provided by modern browsers for detecting changes in the DOM. By using this API you can listen to changes in DOM, like added or removed nodes, attribute changes or changes in the text content of text nodes and make changes. Web apps are getting complex on the client-side nowadays.

What is a mutation event?

The MutationEvent interface provides event properties that are specific to modifications to the Document Object Model (DOM) hierarchy and nodes.


2 Answers

The simple answer is that mutation observers are asynchronous. They are sent whenever the engine feels like it, some time after the change has taken place and in some cases after a great many changes have taken place. That may be long after a DOM mutation event listener would have notified you, but meanwhile the engine has been free to get its work done without having to constantly create and bubble events for every piddling DOM change.

like image 131
Ian Nartowicz Avatar answered Oct 05 '22 22:10

Ian Nartowicz


DOM Mutation Observers, are not intended to be faster than DOM Mutation Events. Rather they are intended to be more efficient and safer.

The basic gist of the difference is that DOM Mutation Events fire whenever there is a change. So this code for example would create a callback loop, that will ultimately crash the browser.

document.addEventListener('DOMNodeInserted', function() {
    var newEl = document.createElement('div');
    document.body.appendChild(newEl);
});

The fact that they are called in this fashion and so often also has a significant effect on the browser, as it forces an interrupt between the browsers recalculate style, reflow and repaint cycle or worse forces the browser to recalculate styles, reflow and repaint on every callback. The problem is further exasperated by the fact that other code maybe executing that makes further changes to the DOM, which will continue to be interrupted by your callback.

What's more is that because events propagate in the same way as normal DOM Events, you're going to start hearing changes on elements that you might not care about or didn't account for in your code. So the whole mechanism of DOM Mutation Events can become troublesome to manage fairly quickly.

DOM Mutation Observers counteract these problems by, as the name suggests observing changes to the DOM and providing you with a report of all the changes that took place from from the start of the change. This is a much better situation to be in as it allows the browsers to notify you at a time that makes sense, for example when the document is idle and all other JavaScript that could make further changes has finished executing, or before the browser restarts the recalc / repaint cycle, so it can apply any changes you make, without having to repeat the cycle shortly after.

It also makes it easier for you to manage, because you can scan through all the changed elements to find what you're looking for, instead of writing lots of case handling code for stuff you don't care about, as was the situation with Mutation Events. And more importantly its only going to call it once, so you don't need to worry that any further changes are going to effect the elements i.e. they are no longer in changing state, they have changed.

So in answer to your question, DOM Mutation Observers are slower because they waited for jQuery to finish its manipulation of the DOM before it notified you of what jQuery changed. Which for the reason explained above and your example, proves it is safer more efficient solution ( you no longer cause an error), and you didn't really care that jQuery added something to the DOM because it would have removed it shortly after. With Observers you would have received a report detailing the jQuery element being added and removed.

This is still a bit troublesome however because you have to figure out what actually happened by matching up elements with all the changes that took place. The reality is that as far as you're concerned nothing happened ( the same element was added and removed ) so nothing has actually changed in the structure of the DOM. To help with this there is a little library called MutationSummary:

http://code.google.com/p/mutation-summary/

That calculates the net effect of the changes and only calls your callback passing in those changes. So in your case your callback would not have been called at all, because the net effect of the change was zero.

E.g. for the following you will only get one change. The body style was changed to left: 1000px. Even though I changed it in 1000 increments. The net effect of the change is only the difference between its initial value and its final one.

function moveBody() {
    for (var i = 0; i < 1000; i++) document.body.style.left = i + 'px';
}
moveBody();
like image 33
AshHeskes Avatar answered Oct 05 '22 23:10

AshHeskes