Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

commented Aug 15, 2014

Good call! updated!

@salcode

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

commented Aug 15, 2014

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

@salcode

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

commented Aug 15, 2014

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

@jtsternberg

This comment has been minimized.

Copy link
Owner Author

commented Aug 15, 2014

@bradyvercher

This comment has been minimized.

Copy link

commented Aug 17, 2014

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

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2014

@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

This comment has been minimized.

Copy link

commented Jun 3, 2015

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

Thanks,
Lakin

@jtsternberg

This comment has been minimized.

Copy link
Owner Author

commented Jul 8, 2015

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

@afanjul

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented Feb 8, 2016

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

This comment has been minimized.

Copy link
Owner Author

commented Feb 9, 2016

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

@rilwis

This comment has been minimized.

Copy link

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
You can’t perform that action at this time.