Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching jquery selector results using underscore memoize

I'm looking into ways to optimize my Single Page Application, and I'm currently focusing on jQuery selectors.

I'm no javascript expert, but it's my understanding that it's much more performant to store the results of a jquery selector in a variable for re-use, as opposed to re-querying the dom.

So, for example, instead of this:

$("#myItem").doSomething();
$("#myItem").doSomethingElse();

It makes more sense to do this:

var result = $("#myItem");
result.doSomething();
result.doSomethingElse();

Also, it's my understanding there can be great performance benefits for using the find() selector attached to an existing jQuery object as to reduce the amount of querying necessary:

$("#myItem").find(".highlighted");

I am interested in exploring the possibility of storing many of the selectors that we constantly reuse in our application not just in a local variable, but possibly in a hash somewhere, or an external function. I don't know if this would optimal, but I'm curious to what responses this receives.

I've attempted (unsuccessfully) to use Underscore.js' memoize function to perform this, but it isn't working the way I expected. But, the concept will look like this:

   jquerySelect = function () {
                var elements = _.memoize(function (selection) {
                    return $(selection);
                });
                return elements;
            };

The idea here being that the actual result work of the "return $(selection)" be performed once, after which the result is cached and returned.

I'd like to be able to use this in the following way:

utils.jquerySelect(".highlighted").find("span")

In this simple example, the key to the cache is the ".highlighted", and the utility function knows how to access the result, thus saving the work of traversing the DOM from being done again. My implementation doesn't work currently, as the "return" statement is hit every time.

I'm not sure if this approach is flawed, but I sense it may be. However, please let me know if you see a way for this to work, or if you see a better way of doing this.

like image 511
letsgetsilly Avatar asked Jun 16 '26 23:06

letsgetsilly


1 Answers

It's not a bad idea, and in fact you can find dozens of similar memoization plugins for JQuery out there already. The major challenge with your approach is knowing when you can use a memoized result, and when it is invalidated due to changes in the DOM. Since JQuery code is just as likely to add, remove, and change nodes in the DOM as they are to select from it, knowing when you must invalidate a selector from your memoized cache is a non-trivial problem.

The way memoize plugins work around this is usually by providing some alternate syntax, such as $_() for selectors you want to be memoized and/or from which you want to retrieve the memoized results. Regardless, though, this adds an additional string/hash lookup to the selector. So, in the end, what's the true advantage of this over caching locally?

Generally speaking, it's better to learn more about how JQuery is essentially a DSL providing monadic transformations to the DOM (also known as chaining).

For example, your code in the question can be written:

$("#myItem").doSomething().doSomethingElse();

You can even use .end() to pop the selector stack, since in a chain results are internally memoized already:

$("#myItem").doSomething().find('a').hide().end().doSomethingElse();
// a in #myItem are hidden, but doSomething() and doSomethingElse() happen to #myItem

Also, the cost of most JQuery selections can be overestimated. ID selects (selectors that begin with #) are passed to fast DOM methods like document.getElementById instead of the Sizzle engine. Repeating calls to selectors that resolve to a single DOM element, e.g. $(this) are of no particular consequence to speed.

If speed is really the concern in a tight loop in your code, it's almost certainly worth it to refactor that section out of using the JQuery selection engine entirely. In my tests, you can get a 10-50% performance gain from local selector caching, depending on the complexity of the selector, and 1000% performance gain from native DOM selectors over JQuery selectors.

like image 57
Plynx Avatar answered Jun 19 '26 17:06

Plynx