diff options
Diffstat (limited to 'js/components/sticky.js')
-rwxr-xr-x | js/components/sticky.js | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/js/components/sticky.js b/js/components/sticky.js new file mode 100755 index 0000000..2765f19 --- /dev/null +++ b/js/components/sticky.js | |||
@@ -0,0 +1,364 @@ | |||
1 | /*! UIkit 2.26.4 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */ | ||
2 | (function(addon) { | ||
3 | |||
4 | var component; | ||
5 | |||
6 | if (window.UIkit) { | ||
7 | component = addon(UIkit); | ||
8 | } | ||
9 | |||
10 | if (typeof define == "function" && define.amd) { | ||
11 | define("uikit-sticky", ["uikit"], function(){ | ||
12 | return component || addon(UIkit); | ||
13 | }); | ||
14 | } | ||
15 | |||
16 | })(function(UI){ | ||
17 | |||
18 | "use strict"; | ||
19 | |||
20 | var $win = UI.$win, | ||
21 | $doc = UI.$doc, | ||
22 | sticked = [], | ||
23 | direction = 1; | ||
24 | |||
25 | UI.component('sticky', { | ||
26 | |||
27 | defaults: { | ||
28 | top : 0, | ||
29 | bottom : 0, | ||
30 | animation : '', | ||
31 | clsinit : 'uk-sticky-init', | ||
32 | clsactive : 'uk-active', | ||
33 | clsinactive : '', | ||
34 | getWidthFrom : '', | ||
35 | showup : false, | ||
36 | boundary : false, | ||
37 | media : false, | ||
38 | target : false, | ||
39 | disabled : false | ||
40 | }, | ||
41 | |||
42 | boot: function() { | ||
43 | |||
44 | // should be more efficient than using $win.scroll(checkscrollposition): | ||
45 | UI.$doc.on('scrolling.uk.document', function(e, data) { | ||
46 | if (!data || !data.dir) return; | ||
47 | direction = data.dir.y; | ||
48 | checkscrollposition(); | ||
49 | }); | ||
50 | |||
51 | UI.$win.on('resize orientationchange', UI.Utils.debounce(function() { | ||
52 | |||
53 | if (!sticked.length) return; | ||
54 | |||
55 | for (var i = 0; i < sticked.length; i++) { | ||
56 | sticked[i].reset(true); | ||
57 | sticked[i].self.computeWrapper(); | ||
58 | } | ||
59 | |||
60 | checkscrollposition(); | ||
61 | }, 100)); | ||
62 | |||
63 | // init code | ||
64 | UI.ready(function(context) { | ||
65 | |||
66 | setTimeout(function(){ | ||
67 | |||
68 | UI.$("[data-uk-sticky]", context).each(function(){ | ||
69 | |||
70 | var $ele = UI.$(this); | ||
71 | |||
72 | if (!$ele.data("sticky")) { | ||
73 | UI.sticky($ele, UI.Utils.options($ele.attr('data-uk-sticky'))); | ||
74 | } | ||
75 | }); | ||
76 | |||
77 | checkscrollposition(); | ||
78 | }, 0); | ||
79 | }); | ||
80 | }, | ||
81 | |||
82 | init: function() { | ||
83 | |||
84 | var boundary = this.options.boundary, boundtoparent; | ||
85 | |||
86 | this.wrapper = this.element.wrap('<div class="uk-sticky-placeholder"></div>').parent(); | ||
87 | this.computeWrapper(); | ||
88 | this.wrapper.css({ | ||
89 | 'margin-top' : this.element.css('margin-top'), | ||
90 | 'margin-bottom' : this.element.css('margin-bottom'), | ||
91 | 'margin-left' : this.element.css('margin-left'), | ||
92 | 'margin-right' : this.element.css('margin-right') | ||
93 | }) | ||
94 | this.element.css('margin', 0); | ||
95 | |||
96 | if (boundary) { | ||
97 | |||
98 | if (boundary === true || boundary[0] === '!') { | ||
99 | |||
100 | boundary = boundary === true ? this.wrapper.parent() : this.wrapper.closest(boundary.substr(1)); | ||
101 | boundtoparent = true; | ||
102 | |||
103 | } else if (typeof boundary === "string") { | ||
104 | boundary = UI.$(boundary); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | this.sticky = { | ||
109 | self : this, | ||
110 | options : this.options, | ||
111 | element : this.element, | ||
112 | currentTop : null, | ||
113 | wrapper : this.wrapper, | ||
114 | init : false, | ||
115 | getWidthFrom : UI.$(this.options.getWidthFrom || this.wrapper), | ||
116 | boundary : boundary, | ||
117 | boundtoparent : boundtoparent, | ||
118 | top : 0, | ||
119 | calcTop : function() { | ||
120 | |||
121 | var top = this.options.top; | ||
122 | |||
123 | // dynamic top parameter | ||
124 | if (this.options.top && typeof(this.options.top) == 'string') { | ||
125 | |||
126 | // e.g. 50vh | ||
127 | if (this.options.top.match(/^(-|)(\d+)vh$/)) { | ||
128 | top = window.innerHeight * parseInt(this.options.top, 10)/100; | ||
129 | // e.g. #elementId, or .class-1,class-2,.class-3 (first found is used) | ||
130 | } else { | ||
131 | |||
132 | var topElement = UI.$(this.options.top).first(); | ||
133 | |||
134 | if (topElement.length && topElement.is(':visible')) { | ||
135 | top = -1 * ((topElement.offset().top + topElement.outerHeight()) - this.wrapper.offset().top); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | } | ||
140 | |||
141 | this.top = top; | ||
142 | }, | ||
143 | |||
144 | reset: function(force) { | ||
145 | |||
146 | this.calcTop(); | ||
147 | |||
148 | var finalize = function() { | ||
149 | this.element.css({"position":"", "top":"", "width":"", "left":"", "margin":"0"}); | ||
150 | this.element.removeClass([this.options.animation, 'uk-animation-reverse', this.options.clsactive].join(' ')); | ||
151 | this.element.addClass(this.options.clsinactive); | ||
152 | this.element.trigger('inactive.uk.sticky'); | ||
153 | |||
154 | this.currentTop = null; | ||
155 | this.animate = false; | ||
156 | |||
157 | }.bind(this); | ||
158 | |||
159 | |||
160 | if (!force && this.options.animation && UI.support.animation && !UI.Utils.isInView(this.wrapper)) { | ||
161 | |||
162 | this.animate = true; | ||
163 | |||
164 | this.element.removeClass(this.options.animation).one(UI.support.animation.end, function(){ | ||
165 | finalize(); | ||
166 | }).width(); // force redraw | ||
167 | |||
168 | this.element.addClass(this.options.animation+' '+'uk-animation-reverse'); | ||
169 | } else { | ||
170 | finalize(); | ||
171 | } | ||
172 | }, | ||
173 | |||
174 | check: function() { | ||
175 | |||
176 | if (this.options.disabled) { | ||
177 | return false; | ||
178 | } | ||
179 | |||
180 | if (this.options.media) { | ||
181 | |||
182 | switch(typeof(this.options.media)) { | ||
183 | case 'number': | ||
184 | if (window.innerWidth < this.options.media) { | ||
185 | return false; | ||
186 | } | ||
187 | break; | ||
188 | case 'string': | ||
189 | if (window.matchMedia && !window.matchMedia(this.options.media).matches) { | ||
190 | return false; | ||
191 | } | ||
192 | break; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | var scrollTop = $win.scrollTop(), | ||
197 | documentHeight = $doc.height(), | ||
198 | dwh = documentHeight - window.innerHeight, | ||
199 | extra = (scrollTop > dwh) ? dwh - scrollTop : 0, | ||
200 | elementTop = this.wrapper.offset().top, | ||
201 | etse = elementTop - this.top - extra, | ||
202 | active = (scrollTop >= etse); | ||
203 | |||
204 | if (active && this.options.showup) { | ||
205 | |||
206 | // set inactiv if scrolling down | ||
207 | if (direction == 1) { | ||
208 | active = false; | ||
209 | } | ||
210 | |||
211 | // set inactive when wrapper is still in view | ||
212 | if (direction == -1 && !this.element.hasClass(this.options.clsactive) && UI.Utils.isInView(this.wrapper)) { | ||
213 | active = false; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | return active; | ||
218 | } | ||
219 | }; | ||
220 | |||
221 | this.sticky.calcTop(); | ||
222 | |||
223 | sticked.push(this.sticky); | ||
224 | }, | ||
225 | |||
226 | update: function() { | ||
227 | checkscrollposition(this.sticky); | ||
228 | }, | ||
229 | |||
230 | enable: function() { | ||
231 | this.options.disabled = false; | ||
232 | this.update(); | ||
233 | }, | ||
234 | |||
235 | disable: function(force) { | ||
236 | this.options.disabled = true; | ||
237 | this.sticky.reset(force); | ||
238 | }, | ||
239 | |||
240 | computeWrapper: function() { | ||
241 | |||
242 | this.wrapper.css({ | ||
243 | 'height' : ['absolute','fixed'].indexOf(this.element.css('position')) == -1 ? this.element.outerHeight() : '', | ||
244 | 'float' : this.element.css('float') != 'none' ? this.element.css('float') : '' | ||
245 | }); | ||
246 | |||
247 | if (this.element.css('position') == 'fixed') { | ||
248 | this.element.css({ | ||
249 | width: this.sticky.getWidthFrom.length ? this.sticky.getWidthFrom.width() : this.element.width() | ||
250 | }); | ||
251 | } | ||
252 | } | ||
253 | }); | ||
254 | |||
255 | function checkscrollposition(direction) { | ||
256 | |||
257 | var stickies = arguments.length ? arguments : sticked; | ||
258 | |||
259 | if (!stickies.length || $win.scrollTop() < 0) return; | ||
260 | |||
261 | var scrollTop = $win.scrollTop(), | ||
262 | documentHeight = $doc.height(), | ||
263 | windowHeight = $win.height(), | ||
264 | dwh = documentHeight - windowHeight, | ||
265 | extra = (scrollTop > dwh) ? dwh - scrollTop : 0, | ||
266 | newTop, containerBottom, stickyHeight, sticky; | ||
267 | |||
268 | for (var i = 0; i < stickies.length; i++) { | ||
269 | |||
270 | sticky = stickies[i]; | ||
271 | |||
272 | if (!sticky.element.is(":visible") || sticky.animate) { | ||
273 | continue; | ||
274 | } | ||
275 | |||
276 | if (!sticky.check()) { | ||
277 | |||
278 | if (sticky.currentTop !== null) { | ||
279 | sticky.reset(); | ||
280 | } | ||
281 | |||
282 | } else { | ||
283 | |||
284 | if (sticky.top < 0) { | ||
285 | newTop = 0; | ||
286 | } else { | ||
287 | stickyHeight = sticky.element.outerHeight(); | ||
288 | newTop = documentHeight - stickyHeight - sticky.top - sticky.options.bottom - scrollTop - extra; | ||
289 | newTop = newTop < 0 ? newTop + sticky.top : sticky.top; | ||
290 | } | ||
291 | |||
292 | if (sticky.boundary && sticky.boundary.length) { | ||
293 | |||
294 | var bTop = sticky.boundary.offset().top; | ||
295 | |||
296 | if (sticky.boundtoparent) { | ||
297 | containerBottom = documentHeight - (bTop + sticky.boundary.outerHeight()) + parseInt(sticky.boundary.css('padding-bottom')); | ||
298 | } else { | ||
299 | containerBottom = documentHeight - bTop; | ||
300 | } | ||
301 | |||
302 | newTop = (scrollTop + stickyHeight) > (documentHeight - containerBottom - (sticky.top < 0 ? 0 : sticky.top)) ? (documentHeight - containerBottom) - (scrollTop + stickyHeight) : newTop; | ||
303 | } | ||
304 | |||
305 | |||
306 | if (sticky.currentTop != newTop) { | ||
307 | |||
308 | sticky.element.css({ | ||
309 | position : "fixed", | ||
310 | top : newTop, | ||
311 | width : sticky.getWidthFrom.length ? sticky.getWidthFrom.width() : sticky.element.width() | ||
312 | }); | ||
313 | |||
314 | if (!sticky.init) { | ||
315 | |||
316 | sticky.element.addClass(sticky.options.clsinit); | ||
317 | |||
318 | if (location.hash && scrollTop > 0 && sticky.options.target) { | ||
319 | |||
320 | var $target = UI.$(location.hash); | ||
321 | |||
322 | if ($target.length) { | ||
323 | |||
324 | setTimeout((function($target, sticky){ | ||
325 | |||
326 | return function() { | ||
327 | |||
328 | sticky.element.width(); // force redraw | ||
329 | |||
330 | var offset = $target.offset(), | ||
331 | maxoffset = offset.top + $target.outerHeight(), | ||
332 | stickyOffset = sticky.element.offset(), | ||
333 | stickyHeight = sticky.element.outerHeight(), | ||
334 | stickyMaxOffset = stickyOffset.top + stickyHeight; | ||
335 | |||
336 | if (stickyOffset.top < maxoffset && offset.top < stickyMaxOffset) { | ||
337 | scrollTop = offset.top - stickyHeight - sticky.options.target; | ||
338 | window.scrollTo(0, scrollTop); | ||
339 | } | ||
340 | }; | ||
341 | |||
342 | })($target, sticky), 0); | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | sticky.element.addClass(sticky.options.clsactive).removeClass(sticky.options.clsinactive); | ||
348 | sticky.element.trigger('active.uk.sticky'); | ||
349 | sticky.element.css('margin', ''); | ||
350 | |||
351 | if (sticky.options.animation && sticky.init && !UI.Utils.isInView(sticky.wrapper)) { | ||
352 | sticky.element.addClass(sticky.options.animation); | ||
353 | } | ||
354 | |||
355 | sticky.currentTop = newTop; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | sticky.init = true; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | return UI.sticky; | ||
364 | }); | ||