summaryrefslogtreecommitdiff
path: root/js/components/sticky.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/components/sticky.js')
-rwxr-xr-xjs/components/sticky.js364
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});