jQuery context or no context

JavaScript performance comparison

Revision 7 of this test case created by Jeremy S

Info

Test to understand better selector performance in jQuery. More varied DOM structure.

Note that some of these test cases aren't actually selecting the same elements as expected -- particularly #2 and #8, #9, which either select too many or are too restrictive (see the console.log for the number of matched elements for each test scenario). I've left them in from the original scenario, but they kinda throw off the results a little.

Preparation code

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
        <style>
                #parent { font-size:8px; }
                h1, h2, h3, h4 { margin:0.25em; padding:0; }
                .level1, .level2, .level3, .level4 { border: 3px solid black; display:inline-block; margin:0.25em; padding:0.5em; }
                .level1 { border-color: red; }
                .level2 { border-color: blue; }
                .level3 { border-color: green; }
               
                .a { background-color: #ccc; }
                .b { background-color: white; }
        </style>
<div id="parent">
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
  <div class="a">
    a
  </div>
  <div class="b">
    b
  </div>
</div>

<script>
// increase the dom size
!(function($){
var $container = $('#parent')
        , scale = 2
        // items per level
        , level1 = 5 * scale
        , level2 = 3 * scale
        , level3 = 4 * scale
        , level4 = 1 * scale
        // ab counter
        , ab_counter = 0
        , ab = function() { return ( ab_counter++ % 7 == 1 ? 'a' : 'b' ); }
        // loop and dom creation
        , createLoop = function(counter, createFn) {
                while(counter-- > 0) {
                        createFn(counter);
                }
        }
        , levelDom = '<div id="level{{level}}-{{id}}" class="level{{level}} {{ab}}"><h{{level}}>Level {{level}} ({{id}})</h{{level}}></div>'
        , createLevel = function(level, id) {
                return $(levelDom.replace('{{ab}}', ab()).replace(/{{level}}/ig, level).replace(/{{id}}/ig, id));
        }
        ;
       
       
        // loop to create each level
        createLoop(level1, function(i1) {
                var $l1 = createLevel(1, i1);

                createLoop(level2, function(i2) {
                        var $l2 = createLevel(2, [i1, i2].join('-'));
                       
                        createLoop(level3, function(i3) {
                                var $l3 = createLevel(3, [i1, i2, i3].join('-'));
                               
                                createLoop(level4, function(i4) {
                                        var $l4 = createLevel(4, [i1, i2, i3, i4].join('-'));
                               
                                        $l3.append($l4);
                                });// loop level4
                               
                                $l2.append($l3);
                        });// loop level3

                        $l1.append($l2);
                });// loop level2

                $container.append($l1);
        });// loop level1
})(jQuery);


///---- setup -----
var context_dom_id = 'level1-0'; // the container (DOM)
var context_selector = '#' + context_dom_id; // the container (jQuery)
var target_dom_class = 'a'; // the thing we want to target (DOM)
var target_selector = '.' + target_dom_class; // the thing we want to target (jQuery)
var context = document.getElementById(context_dom_id); // DOM cached
var $context = $(context); // jQuery cached

///---- confirm that tests will find something ----
!(function($, undefined) {
        /// adapted from http://stackoverflow.com/a/15623322/1037948
       
        get_selector = function(element, delim) {
                if( typeof element === typeof undefined || typeof element.tagName === typeof undefined ) return '';
        delim = delim || ' > ';
                pieces = [];
                do {
                        if (element.className) {
                        var classes = element.className.split(' ')
                        for (var i in classes) {
                                if(classes.hasOwnProperty(i)) {
                                        pieces.unshift(classes[i]);
                                        pieces.unshift('.');
                                }// hasOwnProperty
                        }// foreach classes
                        }// if className
                        if (element.id) {
                        pieces.unshift(element.id);
                        pieces.unshift('#');
                        }// if id
                        // for some reason I'm seeing "undefined" here in Chrome...strip empty items later
                        pieces.unshift(element.tagName);
                        pieces.unshift(delim);
                } while(element = element.parentNode);
               
        return pieces.slice(3).join('');
        };

        $.fn.getSelector = function(only_one, delim) {
                delim = (delim || (typeof only_one === typeof true ? false : only_one) || ' > ');
               
                if (true === only_one) {
                        return get_selector(this[0], delim);
                } else {
                        return $.map( this, function(el) {
                                return get_selector(el, delim);
                        });
                }
        };

})(window.jQuery);

function confirmExistence( t, $el ) {
 if( console && console.log ) {
   if( $el && typeof $el.length == "number" ) {
     console.log('test ' + t, $el.length, $el.getSelector(true));
   }
 }
}

confirmExistence( 0, $(context_selector) );
confirmExistence( 0, $(context) );
confirmExistence( 0, $context );
confirmExistence( 1, $(target_selector, context_selector) );
confirmExistence( 2, $(target_selector) );
confirmExistence( 3, $(context_selector + ' ' + target_selector) );
confirmExistence( 4, $(context_selector + ' div' + target_selector) );
confirmExistence( 5, $(target_selector, context) );
confirmExistence( 6, $(target_selector, $context) );
confirmExistence( 7, $context.find(target_selector) );
confirmExistence( 8, $context.find('> ' + target_selector) );
confirmExistence( 9, $context.children(target_selector) );
confirmExistence( 10, $(document.getElementById(context_dom_id).getElementsByClassName(target_dom_class)) );

</script>
<script>
Benchmark.prototype.setup = function() {
    // see end of preparation code
};
</script>

Preparation code output

a
b
a
b
a
b
a
b
a
b
a
b
a
b
a
b
a
b

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
With explicit context
//= $('.a', '#parent')
$(target_selector, context_selector).css({
  border: '1px solid red'
});
pending…
Without context
//= $('.a')
$(target_selector).css({
  border: '1px solid blue'
});
pending…
Without context, but only select the child
//= $('#parent .a')
$(context_selector + ' ' + target_selector).css({
  border: '1px solid pink'
});
pending…
Without context, selecting the child & give node specificity
//= $('#parent div.a')
$(context_selector + ' div' + target_selector).css({
  border: '1px solid green'
});
pending…
Cached context (vanilla)
//= $('.a', context)
$(target_selector, context).css({
  border: '1px solid gray'
});
pending…
Cached context (jQuery)
//= $('.a', $context)
$(target_selector, $context).css({
  border: '1px solid yellow'
});
pending…
cached-jQuery + .find
//= $context.find('.a')
$context.find(target_selector).css({
  border: '1px solid purple'
});
pending…
cached-jQuery + .find children
//= $context.find('> .a')
$context.find('> ' + target_selector).css({
  border: '1px solid red'
});
pending…
cached-jQuery + .children
//= $context.children('.a')
$context.children(target_selector).css({
  border: '1px solid goldenrod'
});
pending…
jQuery(vanilla js)
$(document.getElementById(context_dom_id).getElementsByClassName(target_dom_class)).css({
  border: '1px solid blue'
});
pending…
vanilla js
var els = document.getElementById(context_dom_id).getElementsByClassName(target_dom_class);
for(var i in els) { if(typeof els[i] == "object") els[i].style.border = '1px solid green'; }
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment