Skip to content

Instantly share code, notes, and snippets.

@jtsternberg
Last active January 22, 2018 08:44
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jtsternberg/14978579a9edf42ed069 to your computer and use it in GitHub Desktop.
Save jtsternberg/14978579a9edf42ed069 to your computer and use it in GitHub Desktop.
jQuery selector cache with reset (original: http://eamann.com/tech/selector-caching-jquery/). If commenting, please ping me on Twitter, same username.
function Selector_Cache() {
var elementCache = {};
var get_from_cache = function( selector, $ctxt, reset ) {
if ( 'boolean' === typeof $ctxt ) {
reset = $ctxt;
$ctxt = false;
}
var cacheKey = $ctxt ? $ctxt.selector + ' ' + selector : selector;
if ( undefined === elementCache[ cacheKey ] || reset ) {
elementCache[ cacheKey ] = $ctxt ? $ctxt.find( selector ) : jQuery( selector );
}
return elementCache[ cacheKey ];
};
get_from_cache.elementCache = elementCache;
return get_from_cache;
}
var cache = new Selector_Cache();
// get selector
cache( '#selector' );
// get selector and reset cache
cache( '#selector', true );
// get selector with $context
cache( 'img', cache( '#selector' ) );
// get selector with $context, and reset
cache( 'img', cache( '#selector' ), true );
// get selector with $context, and reset both selector and $context (whoa)
cache( 'img', cache( '#selector', true ), true );
// Manually add a selector
cache.elementCache['#another-id'] = jQuery( document.getElementById( 'another-id' ) );
// Then use it:
cache( '#another-id' );
@salcode
Copy link

salcode commented Aug 15, 2014

While I can't imagine someone passing false as a value for the reset cache, unless I'm mistaken if one were to I think the cache would still be reset. e.g. cache( '#selector', false );

I think if you change line 7 to reset = context;, it would account for this edge case.

@jtsternberg
Copy link
Author

Good call! updated!

@salcode
Copy link

salcode commented Aug 15, 2014

I think the collectionID variable can be moved inside get_from_cache() to make it local to that function call.

@jtsternberg
Copy link
Author

@salcode it could, but I don't see the purpose in it. I am not too worried about polluting scope within Selector_Cache.

@salcode
Copy link

salcode commented Aug 15, 2014

Threading and concurrency in JS are out of my depth so perhaps this isn't even possible but if the same instance were called at the same time wouldn't the collectionID value be shared between them?

@jtsternberg
Copy link
Author

No, the value of collectionID will be explicitly set with every call, thanks to line 10. I/you could declare the var there.

@jtsternberg
Copy link
Author

@bradyvercher
Copy link

Hi Justin, here's what I meant on Twitter about the context selector needing to be unique to prevent collisions. Say you have the following HTML:

<div class="record">
    <ol>
        <li>Track 1</li>
        <li>Track 2</li>
        <li>Track 3</li>
    </ol>
</div>

<div class="record">
    <ol>
        <li>Track A</li>
        <li>Track B</li>
        <li>Track C</li>
    </ol>
</div>

jQuery allows for a DOM Element, document, or jQuery to use as context. So if I needed to perform an action on each record, I could do something like this:

$( '.record' ).each(function( index, el ) {
    // Various methods to cache tracks using a record as context.
    $tracks = $( 'li', this );
    $tracks = $( 'li', el );
    $tracks = $( 'li', $( el ) );

    //...
    // More code using the $tracks collection.
});

With that in mind, this, el, or $( el ) can't be used as context with the selector cache, otherwise it creates collisions in the collections object. So my question was, does this approach assume that context must be a unique string, such as an id or other fully qualified selector?

@jtsternberg
Copy link
Author

@bradyvercher Ah, I thought you might mean something like that. Honestly, I'm not sure, and I'd love to test it. I think trying to use cached variables in a setting like that may be overkill/unnecessary anyway, but still worth looking into ($( '.record' ) should be cached, but the li selector inside the loop probably doesn't need to be as it's unlikely you'd need to refer to it again).

@lakinmohapatra
Copy link

Hi jtsternberg,
Can you please tell me how to use the cache selector after caching it in this case?

Thanks,
Lakin

@jtsternberg
Copy link
Author

@lakinmohapatra everything after var cache = new Selector_Cache(); are example for how to use it. cache( '#selector' );

@afanjul
Copy link

afanjul commented Dec 2, 2015

There is something wrong because calling to cache( '#selector', true ); it fails with: Uncaught TypeError: $ctxt.find is not a function(…)

I think you can fix it setting $ctxt = false when $ctxt is typeof boolean in line 6.

@michael-ecklund
Copy link

First off, @jsternberg this is a very nice snippet. I would love to adopt this into my code, however... I'm having issues with resetting the cached selectors.

Here's my situation...

I have 3 JS files:

  1. File 1: Sets up the variables (caching all the repetitive selectors).
  2. File 2: Manipulates the HTML on the page, using the cached selectors from File 1.
  3. File 3: Manipulates the manipulated HTML from File 2.

Basically each file relies on the file loaded before the current file is loaded.

By the time it gets to File 3, I can't just use the cached selector variable from File 1, I need to reset SOME of the cached selector variables from File 1. inside of File 3, because File 2 has made changes to them.

Specifying: cache( '#selector', true ); in File 3 doesn't appear to be working. I don't get any errors in the console. Just blank. So I'm not sure what's wrong with it, but it doesn't appear to be working as expected.

Just to clarify, the variable still works with what was cached in File 1, BUT it hasn't been updated from the changes made in File 2, when specifying cache( '#selector', true ); in File 3.

I'm just using jQuery('#selector'); to target the modified selector for the time being.

It would be nice to be able to reset the cached selector.

@jtsternberg
Copy link
Author

@afanjul updated, thanks!
@mecklund, see if the changes made based on @afanjul's suggestion resolve your issue.

@rilwis
Copy link

rilwis commented Jan 22, 2018

I have the same problem as @bradyvercher. If context is a simple jQuery selector like $( '#container' ) then it's good. But if it's complicated like $( '#container' ).next() then the code that retrieves context's key won't work reliably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment