Does jQuery do any kind of caching of "selectors"?

JqueryJquery Selectors

Jquery Problem Overview


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.

Jquery Solutions


Solution 1 - Jquery

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(); 
});

Solution 2 - Jquery

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.

Solution 3 - Jquery

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.

Solution 4 - Jquery

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.

Solution 5 - Jquery

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();

Solution 6 - Jquery

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>

Solution 7 - Jquery

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:

Solution 8 - Jquery

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.

Solution 9 - Jquery

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.

Solution 10 - Jquery

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

Solution 11 - Jquery

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...

Solution 12 - Jquery

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.

Solution 13 - Jquery

$.selectorCache() is useful:

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

Gist embed:

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

Solution 14 - Jquery

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

Came across this as part of our regular project

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAllain LalondeView Question on Stackoverflow
Solution 1 - JquerygnarfView Answer on Stackoverflow
Solution 2 - JqueryNeil C. ObremskiView Answer on Stackoverflow
Solution 3 - JqueryJoeBloggsView Answer on Stackoverflow
Solution 4 - JqueryAleksandr MakovView Answer on Stackoverflow
Solution 5 - JqueryPeter BoughtonView Answer on Stackoverflow
Solution 6 - JquerySefi GrossmanView Answer on Stackoverflow
Solution 7 - JqueryOwenView Answer on Stackoverflow
Solution 8 - JqueryJoschiView Answer on Stackoverflow
Solution 9 - JqueryAnirban DebView Answer on Stackoverflow
Solution 10 - JquerypyronaurView Answer on Stackoverflow
Solution 11 - JqueryjoeytwiddleView Answer on Stackoverflow
Solution 12 - JqueryBrave DaveView Answer on Stackoverflow
Solution 13 - JqueryjduhlsView Answer on Stackoverflow
Solution 14 - JquerySarath.BView Answer on Stackoverflow