I've been optimizing my code and I always tend to cache common selectors like this (jQ):
function init(){
var ob = $('#ob'),
fg = ob.find('.fg');
win = window,
s1 = $('#s1'),
s1em = s1.find('em'),
bodyHtml = $('body,html'),
fboxFirst = ob.find('.first'),
_continue = $('#continue');
/* ...lots of more functions and stuff
here that uses the main variables */
}
init();
I do that since I don't want to limit the querying of the DOM to the stuff that actually is specific for an action:
ob.on('click', '.fbox', function(){
// these need to be quried here
var t = $(this),
tp = t.parent(),
allChild = tp.nextAll('.child'),
tpNext = allChild.first(),
nextClose = tpNext.find('h2');
// no need to query the DOM for these
bodyHtml.doThing(); // bodyHtml = common selector, cache outside scope
fboxFirst.doThing2(); // also common, cache outside scope
})
For me this makes much sense but there is a memory hit since the GC (Garbage Collector) will have a harder time to release memory but there should be more performant due to the fact that less querying of the DOM has to be done on different actions that target the more general DOM elements.
The problem I'm having is that most guides recommend to localize everything but at the same time it doesn't make any sense to query the DOM each time for the same selectors where there is no need. What would the benefit be to localize everything except that any app/website/thing would use a little less memory?
Full code:
/*-----------------------------------------------------
JS + VELOCITY VERBOSE / TMP
+ jquery 2.1.3
+ velocity.js
+ velocity.ui.js
-----------------------------------------------------*/
// INIT
;
(function($, window, document, undefined) {
'use strict';
/* SELECTOR CACHE ------------------*/
var d = document,
_ob = d.getElementById('ob'),
ob = $(_ob),
s1 = d.getElementById('s1'),
body = d.querySelector('body,html'), // used for scrolling
fboxFirst = _ob.querySelectorAll('.first'), // must re-select @ ajax
_continue = d.getElementById('continue'),
win = window,
bestText = d.getElementById('bestText');
/* HELPERS -------------------------------- */
// endsWith
if (typeof String.prototype.endsWith != 'function') {
String.prototype.endsWith = function(str) {
return this.slice(-str.length) == str;
};
}
// regFX helper (velocity.js)
function regFX(name, fx, ease) {
var ease;
if (name.endsWith('In')) {
fx.opacity = [1, 0];
ease = ob_aniEase;
} else {
fx.opacity = [0, 1];
ease = ob_aniEaseOut;
}
fx.translateZ = 0;
$.Velocity.RegisterUI(name, {
defaultDuration: ob_animTime,
calls: [
[fx, 0, {
easing: ease
}]
]
})
}
/* ANIMATION DEFAULTS ------------------*/
var ob_delayTime = 300,
ob_animTime = 200,
ob_doubleTime = 400,
ob_longTime = 800,
ob_aniEase = 'easeOutQuad',
ob_aniEaseOut = 'easeInQuad';
// create fx + set default
regFX('fx.slideIn', {
translateX: [0, -50]
});
regFX('fx.slideOut', {
translateX: 50
});
var animIn = 'fx.slideIn',
animOut = 'fx.slideOut';
/*-----------------------------------------------------
BINDS
-----------------------------------------------------*/
function ob_bindEvents() {
ob.on('click', '.fbox', ob_open);
ob.on('click', '.mtrigger', ob_openMulti);
ob.on('click', '.child h2', ob_close);
$(s1).on('click', ob_closeAll);
}
ob_bindEvents();
/*-----------------------------------------------------
FX FUNCTIONS
require vars from 'animation defaults'
-----------------------------------------------------*/
// Show details + next flight
function nextAnim(element, nextElement, arrow) {
element.velocity('transition.slideDownIn', {
duration: ob_animTime,
delay: ob_animTime,
display: 'block',
complete: function() {
nextElement.velocity(animIn, {
delay: ob_delayTime,
complete: function() { // animate arrow @ h2
arrow.velocity('transition.slideDownIn', {
display: '',
duration: ob_doubleTime
});
}
});
}
});
} // end nextAnim()
// Scroll element with callback
function scrollCallback(element, target, getOffset, callback) {
var offPos = target.offset().top;
offPos = offPos - getOffset;
element.velocity('stop').velocity('scroll', {
duration: ob_doubleTime,
offset: offPos,
complete: callback
});
} // end scrollCallback()
// Check if last element
function isLastBox(element) {
if (element.length < 1) {
$(_continue).velocity(animIn, {
delay: ob_doubleTime * 2
});
}
} // end()
// Button push
function buttonPush(e, callback) {
e.velocity('stop').velocity({
scale: [0.90, 1]
}, {
duration: 60
}).velocity({
scale: 1
}, {
duration: 60,
complete: function() {
if (callback) {
callback();
}
}
});
} // end()
// scrollTop action (re-use scrollCallback instead)
function ob_scrollTop(e) {
var curTop = e.offset().top - 100;
$(body).velocity('stop').velocity('scroll', {
duration: ob_doubleTime,
offset: curTop
}, {
easing: ob_aniEase
})
} // end()
/*-----------------------------------------------------
OPEN FUNCTIONS
-----------------------------------------------------*/
// SINGLE OPEN ---------------------------------------
function ob_open() {
// VARs : this-specific
var t = $(this),
tp = t.parent(),
allChild = tp.nextAll('.child'), // all .child after fbox
tpNext = allChild.first(), // first .child after current .fbox
nextClose = tpNext.find('h2'), // ### fix h2 em prev.Value mux later ###
prevClose = tp.find('h2'),
nextDetails = t.next('.details'); // next .details
// CHECKS
var _isChild = false;
if (t.hasClass('mchild')) {
_isChild = true;
}
var _isMulti = false;
if (tp.hasClass('multi')) {
_isMulti = true;
}
// Single outbound flights
if (!_isMulti || _isChild) {
// CHECKS
var _isFirst;
if (tp.hasClass('first')) {
_isFirst = true;
} else {
_isFirst = false;
}
// ACTIONS
this.className += ' on';
bestText.style.display = 'none'; // fix
// _isFirst
if (_isFirst) {
$(fboxFirst).addClass('off'); // hide all .first (.first's are outside of current flightGroup)
tp.removeClass('off'); // ..but not the current
$(s1).addClass('on'); // show X-mark at first
if (_isChild) { // hide all .fbox, but first
tp.find('.fbox').hide();
t.show();
}
} else {
tp.find('h2').addClass('on');
tp.find('.fbox').addClass('off'); // set OFF on all .fbox in current group (needed for all .child)
t.removeClass('off'); // remove on current
} // end _isFirst
var fa = tpNext.find('h2 .fa');
fa.css('opacity', 0); //tmp
// Scroll to 'this' + run nextAnim()
scrollCallback($(body), t, 60, nextAnim(nextDetails, tpNext, fa));
// Check if last .fbox + run action
isLastBox(allChild);
} // end Single outbound flights
} // end ob_open()
// MULTI OPEN ---------------------------------------
function ob_openMulti() {
// VARs : this-specific
var t = $(this),
tp = t.parent(),
mchild = tp.find('.mchild'),
arrow = t.find('.arrow');
// checks
var isOn = false;
if (t.hasClass('on')) {
isOn = true;
}
if (isOn) {
buttonPush(arrow, function() {
mchild.velocity(animOut);
arrow.velocity({
rotateZ: '0deg'
}, ob_animTime);
});
} else {
ob_scrollTop(t);
buttonPush(arrow, function() {
mchild.velocity(animIn, {
delay: ob_animTime * 1.2
});
arrow.velocity({
rotateZ: '180deg'
}, ob_animTime);
})
}
t.toggleClass('on');
} // end ob_openMulti()
/* -----------------------------------------------------
CLOSE FUNCTIONS
----------------------------------------------------- */
// CLOSE DEFAULT --------------------------------------
function ob_close(e) {
var t = $(this),
tp = t.parent(),
tpNextAll = tp.nextAll('.child');
// actions
//t.addClass('off');
t[0].className += ' off'; // hide X-mark on current
tp.velocity(animOut, {
complete: function() {
// NextAll resets
tp.find('.fbox').removeClass('off on'); // remove local .off/.on
tp.find('.details').hide(); // hide .details on current
tpNextAll.find('.details').hide(); // hide .details on nextAll .child
tpNextAll.children('.fbox').removeClass('off on'); // reset hide in ALL .child(s)
// Change header text
var h2next = tpNextAll.find('h2');
t.removeClass('on off'); // fix?
h2next.removeClass('on'); // fix?
// Animate back
tp.velocity(animIn); // show current flightGroup again
}
});
$(_continue).velocity(animOut);
} // end ob_close()
// CLOSE ALL ----------------------------------------
function ob_closeAll() {
var t = $(this),
fg = ob.find('.fg'),
all = fg.add($(s1));
// checks
var mChilds = $(fboxFirst).find('.mchild');
var _isChild = false;
if (mChilds.hasClass('on')) {
_isChild = true;
}
// add bestText
bestText.style.opacity = 0;
all = all.add($(bestText));
// fix carrier flicker
var carrier = $(fboxFirst).find('.carrier'); // limit to .fbox.first only
// Animations
all.stop().velocity(animOut, {
display: false,
stagger: ob_animTime / 4,
// everything animated out
complete: function() {
var h2 = ob.find('h2'),
fbox = ob.find('.fbox'),
details = ob.find('.details'),
child = ob.find('.child');
// perform functions
details.add(child).hide();
h2.add(fbox).add($(fboxFirst)).removeClass('on off');
// show best text
bestText.style.display = '';
// hide carrier, else flickers
carrier.hide();
// MULTI-CHILD CLOSE
if (_isChild) {
var mTrigger = $(fboxFirst).find('.mtrigger');
mTrigger.removeClass('on').show();
mTrigger.find('.arrow')[0].removeAttribute('style'); // clean styles
mChilds.hide();
all.add(mTrigger);
}
// perform IN-animation
all.velocity(animIn, {
display: false,
stagger: ob_animTime / 4,
complete: function() {
carrier.show();
}
});
} //onComplete
})
$(_continue).velocity(animOut);
} // end closeAll()
})(jQuery, window, document); // end() init