Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="[Profiles performance of CSS animation trick V MutationObserver for detecting dom node insertion]">
  <script src="https://code.jquery.com/jquery-2.2.4.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin - CSS animation V mutationObserver</title>
<style>
  #execution-times, #append-area{
    border:1px solid grey;
    height:8em;
    overflow:auto;
    margin-top:1em;
    width:auto;
  }
  </style>
  <script>
var domInsertionListener = function (selector, callback) {
    function addListenerCSS() {
        if (!$('head style#domnodelistener').length) {
            var css = '<style id="domnodelistener">'+
                '@keyframes nodeInserted {  '+
                'from { opacity: 0.99; }' +
                'to { opacity: 1; }  ' +
                '}'+
                '</style>';
            $('head').append(css);
        }
    }
    function addSelectorCSS(selector) {
        if (!$('head style[data-selector="'+selector+'"]').length) {
            var css = '<style data-selector="'+selector+'">' +
                selector + '{' +
                'animation-duration: 0.0001s;' +
                'animation-name: nodeInserted;' +
                '};' +
                '</style>';
            $('head').append(css);
        }
    }
    function listenDomNodeCreation(selector, callback) {
        addListenerCSS();
        addSelectorCSS(selector);
        var insertListener = function(event){
            if (event.animationName == "nodeInserted") {
                callback();
            }
        }
        document.addEventListener("animationstart", insertListener, false); // standard + firefox
        document.addEventListener("MSAnimationStart", insertListener, false); // IE
        document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
    }
    listenDomNodeCreation(selector, callback);
}
var domMutationObserver = function(tag, callback) {
    var observer = new MutationObserver(function(mutations) {
        // For the sake of...observation...let's output the mutation to console to see how this all works
        var start = new Date().getTime();
        mutations.forEach(function(mutation) {
            for (var n in mutation.addedNodes) {
                var node = mutation.addedNodes[n];
                if (node.nodeName && node.nodeName.toLowerCase === tag.toLowerCase) {
                  if(node.className!='exec-time') {                   
                    callback();
                  }
                  return;
                }
            }
        });
    });
    // Notify me of everything!
    var observerConfig = {
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true
    };
    // Node, config
    // In this case we'll listen to all changes to body and child nodes
    var targetNode = document.body;
    observer.observe(targetNode, observerConfig);
}
function createTestString() {
    var teststr = '';
    var addSubNodes = function(str, count, depth) {
        for (var p = 0; p < count; p++) {
            str += '<div class="blah">Sub node text';
            if (depth > 0) {
                str += addSubNodes(str, count, depth - 1);
            }
            str += '</div>';
        }
        return str;
    }
    var teststr = '';
    for (var p = 0; p < 100; p++) {
        teststr += '<p>A paragraph with some text in ' + addSubNodes('', 1, 4) + '</p>';
    }
    teststr += '<section><header><span>TEST</span><header><section>';
  return teststr;
}
  </script>
</head>
<body>
<script>
  
  startTime = 0;
  
  function showExecTime(listenerType) {
    if (startTime ===0) {
      return;
    }
    var endTime = new Date().getTime();
    var execTime = endTime - startTime;
    var msg = 'Execution time for '+listenerType+ ': '+execTime+'ms';
    console.log(msg);
    $('#execution-times').append('<p class="exec-time">'+msg+'</p>');
  }
  
  $(document).ready(function(){
      var dil = new domInsertionListener('span', function(){
        showExecTime('domInsertionListener');
      });
      var dmo = new domMutationObserver('span', function(){
        showExecTime('domMutationObserver');
      });
  });
  
  function buttonClick() {
    // intentionally global
    startTime = new Date().getTime();
    $('#append-area').append(createTestString());
  }
  
  </script>  
  <h1>CSS anim listener V MutationObserver</h1>
  <p>Guy Thomas 2016 - brudinie@gmail.com</p>
  <p>There are two functions tested in this jsbin. The first function "domInsertionListener" uses CSS3 animation to listen for dom element insertion. The second function "domMutationObserver" uses the MutationObserver API</p>
  <p>The purpose of this jsbin is to test the performance of each approach. It does this by generating some html dynamically with the desired element right at the end of the html (a span element).
  It's at the end of the html because the mutation observer has to itterate through each element that has been inserted.
  Suprisingly, the mutation observer is still faster than the CSS animation technique, even though it has to cycle through a lot of nodes.</p>
  <p>Code for both functions taken from https://davidwalsh.name/detect-node-insertion and https://davidwalsh.name/mutationobserver-api </p>
  <button onclick="buttonClick();">CLICK ME</button>
  <h2>Execution Times</h2>
  <div id="execution-times"></div>
  <h2>Appended HTML</h2>
  <div id="append-area"></div>
  
</body>
</html>
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
gthomas2pro
0viewers