20

Is it recommended that, when I need to access the result of a jQuery selector more than once in the scope of a function, that I run the selector once and assign it to a local variable?

Forgive my trite example here, but i think it illustrates the question. So, will this code perform faster:

var execute = function(){
    var element = $('.myElement');
    element.css('color','green');
    element.attr('title','My Element');
    element.click(function(){
        console.log('clicked');
    });
}

than this code:

var execute = function(){
    $('.myElement').css('color','green');
    $('.myElement').attr('title','My Element');
    $('.myElement').click(function(){
        console.log('clicked');
    });
}

If there is no difference, can anyone explain why? Does jQuery cache elements after selecting them so subsequent selectors don't have to bother searching the dom again?

KodeKreachor
  • 8,852
  • 10
  • 47
  • 64

5 Answers5

21

Reusing the selector reference, your first case, is definitely faster. Here's a test I made as proof:

http://jsperf.com/caching-jquery-selectors

The latter case, redefining your selectors, is reported as ~35% slower.

weotch
  • 5,788
  • 6
  • 35
  • 42
  • Cool test, very nice, but I'm confused...is the red bar representing the 'local variable' example, as it's label states? It looks like that took longer? – KodeKreachor Apr 07 '12 at 14:20
  • Yeah, red is the local variable. It is a bit confusing, longer is better in the chart. It represents how many operations could be run per second. – weotch Apr 07 '12 at 14:21
  • Thank you for introducing me to that site. It's fantastic! – tsherif Apr 07 '12 at 14:23
  • Ah gotcha, thanks man, I need a +2 button here, one for your answer and one for that stellar perf site ;) – KodeKreachor Apr 07 '12 at 14:30
  • Ha, awesome! I noticed when I added the additional tests that the colors changed. Green is the new red ;) – weotch Apr 07 '12 at 14:38
  • See my answer. A truly cached approach is significantly faster. – Guido Bouman Apr 07 '15 at 13:14
9

Don't forget this one:

var execute = function(){ 
    $('.myElement')
        .css('color','green')
        .attr('title','My Element')
        .click(function(){ 
            console.log('clicked'); 
        });
}
ZippyV
  • 12,540
  • 3
  • 37
  • 52
  • @weotch Great. I wasn't fast enough so cancelled my identical answer... But it's great to know for sure that this is an option. Could weotch add this one to his speed test? – ZZ-bb Apr 07 '12 at 14:23
  • 1
    +1. This is the solution that jQuery would have envisioned since most of their examples tend to chain calls together like this. Perhaps it isn't faster than saving in local variables, but it is certainly faster than multiple selections and I would argue that it's more elegant. – Neil Apr 07 '12 at 14:24
  • 2
    Added that test. It looks tied for first! – weotch Apr 07 '12 at 14:27
  • @weotch Thanks. Reusing variable was just barely faster than chaining at the first run. At the second run chaining was significantly faster. Still, +1 to this answer from me also. No need to write the selector repeatedly. Sligthly slower first run is small price to pay for elegant code... – ZZ-bb Apr 07 '12 at 14:34
  • Cool, I've seen a bunch of jQuery fellows slam other devs on this site for chaining though. I personally don't have a problem with it at all, but some people tend to think it's bad practice which makes me second guess doing so. It seems like jQuery was certainly built to handle chaining elegantly though. Should I ignore the chaining haters? Maybe that's another question entirely – KodeKreachor Apr 07 '12 at 14:35
3

Storing the reference in a local variable will be faster than running the selection code each time. It's simply a matter of not having to execute ANY code to find the appropriate element(s) when you store it in a variable. My rule of thumb is to store the results of the jQuery lookup in a variable if I'm going to use it more than once.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
1

Another option here is to use an each instead of repeating the selector, and it's associated work, time and time again

var execute = function(){
    $('.myElement').each(function() {
      var elem = $(this);
      elem.css('color','green');
      elem.attr('title','My Element');
      elem.click(function(){
        console.log('clicked');
      });
    });
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    If you're interested in performance, repeatedly re-allocating memory for objects and making unnecessary function calls (granted, they're fairly quick) by doing `$(this)` over and over for the same element is less than ideal. – T.J. Crowder Apr 07 '12 at 14:18
  • 1
    *"The difference is in the `attr` function. If you apply it to a collection it only affects the first element in the collection."* No, that's only true on *get*. On *set*, it sets it for all of them: http://api.jquery.com/attr/#attr2 – T.J. Crowder Apr 07 '12 at 14:20
  • @T.J.Crowder my bad, I didn't look at the signature of the method I was reading the docs for. – tvanfosson Apr 07 '12 at 14:21
  • This is a rather inefficient approach. With each execution of the function you're looping over all of the elements inside the selector, and make a new assignment with `var elem = $(this)`, making this whole process a factor `n` slower. Where `n = the amount of elements in the selector`. – Guido Bouman Apr 07 '15 at 13:12
1

You're actually forgetting the truly cached approach.

The thing with jQuery is that the initial lookup: $('.selector') is expensive. But after that, chaining your actions onto it, or assigning it to a variable and executing your actions on the variable don't matter that much. The main performance gain you can get is caching the element even further, and not assigning the jQuery selector each iteration of your function call.

var element = $('.myElement');
var execute = function(){
    element.css('color','green');
    element.attr('title','My Element');
    element.click(function(){
        console.log('clicked');
    });
}

This approach is almost twice as fast as the fastest version from the other approaches suggested.

See http://jsperf.com/caching-jquery-selectors/17

Note: If your DOM changes during it's lifetime, you can update the element variable with the a fresh selection of elements.

Guido Bouman
  • 3,155
  • 4
  • 22
  • 33