Walter Rumsby's Web Site

Dust Me Selector

03 Feb 2008 at 13:00

In version 2.4.0 YUI got its own Selector Utility allowing you to use CSS 3 Selectors to find nodes. This means you can write the selector query:

var nodes = YAHOO.util.Selector.query('input[name^=yui]');

and nodes will contain all the input elements whose name attribute begins with "yui".

As this timeline indicates this is a feature in common with a number of other JavaScript libraries and something that is bound to be used more and more often.

But before you go off a play with your shiny new plaything I'd like to share the results of some profiling that I did with you.

Initially I did this profiling after someone suggested to me that

YAHOO.util.Event.onDOMReady(
    function() {
        var nodes = YAHOO.util.Dom.getElementsByClassName(...);
        // Do something AMAZING with the nodes
    }
);

might be slow - i.e. that YAHOO.util.Dom.getElementsByClassName() might struggle with large or complex documents. Since we use this technique for some of our widgets I wanted to make sure that what we were doing was as fast as possible.

To get an idea of the relative performance of a variety of techniques I grabbed the source of a page that had recently been the subject of debate at work (thinking it to at least be a somewhat realistic example) and compared the following techniques to find table elements with a class name of "Table":

  • YAHOO.util.Dom.getElementsByClassName('Table');
  • YAHOO.util.Dom.getElementsByClassName('Table', 'table');
  • YAHOO.util.Selector.query('[class~="Table"]');
  • YAHOO.util.Selector.query('table.Table');
  • $('.Table');
  • $('table.Table');

For the last two techniques I used jQuery as it has supported CSS3 Selectors for a while and I wanted to see how it performed relative to YUI, given the YUI implementation is in beta. Also note that there is a bug in the initial version of the YUI Selector Utility that means the class selector does not work if the class name begins with a capital letter, this means I tested YAHOO.util.Selector.query('[class~="Table"]'); rather than YAHOO.util.Selector.query('.Table'); and as the results (which we'll get to in a second) indicate comparing [class~="Table"] to .Table might not be a fair comparison.

These results are for Firefox 2 running on an HP Compaq 6910p with 2GB of RAM and Windows XP Professional SP 2.

Time (s) for 25 iterations
Technique Time (s)
YUI DOM ('Table') 0.9162
YUI DOM ('Table', 'table') 0.0062
YUI Selector ('[class~="Table"]') 2.9312
YUI Selector ('table.Table') 0.0092
jQuery ('.Table') 1.2674
jQuery ('table.Table') 0.0144

What these numbers tell us:

  • I think the most important conclusion is that you need to reduce the scope of your query as much as possible. Looking for table elements with class="Table" is much faster than looking for any element with class="Table".
  • In this test YUI Selector appears to hold its own in terms of performance when compared to jQuery for the selectors used (if we accept that we can't regard [class~="Table"] and table.Table as equivalent tests). However I'm reluctant to conclude that the YUI Selector implementation is faster than the jQuery one across the board.

For round two I focused on the fastest queries and tried 10,000 iterations of each:

Time (s) for 10,000 iterations
Technique Time (s)
YUI DOM ('Table', 'table') 1.743
YUI Selector ('table.Table') 4.8714

From this I think we can conclude:

  • If you can perform an equivalent search using YAHOO.util.Dom.getElementsByClassName it should outperform a selector query.
  • If you can optimise your query it shouldn't hit too many speed bumps when you try to find matching nodes regardless of the technique you use.

I guess those points deserve a little more explanation.

On the first point, I have seen people write selectors that look like this:

var nodes = YAHOO.util.Selector.query('div#Foo a.Bah');

i.e. get all the a elements with class="Bah" that are descendants of the element with id="Foo".

Because an id should be unique on the page this can be rewritten as:

var nodes = YAHOO.util.Dom.getElementsByClassName('Bah', 'a', 'Foo');

(or even var nodes = YAHOO.util.Selector.query('a.Bah', 'Foo');) which should run faster today and, I would wager, in the future as more browsers implement getElementsByClassName natively (I'm assuming YUI decides to delegate to the native implementation at some point in the future).

The second point, however, suggests that if you minimise the scope of your selector query your shiny new plaything should be acceptably performant unless you have an unusually complex document.

blog comments powered by Disqus