Question

For example, will the first piece of code perform a full search twice, or is it smart enough to cache results if no DOM changes have occurred?

if ($("#navbar .heading").text() > "") {
  $("#navbar .heading").hide();
}

and

var $heading = $("#navbar .heading");

if ($heading.text() > "") {
  $heading.hide();
}

If the selector is more complex I can imagine it's a non-trivial hit.

Was it helpful?

Solution

jQuery doesn't, but there's the possibility of assigning to variables within your expression and then use re-using those in subsequent expressions. So, cache-ifying your example ...

if ((cached = $("#navbar .heading")).text() > "") {
  cached.hide();
}

Downside is it makes the code a bit fuglier and difficult to grok.

OTHER TIPS

Always cache your selections!

It is wasteful to constantly call $( selector ) over and over again with the same selector.

Or almost always... You should generally keep a cached copy of the jQuery object in a local variable, unless you expect it to have changed or you only need it once.

var element = $("#someid");

element.click( function() {

  // no need to re-select #someid since we cached it
  element.hide(); 
});

It's not so much a matter of 'does it?', but 'can it?', and no, it can't - you may have added additional matching elements to the DOM since the query was last run. This would make the cached result stale, and jQuery would have no (sensible) way to tell other than running the query again.

For example:

$('#someid .someclass').show();
$('#someid').append('<div class="someclass">New!</div>');
$('#someid .someclass').hide();

In this example, the newly added element would not be hidden if there was any caching of the query - it would hide only the elements that were revealed earlier.

I just did a method for solving this problem:

var cache = {};

function $$(s)
{
    if (cache.hasOwnProperty(s))
    {
        return $(cache[s]);
    }

    var e = $(s);

    if(e.length > 0)
    {
        return $(cache[s] = e);
    }

}

And it works like this:

$$('div').each(function(){ ... });

The results are accurate as far as i can tell, basing on this simple check:

console.log($$('#forms .col.r')[0] === $('#forms .col.r')[0]);

NB, it WILL break your MooTools implementation or any other library that uses $$ notation.

I don't think it does (although I don't feel like reading through three and a half thousand lines of JavaScript at the moment to find out for sure).

However, what you're doing does not need multiple selectors - this should work:

$("#navbar .heading:not(:empty)").hide();

Similar to your $$ approach, I created a function (of the same name) that uses a memorization pattern to keep global cleaner and also accounts for a second context parameter... like $$(".class", "#context"). This is needed if you use the chained function find() that happens after $$ is returned; thus it will not be cached alone unless you cache the context object first. I also added boolean parameter to the end (2nd or 3rd parameter depending on if you use context) to force it to go back to the DOM.

Code:

function $$(a, b, c){
    var key;
    if(c){
        key = a + "," + b;
        if(!this.hasOwnProperty(key) || c){
            this[key] = $(a, b);
        }        
    }
    else if(b){
        if(typeof b == "boolean"){  
            key = a;  
            if(!this.hasOwnProperty(key) || b){
                this[key] = $(a);
            }
        }
        else{
            key = a + "," + b;
            this[key] = $(a, b);   
        }            
    }
    else{
        key = a;
        if(!this.hasOwnProperty(key)){
            this[key] = $(a);
        } 
    }
    return this[key]; 
}

Usage:

<div class="test">a</div>
<div id="container">
    <div class="test">b</div>
</div>​

<script>
  $$(".test").append("1"); //default behavior
  $$(".test", "#container").append("2"); //contextual 
  $$(".test", "#container").append("3"); //uses cache
  $$(".test", "#container", true).append("4"); //forces back to the dome
​
</script>

i don't believe jquery does any caching of selectors, instead relying on xpath/javascript underneath to handle that. that being said, there are a number of optimizations you can utilize in your selectors. here are a few articles that cover some basics:

This $$() works fine - should return a valid jQuery Object in any case an never undefined.

Be careful with it! It should/cannot with selectors that dynamically may change, eg. by appending nodes matching the selector or using pseudoclasses.

function $$(selector) {
  return cache.hasOwnProperty(selector) 
    ? cache[selector] 
    : cache[selector] = $(selector); 
};

And $$ could be any funciton name, of course.

John Resig in his Jquery Internals talk at jQuery Camp 2008 does mention about some browsers supporting events which are fired when the DOM is modified. For such cases, the Selctor results could be cached.

There is a nice plugin out there called jQache that does exactly that. After installing the plugin, I usually do this:

var $$ = $.q;

And then just

$$("#navbar .heading").hide();

The best part of all this is that you can also flush your cache when needed if you're doing dynamic stuff, for example:

$$("#navbar .heading", true).hide(); // flushes the cache and hides the new ( freshly found ) #navbar .heading

And

$$.clear(); // Clears the cache completely

jsPerf is down today, but this article suggests that the performance gains from caching jQuery selectors would be minimal.

enter image description here

This may simply be down to browser caching. The selector tested was only a single id. More tests should be done for more complicated selectors and different page structures...

jQuery Sizzle does automatically cache the recent functions that have been created from the selectors in order to find DOM elements. However the elements themselves are not cached.

Additionally, Sizzle maintains a cache of the most recently compiled functions. The cache has a maximum size (which can be adjusted but has a default) so you don’t get out-of-memory errors when using a lot of different selectors.

$.selectorCache() is useful:

https://gist.github.com/jduhls/ceb7c5fdf2ae1fd2d613e1bab160e296

Gist embed:

<script src="https://gist.github.com/jduhls/ceb7c5fdf2ae1fd2d613e1bab160e296.js"></script>

Check if this helps https://plugins.jquery.com/cache/

Came across this as part of our regular project

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top