6

There are quite a few questions that ask about caching jQuery objects but I can't find one that asks where exactly jQuery objects can and should be cached. I have a web page that has a single JavaScript file with a bunch of functions as shown below.

$(document).ready(function () {

    // do some setup
});

/* function queries the DOM with the same selector multiple times without caching */
function myFunctionOne() {

    $('#name_input').css("border","1px solid #ccc");
    $('#name_input').val(someValue);        
}

/* function uses cached object from a single query */
function myFunctionTwo() {

    var nameInput = $('#name_input')
    nameInput.css("border","1px solid #ccc");
    nameInput.val(someValue);
    // do some other stuff with cached selector        
}

In myFunctionOne I inefficiently query the DOM twice whereas in myFunctionTwo I query the DOM once, cache the result in a local variable, then work with that variable. I understand that the approach in myFunctionTwo is more efficient but I am unsure as to where I should actually be caching those objects. At the minute I am caching the object at the method level but I am wondering if I can actually cache it at a higher level and then use it across multiple functions. This way I would only query the DOM once and then reuse the result in all functions in this file. An example of what I am suggesting is shown below.

$(document).ready(function () {

    // do some setup
    var nameInput = $('#name_input')
});

/* function uses cached query result from .ready function above */
function myFunctionOne() {

    nameInput .css("border","1px solid #ccc");
    nameInput .val(someValue);        
}

/* function uses cached query result from .ready function above */
function myFunctionTwo() {

    nameInput.val(someValue);
    // do some other stuff with cached selector        
}

Is this approach sensible or is there a better way of doing it? Perhaps using the .ready function is a bad place to do this kind of setup as it will slow down page load? Is there an alternative way to cache jQuery objects at an object level or should they only be cached at a function level?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Brian
  • 217
  • 1
  • 2
  • 11

3 Answers3

8

As always in these kinds of questions, don't optimize prematurely. In this case, it means don't use any caching until you notice performance problems. If your target customers use low-spec computers or mobile devices, this means you would want to test on low-spec hardware yourself so you can identify such issues. I would strongly recommend you go for clarity before trying to improve speed by adding caching.

Some further points:

  • If you are using a selector that uses an ID, it should be fast as it will use getElementById behind the scenes, so that shouldn't need caching.
  • Use method chaining instead of caching, which will only use the selector once
  • If you do implement caching, do it locally first. Caching between methods will cost more in code complexity than local caching.
  • Using .ready is fine. It runs after the DOM has been loaded, and is the exactly right place to do setup tasks.
Peter
  • 127,331
  • 53
  • 180
  • 211
  • 1
    I agree with your point that its important not to optimise prematurely, and in my case it isn't premature. I'm seeing performance issues in IE8 and in attempt to rectify this we are looking at some of the recommended best practices for jQuery, one of which is caching selectors. I intend to use local caching at a minimum and caching between methods if possible. You said, "Caching between methods will cost more in code complexity than local caching". Can you elaborate on this please, as this may help answer my original question? – Brian Feb 19 '13 at 23:12
1

One approach could be to store the selector in the $.cache:

$(function(){

      var $nameInput = $('#name_input');

      $('body').data({ nameInput: $nameInput });

});

And then access it using:

function myFunctionOne() {

    var $nameInput = $('body').data('nameInput');
}
Johan
  • 35,120
  • 54
  • 178
  • 293
  • This has no benefits compared to just using your own variable (avoiding a global if desired -- and it should be desired -- with a wrapper IIFE). But it has complexity and performance costs compared to using your own variable. – T.J. Crowder Apr 13 '17 at 09:22
-1

This code will obfuscate nicely. You can add as many controls as you want in _initControls and use them later throughout this script block. If you want it global to all script blocks then move the _initControls function out of the jQuery onreadystate function.

 $(function () {
    "use strict";

    var _controls = {},
    _init = function () {
        _initControls();
        _updateData("Hello");
    },
    _initControls = function () {            
        _controls.nameInput = $("#name_input");
    },
    _updateData = function (value) {
        var nameInput = _controls.nameInput;
        nameInput.css("border", "1px solid #ccc");
        nameInput.val(value);
    };

    _init();
});
raff
  • 9
  • 1