DOM MutationObserver vs. CSS Animation vs. Mutation Events (v2)

Revision 2 of this benchmark created by Mike Henry on


Preparation HTML

<div id="container"></div>

<style type="text/css">
@keyframes nodeInserted {  
	from { clip: rect(1px, auto, auto, auto); }
	to { clip: rect(0px, auto, auto, auto); }  
}
@-moz-keyframes nodeInserted {  
	from { clip: rect(1px, auto, auto, auto); }
	to { clip: rect(0px, auto, auto, auto); }
}
@-webkit-keyframes nodeInserted {  
	from { clip: rect(1px, auto, auto, auto); }
	to { clip: rect(0px, auto, auto, auto); }
}
@-ms-keyframes nodeInserted {  
	from { clip: rect(1px, auto, auto, auto); }
	to { clip: rect(0px, auto, auto, auto); }
}
@-o-keyframes nodeInserted {  
	from { clip: rect(1px, auto, auto, auto); }
	to { clip: rect(0px, auto, auto, auto); }
}

#container > p {
    animation-duration: 0.001s;
    -o-animation-duration: 0.001s;
    -ms-animation-duration: 0.001s;
    -moz-animation-duration: 0.001s;
    -webkit-animation-duration: 0.001s;
    animation-name: nodeInserted;
    -o-animation-name: nodeInserted;
    -ms-animation-name: nodeInserted;        
    -moz-animation-name: nodeInserted;
    -webkit-animation-name: nodeInserted;
}
</style>

<script>
console.log("preparation");
var COUNT = 100;
var globalDeferred;
var container = document.getElementById("container");

var isExpectedNode = function(node) {
  return node.nodeName.toUpperCase() === "P";
};

var handleNodeFound = function() {
  globalDeferred.resolve();
  globalDeferred = null;
};

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (mutation.type === "childList" && mutation.addedNodes !== null) {
      for (var i = 0; i < mutation.addedNodes.length; i++) {
        if (isExpectedNode(mutation.addedNodes[i]))
          handleNodeFound();
      }
    }
  });
});

var animationListener = function(event) {
  if (event.animationName === "nodeInserted" &&
      isExpectedNode(event.target))
    handleNodeFound();
};

var nodeInsertedListener = function(event) {
  if (isExpectedNode(event.target))
    handleNodeFound();
};

// Note: child element must be created each time it's appended
var runTest = function(count, deferred) {
  globalDeferred = deferred;
  for (var i = 0; i < count; i++) {
    container.appendChild(document.createElement("span"));
  }
  container.appendChild(document.createElement("p"));
};

// Note: cannot use global setup when using ui.benchmarks[i].setup
ui.benchmarks[0].setup = function() {
  console.log("setup[0]");
  observer.observe(container, {
    childList: true,
    subtree: true
  });
};

ui.benchmarks[1].setup = function() {
  console.log("setup[1]");
  container.addEventListener("animationstart", animationListener, false);
  container.addEventListener("MSAnimationStart", animationListener, false);
  container.addEventListener("webkitAnimationStart", animationListener, false);
};

ui.benchmarks[2].setup = function() {
  console.log("setup[2]");
  container.addEventListener("DOMNodeInserted",
    nodeInsertedListener, false);
};

ui.benchmarks[0].teardown = function() {
  console.log("teardown[0]");
  observer.disconnect();
  container.innerHtml = "";
};

ui.benchmarks[1].teardown = function() {
  console.log("teardown[1]");
  container.removeEventListener("animationstart", animationListener, false);
  container.removeEventListener("MSAnimationStart", animationListener, false);
  container.removeEventListener("webkitAnimationStart",
    animationListener, false);
  container.innerHtml = "";
};

ui.benchmarks[2].teardown = function() {
  console.log("teardown[2]");
  container.removeEventListener("DOMNodeInserted",
    nodeInsertedListener, false);
  container.innerHtml = "";
};
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
MutationObserver
// async test
runTest(COUNT, deferred);
ready
CSS Animations
// async test
runTest(COUNT, deferred);
ready
Mutation Events (DOMNodeInserted)
// async test
runTest(COUNT, deferred);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.