-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscroll_follower.js
More file actions
178 lines (156 loc) · 4.46 KB
/
scroll_follower.js
File metadata and controls
178 lines (156 loc) · 4.46 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
* Gives an element the same behavior than the new Gmail toolbar : fix it to the top of the page when
* the user scroll down, and gives it its original position when back to top.
*
* Usage is simple :
* new ScrollFollower('id') ;
* new ScrollFollower('id',{option : value});
*
* Options :
* - autoGhost (bool) : generate or not a ghost for not absolutized element
* - fixedClass (string) : css class to add when the element is fixed
* - absolutizedClass (string) : css class to add when the element is absolutized
*
* Notes :
* - Require Prototype.js (jQuery has its own tools)
* - IE6 don't support the css "fixed" position, so you should avoid using this class with it.
* - I wrote that code in a few minutes and I didn't test it a lot, so it's certainly a little buggy.
*
* @author Sébastien Charrier <scharrier@gmail.com>
* @copyright Copyright 2011, Sébastien Charrier (http://sebastien-charrier.com)
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/
ScrollFollower = Class.create({
/**
* The element we want to follow us
*
* @var Element
*/
_main : null,
/**
* Options
*
* @var Object
*/
_options : null,
/**
* The ghost, for cases where the main element is not absolute : it's
* a hack in order to keep the layout good.
*
* @var Element
*/
_ghost : null,
/**
* Initial top offset of the main element.
*
* @var Object
*/
_fixedOffset : null,
/**
* Offset relative to first parent
*
* @var Object
*/
_positionedOffset : null,
/**
* Offset of the first relative parent
*
* @var Object
*/
_parentOffset : null,
/**
* Calculated top offset
*
* @var Integer
*/
_top : null,
/**
* Flag : actually fixed or not ?
*
* @var bool
*/
_fixed : false,
/**
* Constructor
*
* @param Mixed Id of the element, or the element (String or Element)
*/
initialize : function(id, options) {
this._main = $(id) ;
if (!this._main) {
throw 'Element #' + id + ' doesn\'t exist' ;
}
this._options = $H({
'autoGhost' : true,
'fixedClass' : 'scroll_follower_fixed'
}).merge(options).toObject() ;
// Initialize style properties and offsets
this._initialize() ;
// On scroll : yea baby, keep my toolbar in place
Event.observe(window, 'scroll', this._scroll.bind(this)) ;
},
/**
* Initialize style properties and offsets
*/
_initialize : function() {
// Initial offset
this._fixedOffset = this._main.cumulativeOffset() ;
this._positionedOffset = this._main.positionedOffset() ;
this._parentOffset = this._main.getOffsetParent().cumulativeOffset() ;
this._top = this._positionedOffset.top - this._main.getHeight() ;
// Save the initial style properties we'll have to overwrite
var properties = $H({'position' : 'static'}) ;
if (this._main.style.position) { properties.set('position', this._main.style.position) ; }
if (this._main.style.top) { properties.set('top', this._main.style.top) ; }
if (this._main.style.left) { properties.set('left', this._main.style.left) ; }
this._initialProperties = properties;
// Fix the dimensions
this._main.style.height = this._main.getHeight() + 'px' ;
this._main.style.width = this._main.getWidth() + 'px' ;
},
/**
* Callback : when user scroll, we check if the main element should be fixed
* or absolutized.
*/
_scroll : function() {
if (!this._fixed && this._scrollOffset().top > this._top) {
// Ok, fix it !
this._fix() ;
} else if (this._fixed && this._scrollOffset().top <= this._top) {
// Back to initial : absolutize it
this._reset() ;
}
},
/**
* Fix the element
*/
_fix : function() {
this._main.style.position = 'fixed' ;
this._main.style.top = 0 ;
this._main.style.left = this._fixedOffset.left + 'px' ;
this._main.style.width
this._main.addClassName(this._options.fixedClass) ;
this._fixed = true ;
},
/**
* Reset the element
*/
_reset : function() {
this._main.removeClassName(this._options.fixedClass) ;
if (this._initialProperties.keys().length) {
this._initialProperties.each(function(property) {
this._main.style[property[0]] = property[1] ;
}, this) ;
}
this._fixed = false ;
},
/**
* Calulate current scroll offset of the element
*/
_scrollOffset : function() {
var cumulative = this._main.cumulativeScrollOffset() ;
cumulative.top = cumulative.top - this._parentOffset.top ;
cumulative.left = cumulative.left - this._parentOffset.left ;
return cumulative ;
}
}) ;