Jamie Hutber
Jamie Hutber

Reputation: 28076

Mootools Selectors $ or $$. Which is more performant

I am curious as which selector would be quicker for the browser to look up. I am interested in general rather than specific browsers.

$('accountDetailsTable').getElements('.toggler')

This will look for accountDetailsTable as if you were using document.getElementById('accountDetailsTable'); and then look for .toggler inside of the element.

$$('.toggler')

Whereas this one will return all the selectors directly. But ultimately they will both give me the same result.

So which one would be quicker? How could I test this?

Upvotes: 1

Views: 113

Answers (2)

Dimitar Christoff
Dimitar Christoff

Reputation: 26165

performance of selectors will differ between browsers significantly. eg. with querySelector and querySelectorAll (QSA) vs without. When without QSA, with getElementsByClassName or without.

the amount of work that the selector engine needs to do will vary.

you can write this differently. 3 ways to do so, not just 2:

1. $('accountDetailsTable').getElements('.toggler')

anatomy of the above is:

  • get element (1 call).
  • call method getElements from element prototype (or element directly in old IE)
  • look for all childNodes that match the class selector

this will be consistent in browsers in that it gets a root node and calls methods off it. if no QSA, it will go to getElementsByClassName or walk all childNodes and filter the className property until it has a list of matches. Performance wise, this will be worse in modern browsers because it needs to chain whereas method 3 will be a direct result.

because of how selectors work in JS, unlike in CSS - it's from left to right and more qualified selectors improve performance, having something like .toggler means when there is an older browser, it needlessly needs to consider every single DOMnode in the tree (except for text nodes). always qualify the selectors when you can, i.e. div.toggler or a.toggler.

2. $$('.toggler')

  • if QSA is available, rely on the browser to return stuff (no extra hacks here like :not, or :contains or :has or ! (reverse combinators).
  • if NO QSA, it will parse the expression via the Slick parser and basically internally degrade to something like in case 1 above. The biggest difference here is the lack of context - it will run against the whole document as $$ internally does something like document.getElements('.toggler') so there are a lot more nodes to consider. Always anchor your queries to a stable upper-most common node. or pass it into the query string for qualification like in case 3

once again, this will be improved for performance by making it more qualified eg:

$$('a.toggler')

3. $$('#accountDetailsTable .toggler')

  • Similar to case 2 but when QSA is available, it will be faster.
  • When QSA it is not there, it will run against the context of the #accountDetailsTable node so it will be better than case 2.

Making this more qualified will make a difference:

$$('#accountDetailsTable td.control > a.toggler')

big discount: it will depend on how big your DOM is, how many matches are found and returned. on a simple DOM, the expected performance order may differ.

Performance optimisations of selectors are increasingly irrelevant these days.

The days of SlickText comparisons of frameworks are over and application performance has have little to do with selector speed, or you are doing something wrong.

If you do your job right, you won't need to be constantly selecting elements. You can cache things, reuse, be smart and reduce DOM lookups to a minimum.

Events etc can be attached through smart event delegation, where appropriate, completely negating the need to select and add events to multiple nodes etc - use common sense and don't get hung up on theoretical performance benchmarks. Instead, use a profiler and test your actual app and see where you lose time / CPU cycles.

You can run a simple selector like that over 50000 times in the space of a second. That's micro-benchmarking and does not measure actual real world performance inside your APP, DOM, browser etc.

More thoughts on benchmarking performance and premature optimisations: http://www.youtube.com/watch?v=65-RbBwZQdU

Careful

Write for correctness and readability first. Even if you can get extra performance by doing something, don't do it at the expense of readability if it's not critical / reused all the time. Selectors like .toggler, .new, .button etc tend to be too generic and can be re-used across the app in different parts of the DOM. You need to qualify selectors to also ensure your intended functionality will continue working when it gets moved into a different page / widget / DOM and your project gets an influx of new developers.

just my two cents, I know you already accepted the answer.

Upvotes: 5

Bjoern
Bjoern

Reputation: 16304

My internal MooTools knowledge got kind of rusty over the last two years, but generally speaking I expect the $$-selector to be much faster, since it has to run through the elements only once.

Why not give it a try? See this JSfiddle:

Rough HTML:

<div id="accountDetailsTable">
    <div id=".toggler">1</div>    
    <div id=".toggler">2</div>    
    <div id=".toggler">3</div>    
</div>

JScript (please don't comment on the bad code, its just to demonstrate the functionality):

window.addEvent('domready', function() { 
    var iter = 50000;

    var tstart = new Date().getTime();
    for (var i=0;i<iter;i++) {
        var x = $('accountDetailsTable').getElements('.toggler');
    }

    var tend = new Date().getTime();   
    var tdur = tend - tstart;

    console.log('$: ' + tdur + ' ms');

    var tstart = new Date().getTime();
    for (var i=0;i<iter;i++) {
         var x = $$('.toggler');
    }

    var tend = new Date().getTime();   
    var tdur = tend - tstart;

    console.log('$$: ' + tdur + ' ms');
});

That said, my tests with about 50000 iterations lead to roughly this results:

  • $('accountDetailsTable').getElements('.toggler') : ~ 4secs
  • $$('.toggler') : ~ 2secs

Results will vary across browsers, elements and much more, so this is only a rough approximation.

This said, you'll probably feel only a difference if you have that many selectors in such a short period. Yes, performance should be thought about, if you only have a few selectors in your application, it shouldn't really matter.

I prefer the $$(), not because of the better performance, but because of better readability.

Upvotes: 1

Related Questions