Found Code: JavaScript getElementById, Performance, and the $ Function

Apr 11, 2008

Disclaimer: Check the date! This post was written way back in 2008. If there are technical things in here, they may be outdated, or there may be better ways to do them by now. You've been warned.

From what I’ve seen, most of the popular JavaScript frameworks out there provide some form of the $
method. This method is usually a cross-browser translation of document.getElementById with a few
extras. What this means is instead of always having to type document.getElementById("myId") you
can now type $("myId"). All-in-all it’s a very nice shortcut and will probably cut quite a bit of
typing out of your JavaScript projects. My problem is with the number of times I’ve come across inefficient uses
of this helper function because it’s easier to type. This usually manifests itself as something along these lines
(this example written using prototype.js):

This code looks pretty concise, if you know what the $ function does it’s also pretty simple, but take
a second to think about performance. In this code it’s possible that you’re calling document.getElementByIdfive times! Ignoring whatever else the $ function does, the
document.getElementById function could traverse the entire DOM
on each call. (I’m not saying it does, but you really don’t know what the browser is doing under the covers and since
you’re designing for all JavaScript enabled browsers, it’s better safe than sorry!) If you actually had to type out the
document.getElementById you would probably consider something like this:

With this code, you’re only calling document.getElementById once, and therefore only traversing the
DOM once. Makes a little more sense right?
Let’s look at some metrics.

For this experiment, let’s make the assumption that the larger your document is the longer it will take to perform a
DOM traversal, so in order to see some results
we’ll need a decent sized document. For that we’ll go to wikipedia and grab something off the front page, in this case
it’ll be Harry Potter. To get my test document I viewed the source,
grabbed the main body content, and pasted it into a new HTML document. I also removed all of the
images since I didn’t want broken images or to be hitting wikipedia’s servers for my experiment. I wrote a quick JavaScript
class that will do my benchmarking, and a quick test case that calls my two methods above as well as two other methods and
single calls to both the $ function and the document.getElementById function. To perform the
benchmark, I run each method 1000 times. I initially tried smaller numbers but there was not enough visual difference to
prove my theory since JavaScript is only accurate to the millisecond. You can find
the test code here, the
benchmark class here, and you can
run the test here.
I’m using Firebug Lite for the console logging, but if you have
Firebug installed it will use the Firebug console. As I said before I just wrote the
benchmarking class, but look for a future post and improved version now that the seed has been planted.

I have successfully run the test on Safari 3.1 on OSX 10.5, Firefox 2.0.0.13 and
IE</span> 7.0.6000.16643 on Windows Vista Business 64-bit, and IE 6.0.2900…
and Safari 3.0.4 on Windows XP Pro. My results were as follows:

getElementById

$ function

Un-optimized $ function

Optimized $ function

Un-optimized getElementById

Optimized getElementById

Safari Mac

1 ms

4 ms

67 ms

37 ms

42 ms

33 ms

Firefox

7 ms

9 ms

177 ms

121 ms

154 ms

118 ms

IE 7

273 ms

291 ms

1829 ms

364 ms

1688 ms

337 ms

IE 6

312 ms

297 ms

1960 ms

484 ms

1735 ms

375 ms

Safari Windows

0 ms

0 ms

94 ms

47 ms

47 ms

46 ms

Across the board the optimized functions performed better, and in most cases the $ function was
slightly slower than the document.getElementById function. The most surprising result is Safari
on Windows because it’s actually the slowest machine that these tests were run on. The only problem that I
can think of with this test is that it does quite a bit of looking up by id, and that’s probably not an accurate
test case, but even if you’re cutting out only a few milliseconds on an event, somebody will notice the
improvement. I’m welcome to any suggestions or comments on my testing methodology.