-
Notifications
You must be signed in to change notification settings - Fork 6
/
jquery.stickysidebar.js
128 lines (100 loc) · 3.66 KB
/
jquery.stickysidebar.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* jQuery stickySidebar
*
* Copyright (c) 2011 Ca-Phun Ung <caphun at yelotofu dot com>
* Licensed under the MIT (MIT-LICENSE.txt) license.
*
* http://github.com/caphun/jquery.stickysidebar/
*
* Plugin to handle sticky sidebars.
*
*/
(function($){
// cached values
var namespace = '.stickySidebar',
stickyPrevScrollTop = 'prevScrollTop'+namespace,
stickyInitPosLeft = 'initPosLeft'+namespace,
stickyInitPosLeftGutter = 'initPosLeftGutter'+namespace,
$window = $(window),
stickyTimeout;
// define stickySidebar method
$.fn.stickySidebar = function( options ) {
return this.each(function() {
new $.stickySidebar( this, options );
});
}
// plugin constructor
$.stickySidebar = function( elem, options ) {
// deep extend
this.options = $.extend(true, {}, $.stickySidebar.defaults, options );
// the original element | the parent element
this.element = $( elem );
this.parentElem = this.element.parent();
this.naturalPosition = this.element.css('position');
// run
this.init();
}
// plugin defaults
$.stickySidebar.defaults = {
gutter: 0 // defines the space to leave above the sidebar when it's sticky - default 0
}
// plugin prototypes
$.stickySidebar.prototype = {
init: function() {
// save initial values
this.initValues();
// define self
var self = this;
// here comes the magic!
$window.bind(['resize'+namespace, 'scroll'+namespace].join(', '), function() {
// throttle execution (your cpu will like this!)
clearTimeout(stickyTimeout);
stickyTimeout = setTimeout(function(){
self.stickiness().data(stickyPrevScrollTop, $window.scrollTop());
}, 10);
});
},
// actually, all the logic is here!!!
stickiness: function() {
var scrollTop = $window.scrollTop(),
elem = this.element, elemTop = elem.offset().top, elemHeight = elem.outerHeight(),
pTop = this.parentElem.offset().top, pHeight = this.parentElem.outerHeight(),
down = (scrollTop > elem.data(stickyPrevScrollTop)),
// check natural flow left (useful on window resize and liquid columns)
naturalLeft = elem.css({position: 'static'}).offset().left;
// reset left position if natural flow left has changed and natural position is not absolute/fixed
if (naturalLeft !== elem.data(stickyInitPosLeft) && !/absolute|fixed/.test(this.naturalPosition)) {
elem.data(stickyInitPosLeft, naturalLeft);
}
return elem.css(
// if reached the element minus some gutter space we're entering sticky territory.
scrollTop > pTop - this.options.gutter
// if scrolling down check if reached the bottom of the parent container
&& ( (down && (pTop + pHeight > elemTop + elemHeight))
// if scrolling up check if scrollTop is less than the element top position
|| (!down && scrollTop <= elemTop) )
// sticky
? {
position: 'fixed',
top: 0,
left: elem.data(stickyInitPosLeft)
}
// not sticky
: {
position: 'absolute',
top: (pTop + pHeight <= elemTop + elemHeight ? pHeight - elemHeight : 0) - this.options.gutter,
marginTop: this.options.gutter,
left: elem.data(stickyInitPosLeft) - elem.data(stickyInitPosLeftGutter)
}
);
},
// save initial positions and values
initValues: function(resized) {
var elem = this.element;
elem
.data(stickyInitPosLeft, elem.offset().left - parseInt(elem.css('marginLeft')))
.data(stickyInitPosLeftGutter, elem.offset().left - elem.position().left)
.css({ position: 'absolute', left: elem.position().left });
}
}
})(jQuery);