Skip to content

Instantly share code, notes, and snippets.

@corford
Last active December 23, 2015 21:39
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 corford/6697919 to your computer and use it in GitHub Desktop.
Save corford/6697919 to your computer and use it in GitHub Desktop.
Jquery plugin to fake 'position: sticky'
// Jquery plugin to fake 'position: sticky' (note: relies on underscore.js for throttling, debouncing and passing in options)
(function ($, _) {
$.fn.fakeSticky = function (options) {
var opts = _.extend({
width_guide_id: 'fake-sticky-width-guide', // This allows the sticky element to support having a % based width value (useful for responsive designs)
pin_boundary_offset: 0, // Increase this if you want to prevent the bottom edge of the sticky element getting too close to the bottom edge of its container
scroll_throttle_ms: 15,
debounce_timer_ms: 25
}, options);
return this.each(function () {
var $this = $(this),
$offset_parent = $this.offsetParent(),
$window = $(window),
$document = $(document),
$width_guide_el = $('<div id=' + opts.width_guide_id + '></div>').insertBefore($this),
initial_top_pos = Math.floor($this.offset().top - parseFloat($this.css('marginTop').replace(/auto/, 0))),
pin_boundary_offset = opts.pin_boundary_offset,
standard = true,
floating = false,
pinned = false,
scroll = function() {
var pos = $this[0].getBoundingClientRect(),
top_margin = parseFloat($this.css('marginTop').replace(/auto/, 0)),
top = pos.top - top_margin,
height = $this.outerHeight(true),
parent_height = $offset_parent.height(),
parent_top_padding = parseFloat($offset_parent.css('paddingTop').replace(/auto/, 0)),
parent_offset_top = $offset_parent.offset().top;
if ($offset_parent.height() > $this.outerHeight(true)) {
var pin_boundary = Math.ceil(parent_offset_top + parent_top_padding + parent_height - height - pin_boundary_offset),
scroll_pos_y = $window.scrollTop();
if ((scroll_pos_y > initial_top_pos) && (scroll_pos_y < pin_boundary) && (floating === false) && (pinned === false)) {
floatIt(0);
} else if ((scroll_pos_y > initial_top_pos) && (scroll_pos_y < pin_boundary) && (pinned) && (top >= 0)) {
floatIt(top);
} else if ((scroll_pos_y >= pin_boundary) && (pinned === false)) {
pinIt();
} else if ((scroll_pos_y <= initial_top_pos) && (floating)) {
restoreIt();
} else {
// Noop
}
}
},
resize = function() {
$this.css({width: $width_guide_el.width()});
};
function floatIt(top) {
var width = $width_guide_el.width();
floating = true;
standard = false;
pinned = false;
$this.css({
position: 'fixed',
top: top,
bottom: '',
width: width
});
}
function pinIt() {
var width = $width_guide_el.width(),
bottom_edge = $this.offset().top + $this.outerHeight(),
parent_bottom_edge = $offset_parent.offset().top + $offset_parent.outerHeight(),
bottom = parent_bottom_edge - bottom_edge;
$this.css({
position: 'absolute',
top: '',
bottom: (bottom >= pin_boundary_offset) ? bottom : pin_boundary_offset,
width: width
});
pinned = true;
}
function restoreIt() {
standard = true;
floating = false;
pinned = false;
$this.css({
position: '',
top: '',
bottom: '',
width: ''
});
}
$window.scroll(_.throttle(scroll, opts.scroll_throttle_ms));
$window.resize(_.debounce(resize, opts.debounce_timer_ms));
});
};
})(jQuery, _);
// Example: Make a 'filters' div appear to stick to the right of a main results div
// and ensure the bottom edge of the filters div never gets closer than 50px to the
// bottom edge of its container
$('#filters').fakeSticky({pin_boundary_offset: 50});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment