Skip to content

Instantly share code, notes, and snippets.

@joecritch
Last active December 15, 2015 15:58
Show Gist options
  • Save joecritch/5285287 to your computer and use it in GitHub Desktop.
Save joecritch/5285287 to your computer and use it in GitHub Desktop.
This is a 'utility-belt' mixin for Twitter Flight that improves querying and caching of set's of jQuery selectors. It exposes a clean API for searching through a 'cached tree' of elements.
///////////////////////
// Example of how to use this mixin....
// - Think of UI components that can
define(
['flight/lib/component', 'mixins/with_set_cache'],
function(defineComponent, withSetCache, _) {
return defineComponent(myComponent, withSetCache);
function myComponent() {
// Configuration
this.defaultAttrs({
// You can set-up a map of the different selectors, and how 'deep' they go.
// ... '0' is the root element, but you can always caches selectors inside or outside of it.
// (Part of Flight's this.attr configuration)
dict: {
'$el': {selector: '.js-confirm', level: 0}, // Used for the loading of the vote.
'$inner': {selector: '.js-confirm__el', level: 1}, // Used to apply the class to.
'$outer': {selector: '.js-confirm-outer', level: -1}, // Used as the hit state.
'$overlay': {selector: '.inline-overlay', level: 1}
}
});
this.after('initialize', function() {
// Use 'getSelectorByKey' to output the selector associated with a key.
// ... and use this.node as more of a wrapper for multiple sub-nodes (of which you're more concerned about.)
this.$node.on('mouseleave', this.getSelectorByKey('$overlay'), this.onMouseOut);
// Use 'recacheSelectors' to re-build the selectors.
this.on(document, 'page:change', this.recacheSelectors);
});
this.examplePublicMethod = function() {
var _this = this;
// Use 'getElementsByKey' to loop through each set in the cache, and filter it by a particular key.
// e.g, get all root elements, or get outer $outer elements.
var elements = this.getElementsByKey('$el');
_.each(elements, function(v) { _this.trigger('cancel'); });
};
this.exampleEventHandler = function(e) {
// You can look-up selectors that were passed in from an event.
// This essentially allows your component to handle multiple child-nodes.
// ... instead of having an instance of a component for every node.
var set = this.getSetByNode(e.currentTarget);
/// ... which reveals...
console.log(set.$outer, set.$el);
};
}
}
);
//////
///// - This is a 'utility-belt' mixin for Twitter Flight that improves querying and caching of set's of jQuery selectors.
///// - It exposes a clean API for searching through a 'cached tree' of elements.
define(
[],
function() {
function withSetCache() {
this.selectorCache = [];
var dictTest = {};
// Quickly dip into the jQuery selector by caching as a dictionary.
this.getSetByNode = function(node, nodeKey) {
if(typeof nodeKey === 'undefined') nodeKey = this.getDefaultKey();
// Look for an existing cached version.
// Note: this will only find the first element, so it's only for 'singleton' DOM elements.
var elemSet = _.find(this.selectorCache, function(v) {
return v[nodeKey][0] === node;
});
// No cached version exist, but let's find it anyway.
if(!elemSet || !elemSet.length) {
elemSet = this.addSetToCache(node, nodeKey);
}
return elemSet;
};
this.addSetToCache = function(node, nodeKey) {
var elemSet = { };
var keySelector = dictTest[nodeKey];
elemSet[nodeKey] = $(node, this.$node);
// Add all the other selectors to the set.
_.each(dictTest, function(v, k) {
if(k === nodeKey) return false; // Don't add the key selector again.
var difference = (v.level - keySelector.level);
var traverseFn = (difference > 0) ? 'find' : 'closest';
elemSet[k] = elemSet[nodeKey][traverseFn](v.selector);
});
this.selectorCache.push(elemSet);
return elemSet;
};
/**
* Because we've got elements cached already,
* it's quicker to use the cached array, and return in a $ wrapper
* than it would be to reselect by string again.
*/
this.getElementsByKey = function(key) {
var $elements;
key = key || this.getDefaultKey();
var elements = _.map(this.selectorCache, function(v) {
return v[key];
});
$elements = elements[0];
_.each(elements, function(v, k) {
if(k === 0) return true;
$elements = $elements.add(v);
});
return $elements;
};
this.getDefaultKey = function() {
var key;
_.each(dictTest, function(v, k) {
if(v.level === 0) {
key = k;
return false;
}
});
return key;
};
this.getSelectorByKey = function(key) {
return dictTest[key].selector;
};
/**
* Purge the cache and rebuild with new jQuery selectors.
*/
this.recacheSelectors = function(dict) {
var _this = this;
// This function acts as an opportunity to set the dictionary
// if this is the first call of caching selectors
if(dict && !(dict instanceof jQuery.Event)) dictTest = _.clone(dict);
this.selectorCache = [];
var defaultKey = this.getDefaultKey();
var $els = $(dictTest[defaultKey].selector);
$els.each(function() {
_this.addSetToCache(this, defaultKey);
});
};
}
return withSetCache;
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment