$(“#ID”) is fast enough

We all know that the fastest selector to use is the ID. Something like $(“#id”) is better than using a class. However,
after looking at the last test we did on The Great Assumption – JavaScript I noticed
a large discrepancy in $(“#ID”) versus the native document.getElementById().

Now, I always assumed it was slower, but not anywhere near close to what I saw. So, lets challenge the assumption that $(“#ID”) is good enough.

Setting up the Test

You can see the test here: JSPerf $(doc.getElementById()) versus $(“#id”)

I wanted to test 3 of the latest versions of jQuery, same as the last test. I added the following to the test:


<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.min.js"></script>
<script type="text/javascript">
var $15 = jQuery.noConflict();
</script>

<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
var $144 = jQuery.noConflict();
</script>

<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
var $142 = jQuery.noConflict();
</script>

<div class="wrapper">
<div class="innerWrapper">
<div class="button">
<a href="javascript:void(0)" id="button" class="buttonRef">
<span class="buttonText">Text</span>
</a>
</div>
</div>
</div>

The Tests

So lets setup the actual tests:


//getElement - 1.4.2
$142(document.getElementById("button"));

//ID - 1.4.2
$142("#button");

//getElement - 1.4.4
$144(document.getElementById("button"));

//ID - 1.4.2
$144("#button");

//getElement - 1.5
$15(document.getElementById("button"));

//ID - 1.4.2
$15("#button");

Each one of the tests above will run and the result will be returned in Ops/Sec.

Results

After runs in all different versions of the browsers we have a fairly good idea of the results. Take a look below.


Native Browser Call Results

From the last test we know that getting a Node Element from the native browser call is suprisingly efficient and fast in many browsers. Well, that is unless you are using IE 6, 7, 8 or even the beta 9.

jQuery $(“#ID”) Results

The results from this test show that getting an ID using jQuery is much faster than using the class selector. However, it is way behind the native call.
The root cause of this is actually due to how the init function is configured. Looking at the source for 1.5, an ID selector will hit 6 IF statements, plus
1 regex before it makes the native document.getElementByID(). After the call, there is a series of browser compatibility checks before it assigns the Node
Element to the jQuery object and returns.


init: function( selector, context ) {
var match, elem, ret, doc;

// Handle $(""), $(null), or $(undefined)
if ( !selector ) {
return this;
}

// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}

// The body element only exists once, optimize finding it
if ( selector === "body" && !context && document.body ) {
this.context = document;
this[0] = document.body;
this.selector = "body";
this.length = 1;
return this;
}

// Handle HTML strings
if ( typeof selector === "string" ) {
// Are we dealing with HTML string or an ID?
match = quickExpr.exec( selector );

// Verify a match, and that no context was specified for #id
if ( match && (match[1] || !context) ) {

// HANDLE: $(html) -> $(array)
if ( match[1] ) {
doc = (context ? context.ownerDocument || context : document);

// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
ret = rsingleTag.exec( selector );

if ( ret ) {
if ( jQuery.isPlainObject( context ) ) {
selector = [ document.createElement( ret[1] ) ];
jQuery.fn.attr.call( selector, context, true );

} else {
selector = [ doc.createElement( ret[1] ) ];
}

} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
}

return jQuery.merge( this, selector );

// HANDLE: $("#id")
} else {
elem = document.getElementById( match[2] );

// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}

// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}

this.context = document;
this.selector = selector;
return this;
}

// HANDLE: $("TAG")
} else if ( !context && !rnonword.test( selector ) ) {
this.selector = selector;
this.context = document;
selector = document.getElementsByTagName( selector );
return jQuery.merge( this, selector );

// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return (context || rootjQuery).find( selector );

// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return jQuery( context ).find( selector );
}

// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}

if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}

return jQuery.makeArray( selector, this );
},

Notice the elem = document.getElementById( match[2] ) on line 58. This is the part where it delegates its call to the native function.

HOWEVER, what is that on line 9? A use case for $(DOMElement)? Why yes, yes it is, and that leads us to the next test.

Hybrid Results

So by selecting the DOM Element, and passing it into to jQuery we have achieved nearly 100% or more increase in speed across all browsers (except IE). IE still benefits from this method, it had a 49% increase
in operations per second.

Hybrid Results

Here you can clearly see that by using a hybrid solution we have achieve a significant amount of gain over using the built
in jQuery ID selector. Its not nearly as fast, but allows us to benefit from the built-in functions of jQuery and speed up the
operation.

Do This, Not That

Not exactly what I expected when I set out on this one. However I think we have a clear winner. IF you are using jQuery, pass the node Element in, other wise just use the native getElementById().

Conclusion

Do This


$(document.getElementById("someId"));

Not That


$("#someId");

6 thoughts on “$(“#ID”) is fast enough”

  1. I would just like to let ufo know how much I learn from your website Bookmarked book , be back fast for some more good articles.

Leave a Reply to JC Fant IV Cancel reply

Your email address will not be published. Required fields are marked *