Skip to content

Instantly share code, notes, and snippets.

@ifthenelse
Forked from chriskoelle/sticky.js
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ifthenelse/55f1e3226e263ca1c9bb to your computer and use it in GitHub Desktop.
Save ifthenelse/55f1e3226e263ca1c9bb to your computer and use it in GitHub Desktop.
Add an affix behavior to an element, by keeping it sticky between 2 elements.
;(function($) {
$.fn.extend({
stickyWidget: function(options) {
// Exit if there are no elements to avoid errors:
if (this.length === 0) {
return this;
}
var settings = $.extend({
header: '', // The page header or any element that the sticky module should be affixed
footer: '', // The page footer or any element that the sticky module should stop when it hits
minWidth: 980, // The minimum width to enable sticky - usually the width of the content area
threshold: 60, // An extra buffer added to the height when determining whether or not to enable
bufferTop: 0, // Distance from top of page element should stick (added to element bottom margin)
bufferBottom: 0 // Distance from footer to stop (added to element bottom margin)
}, options);
var sticky = {
el: $(this),
stickyLeft: 0,
stickyTop: 0,
stickyHeight: 0,
win: $(window),
header: false,
footer: false,
headerHasChanged: null,
threshold: settings.bufferTop + settings.threshold + settings.bufferBottom
};
// Orginal positioning
var orig = {
mar: $(this).css('margin-left'),
left: $(this).css('left'),
top: $(this).css('top'),
pos: $(this).css('position')
}
var timer;
var stickyEnabled = true;
cacheElements();
return this.each(function() {
buildSticky();
});
function buildSticky() {
sticky.win.bind({
load: setupSticky,
sticky: setupSticky,
scroll: function () {
if (sticky.header.length) {
if (sticky.header.height() <= 158) { // yeah, magic static value
if (sticky.headerHasChanged == null || sticky.headerHasChanged == 0) {
setupSticky();
calculateLimits();
sticky.headerHasChanged = 1;
}
stick();
} else {
if (sticky.headerHasChanged == null || sticky.headerHasChanged == 1) {
setupSticky();
calculateLimits();
sticky.headerHasChanged = 0;
}
}
} else {
stick();
}
},
resize: function() {
clearTimeout(timer);
timer = window.setTimeout(enableSticky, 200);
}
});
}
function setupSticky() {
console.log("setupsticky!");
var mid = sticky.win.width() / 2;
var widgetLeft = sticky.el.offset().left;
sticky.stickyWidth = sticky.el.width();
sticky.stickyLeft = widgetLeft - parseInt(mid, 10);
sticky.stickyTop = sticky.el.offset().top;
sticky.stickyHeight = sticky.el.outerHeight(true);
enableSticky();
}
// Caches elements into jquery objects
function cacheElements() {
if (settings.header !== '' && $(settings.header).length !== 0) {
sticky.header = $(settings.header);
}
if(settings.footer !== '' && $(settings.footer).length !== 0) {
sticky.footer = $(settings.footer);
}
}
// Only enable the sticky module if it's not going to break anything
function enableSticky() {
var ft = sticky.footer ? sticky.footer.offset().top : $(document).height();
var track = (ft - sticky.threshold) >= (sticky.stickyHeight + sticky.stickyTop) &&
// do not check whether sticky content is taller than window height
//sticky.stickyHeight < sticky.win.height() &&
sticky.win.width() >= settings.minWidth &&
'ontouchstart' in window === false;
if( track ) {
stickyEnabled = true;
} else {
stickyEnabled = false;
}
calculateLimits();
stick();
}
// Calcualtes the limits top and bottom limits for the sidebar
function calculateLimits() {
sticky.stickyStart = sticky.stickyTop - settings.bufferTop;
if(sticky.footer) {
sticky.stickyStop = sticky.footer.offset().top - sticky.stickyHeight - settings.bufferTop - settings.bufferBottom;
}
}
// Sets widget to fixed position
function setFixedWidget() {
sticky.el.css({
position: 'fixed',
top: settings.bufferTop,
left: '50%',
marginLeft: sticky.stickyLeft,
width: sticky.stickyWidth + "px"
});
}
// sets widget to a static positioned element
function setStaticWidget() {
sticky.el.removeAttr("style");
}
// initiated to stop the widget from intersecting the footer
function setLockedWidget(diff) {
sticky.el.css({
top: diff
});
}
//determines whether sidebar should stick and applies appropriate settings to make it stick
function stick() {
var windowTop = sticky.win.scrollTop();
var hitBreakPoint = stickyEnabled && sticky.stickyStart < windowTop;
if (hitBreakPoint) {
setFixedWidget();
} else {
setStaticWidget();
}
if (sticky.footer && sticky.stickyStop < windowTop) {
var diff = sticky.stickyStop - windowTop + settings.bufferTop;
setLockedWidget(diff);
}
}
}
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment