6

Details:

Commonly-used jQuery selectors should be cached.

var $thing = $("#thing");
// ...lots of invocations of $thing...

It can be handy (and sometimes, arguably more performant) to select an outer div and then use .find() to get an inner element, especially if you're going to grab multiple things from within that div.

var $outerDiv = $("div#outer");
var $thing1 = $outerDiv.find(".thing1");
var $thing2 = $outerDiv.find(".thing2");
// ...etc....

But what if I have lots of outer divs and never use them after initially finding the necessary children?

var $outerDiv1 = $("div#outer1");
var $foo1 = $outerDiv1.find(".foo1");
var $foo2 = $outerDiv1.find(".foo2");
// ...etc....

var $outerDiv2 = $("div#outer2");
var $bar1 = $outerDiv2.find(".bar1");
var $bar2 = $outerDiv2.find(".bar2");
// ...etc....

Now I have $outerDiv1, $outerDiv2, etc. sitting in the global namespace taking up memory.

My questions are:

  • Should I be caching jQuery selectors in the global namespace at all? Why (not)?
  • If I place cached selectors in some namespace other than global, will the later-unreferenced $outerDiv* variables be cleaned up in garbage collecting?
  • Are the performance costs/benefits negligible anyway for a typical web page?

Note:

My question assumes the truth of the accepted answer for the question "jQuery object: to cache or not to cache?". It's not a duplicate, because it expands by considering the memory impact if we know some of the globally-cached selectors won't ever be used again.

crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81
  • 1
    Use the scope of the function you pass to `$(document).ready()`. That way you won't pollute the global scope. They still will take memory, though. – Teemu Aug 29 '17 at 18:42
  • I feel that we are dealing with two things performance and memory. About 1st question: if we need some selectors frequently through out the page then we should cache it in global namespace; but not all. Scoping it will always be helpful. – Amogh Aug 29 '17 at 18:43
  • 1
    for question 2nd you should refer to answer of https://stackoverflow.com/questions/37377653/when-is-this-scope-closure-being-garbage-collected-in-javascript this post – Amogh Aug 29 '17 at 18:45
  • 1
    Also see here: https://ttmm.io/tech/selector-caching-jquery/ – Peter B Aug 29 '17 at 18:49
  • @Amogh, can you clarify? It sounds like you're saying sometimes global namespace is best, but then scoping is always best. – crenshaw-dev Aug 29 '17 at 18:50
  • @PeterB, my question assumes the truth of the accepted answer for that question and expands by asking, "What's the memory impact if we know some of the cached selectors won't ever be used again?" – crenshaw-dev Aug 29 '17 at 18:51
  • @mac9416 :) sure, consider in your example `div#outer1` is needed for frequent operations like showing data from ajax response on a particular interval or may be for the operation which will be alive constantly for that purpose cache for `div#outer1` in global namespace is always helpful but at the same time if `div#outer2` is not needed frequently only to a very specific operations then why to keep cache of div#outer2 in global namespace(?) in this case just use it in-line or scope it for that block..this what i feel and experienced – Amogh Aug 29 '17 at 18:56
  • @Amogh, that makes sense, thanks. So if `div#outer1` is used a lot as you describe, is there any advantage over scoping in, say, a document.ready function? Or is it just perhaps more convenient? – crenshaw-dev Aug 29 '17 at 19:01
  • 1
    You can do experiments using e.g. the Chrome Developer Tools (F12), it has a **Memory** tab that allows for both snapshots and timelines, an it has a Garbage Collect button. However the results mainly mean someting for *that* version of Chrome using *that* Javascript engine on *your* current OS, and less so for the broad landscape of all of those. – Peter B Aug 29 '17 at 19:03
  • @PeterB, thanks, I'll check out those tools. I'm afraid my current applications aren't big enough to give me an idea about what best practices to adopt, but I'll investigate. Also, I read through the link to ttmm.io, and [commented](https://ttmm.io/tech/selector-caching-jquery/#comment-54744) the only concern I had about that approach. – crenshaw-dev Aug 29 '17 at 19:10
  • 1
    @mac9416 If you are sure that `div#outer1` is only used in document.ready then I will surely go with scoping it. remember "Javascript interpreter / virtual machine that automatically frees the memory of objects no longer needed by your program" so closure and scope will obviously help. program described in [answer](https://stackoverflow.com/a/37378174/1629242) will make these things clear. Also the point mentioned by @PeterB is also make a sense Memory profiling by developer tool will help you to analyze your program as everyone's scenario and experience is very different in such topic – Amogh Aug 29 '17 at 19:18
  • _"It's best to select an outer div and then use .find() to get an inner element, to avoid traversing the whole DOM."_ - says who? Someone who made a browser that totally sucks at one of the most basic DOM tasks, getting an element by its ID ...? Nothing against caching of selections used more than once - but `$("div#outer").find("#thing");` is some of the biggest nonsense I ever heard. We would have to be talking about _massive_ DOMs here before that would even remotely start making sense IMHO. – CBroe Aug 29 '17 at 20:01
  • 1
    @CBroe, my examples using IDs were more specific than my claims. I updated to generalize and softened the language about performance. A little research shows that the most efficient selector strategy [depends on a lot of factors](http://vaughnroyko.com/the-real-scoop-on-jquery-find-performance/), including whether you're dealing with IDs or less specific elements.. – crenshaw-dev Aug 29 '17 at 20:24
  • 1
    Ok, that we can fully agree on. When it comes to other types of selectors, this "chaining" of course makes sense. With the ids it was just a bit of a misleading example. – CBroe Aug 29 '17 at 20:37
  • @CBroe, yep, thanks for pushing back on that! – crenshaw-dev Aug 29 '17 at 20:49
  • 1
    I think you really need to consider your structure, what you are trying to do, how many different objects you may be 'caching' in your page. Unless you are having serious performance issues you should not need to worry. You should also not need to cache selectors. Personally, I think having a broad range selector and then using find to get child elements is a mistake. I think you would be better off making a more specific selector. You can instead concatenate a string and use that as your selector - `$("#outerDiv1" + " #thing1")` which gets rid of an additional function call to `find()` – Adjit Aug 29 '17 at 21:05

0 Answers0