The Great Assumption – Javascript

A couple of weeks ago I was sitting down with a Google Frontend Engineer friend of mine, talking about some of the JavaScript best practices we all have come to know and abide by. I found myself in agreement with 90% of everything we were talking about. However, something kept eating at me. Why do we both agree on these things? Is it because we actually sat down and did some test? Or is more because early in our careers we were told it was the correct way and have never gone about challenging our assumptions.

So with this post, I am going to start challenging our assumptions on what’s right, OR validate those assumptions with some documented proof. Thus begins our first in a series of posts about performance, and a “Do this, Not that” series. Some of the posts will focus on jQuery, others on core JavaScript, however all of them should give you some insight on what to do in the future.



Find Versus Context


Everyone has heard that we should scope our requests in jQuery to make sure and only return results that we are looking for. This is especially true if we are writing a plugin. And lately I have been hearing we should be using .find() to get the elements we are looking for.

Of course, that peaked my interest. Why use .find() which I had heard was slower, than using $(“”, context) which scopes the call in much the same way in jQuery. So I set off to write a Performance Test that would check to see which one I should be using.

Setting up the Test


You can see the test here: JSPerf jQuery Find Versus Context versions 1-4-2 to 1-5

I wanted to test 3 of the latest versions of jQuery, plus the browsers native docuemnt.getElementById(). so 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>
<script>
var context5 = $15("div.wrapper");
var context44 = $144("div.wrapper");
var context42 = $142("div.wrapper");
</script>


Notice that I am going to set a variable for each of the individual jQuery versions. This allows me to use multiple version in the noConflict() state. (They won’t overwrite each other and I will be able to have all of them on the same page.) Also, I setup a couple of variables that I want to cache the results. If we had to call this for each of the tests, we wouldn’t get an accurate result between a find and context search.

The Tests


So lets setup the actual tests:


//Get - document
var item = document.getElementById("button");

//Get - ID 1.4.2
var item = $142("#button");

//Get - ID 1.4.4
var item = $144("#button");

//Get - ID 1.5
var item = $15("#button");

//Context - ID 1.4.2
var item = $142("#button", context42);

//Context - ID 1.4.4
var item = $144("#button", context44);

//Context - ID 1.5
var item = $15("#button", context5);

//Find - ID 1.4.2
var item = context42.find("#button")

//Find - ID 1.4.4
var item = context44.find("#button")

//Find - ID 1.5
var item = context5.find("#button")

//Get - Class 1.4.2
var item = $142(".buttonRef");

//Get - Class 1.4.4
var item = $144(".buttonRef");

//Get - Class 1.5
var item = $15(".buttonRef");

//Context - Class 1.4.2
var item = $142(".buttonRef", context42);

//Context - Class 1.4.4
var item = $144(".buttonRef", context44);

//Context - Class 1.5
var item = $15(".buttonRef", context5);

//Find - Class 1.4.2
var item = context42.find(".buttonRef")

//Find - Class 1.4.4
var item = context44.find(".buttonRef")

//Find - Class 1.5
var item = context5.find(".buttonRef")



Each one of the tests above will run and the result will be returned in Ops/Sec. At this point, we are going to run a test to figure out how fast a context.find(“”), $(“”, context) take for an ID and for a class. I also added in a default document.getElementById() to test how fast natively the browser is able to get a Node Element.

Results


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







Find a class element Results






In General this test held true. Using find in all but FF3 and Opera is a faster way to find an element using a class.

Conclusion

Do This


context.find(".someclass");


Not That


$(".someclass");


Find an element by ID Results







WOW, not what I expected. Not one browse was a find faster than just using $(“#someID”). Basically this all boils down to how jQuery treats an ID selector. It doesn’t use the sizzle engine to query the Node Element, but uses the browsers built in document.getElementById(). However, if you pass in a context jQuery will use the engine to make sure the element falls within the context you are using.

Conclusion

Do This


$("#someID");


Not That


context.find("#someId");
$("#someId", context);


Find an element using built in getElementById() Results







Ok, so this result really confused me after seeing the jQuery $(“#someID”) results. I wasn’t expecting there to be such a discrepancy in operations per second. After seeing this result, I actually started putting together another post. Which will end up the second part of this series.

It should be noted that Chrome, Opera, Safari and FF are all doing well over a million operations per second in this area.


IE breakdown







After seeing the results above, I thought we needed one more graphic showing the extremely bad numbers IE put up in this test. There was no noticable change between IE6 and IE7, and while IE8 is better, its still bad. However, it looks like the IE9 team is finally taking web development seriously and trying to make some improvements. Of course those improvements still fall WAY short of any of the other browsers on the market.

Come on IE team, haven’t we learned anything from the IE6 debacle?


Conclusion


Overall, the conlusion here is to never use a context based find/search for an ID. (since there is always only one on the page… right?) If you need to get an element using an ID, just call it via $(“#someID”).

IF you need to get a class element(s), follow this rule:

context.find() > $(“.someClass”, context) > $(“.someclass”);

Next Article >> $(“#ID”) is fast enough

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>