PageSourceSearch

https://cdn.bmwblog.com/wp-content/themes/wpdevel_bmwblog_production_v2/assets/js/sticky-sidebar.js

js bmwblog.com collected 2026-09-24 08:39:07 UTC 19,127 bytes, 476 lines download raw bytes

1// sidebar
2/*!
3 * Theia Sticky Sidebar v1.7.0
4 * https://github.com/WeCodePixels/theia-sticky-sidebar
5 *
6 * Glues your website's sidebars, making them permanently visible while scrolling.
7 *
8 * Copyright 2013-2016 WeCodePixels and other contributors
9 * Released under the MIT license
10 */
11
12(function ($) {
13    $.fn.theiaStickySidebar = function (options) {
14        var defaults = {
15            'containerSelector': '',
16            'additionalMarginTop': 0,
17            'additionalMarginBottom': 0,
18            'updateSidebarHeight': true,
19            'minWidth': 0,
20            'disableOnResponsiveLayouts': true,
21            'sidebarBehavior': 'modern',
22            'defaultPosition': 'relative',
23            'namespace': 'TSS'
24        };
25        options = $.extend(defaults, options);
26
27        // Validate options
28        options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0;
29        options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0;
30
31        tryInitOrHookIntoEvents(options, this);
32
33        // Try doing init, otherwise hook into window.resize and document.scroll and try again then.
34        function tryInitOrHookIntoEvents(options, $that) {
35            var success = tryInit(options, $that);
36
37            if (!success) {
38                console.log('TSS: Body width smaller than options.minWidth. Init is delayed.');
39
40                $(document).on('scroll.' + options.namespace, function (options, $that) {
41                    return function (evt) {
42                        var success = tryInit(options, $that);
43
44                        if (success) {
45                            $(this).unbind(evt);
46                        }
47                    };
48                }(options, $that));
49                $(window).on('resize.' + options.namespace, function (options, $that) {
50                    return function (evt) {
51                        var success = tryInit(options, $that);
52
53                        if (success) {
54                            $(this).unbind(evt);
55                        }
56                    };
57                }(options, $that))
58            }
59        }
60
61        // Try doing init if proper conditions are met.
62        function tryInit(options, $that) {
63            if (options.initialized === true) {
64                return true;
65            }
66
67            if ($('body').width() < options.minWidth) {
68                return false;
69            }
70
71            init(options, $that);
72
73            return true;
74        }
75
76        // Init the sticky sidebar(s).
77        function init(options, $that) {
78            options.initialized = true;
79
80            // Add CSS
81            var existingStylesheet = $('#theia-sticky-sidebar-stylesheet-' + options.namespace);
82            if (existingStylesheet.length === 0) {
83                $('head').append($('<style id="theia-sticky-sidebar-stylesheet-' + options.namespace + '">.theiaStickySidebar:after {content: ""; display: table; clear: both;}</style>'));
84            }
85
86            $that.each(function () {
87                var o = {};
88
89                o.sidebar = $(this);
90
91                // Save options
92                o.options = options || {};
93
94                // Get container
95                o.container = $(o.options.containerSelector);
96                if (o.container.length == 0) {
97                    o.container = o.sidebar.parent();
98                }
99
100                // Create sticky sidebar
101                o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574
102                o.sidebar.css({
103                    'position': o.options.defaultPosition,
104                    'overflow': 'visible',
105                    // The "box-sizing" must be set to "content-box" because we set a fixed height to this element when the sticky 
105sidebar has a fixed position.
106                    '-webkit-box-sizing': 'border-box',
107                    '-moz-box-sizing': 'border-box',
108                    'box-sizing': 'border-box'
109                });
110
111                // Get the sticky sidebar element. If none has been found, then create one.
112                o.stickySidebar = o.sidebar.find('.theiaStickySidebar');
113                if (o.stickySidebar.length == 0) {
114                    // Remove <script> tags, otherwise they will be run again when added to the stickySidebar.
115                    var javaScriptMIMETypes = /(?:text|application)\/(?:x-)?(?:javascript|ecmascript)/i;
116                    o.sidebar.find('script').filter(function (index, script) {
117                        return script.type.length === 0 || script.type.match(javaScriptMIMETypes);
118                    }).remove();
119
120                    o.stickySidebar = $('<div>').addClass('theiaStickySidebar').append(o.sidebar.children());
121                    o.sidebar.append(o.stickySidebar);
122                }
123
124                // Get existing top and bottom margins and paddings
125                o.marginBottom = parseInt(o.sidebar.css('margin-bottom'));
126                o.paddingTop = parseInt(o.sidebar.css('padding-top'));
127                o.paddingBottom = parseInt(o.sidebar.css('padding-bottom'));
128
129                // Add a temporary padding rule to check for collapsable margins.
130                var collapsedTopHeight = o.stickySidebar.offset().top;
131                var collapsedBottomHeight = o.stickySidebar.outerHeight();
132                o.stickySidebar.css('padding-top', 1);
133                o.stickySidebar.css('padding-bottom', 1);
134                collapsedTopHeight -= o.stickySidebar.offset().top;
135                collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight;
136                if (collapsedTopHeight == 0) {
137                    o.stickySidebar.css('padding-top', 0);
138                    o.stickySidebarPaddingTop = 0;
139                }
140                else {
141                    o.stickySidebarPaddingTop = 1;
142                }
143
144                if (collapsedBottomHeight == 0) {
145                    o.stickySidebar.css('padding-bottom', 0);
146                    o.stickySidebarPaddingBottom = 0;
147                }
148                else {
149                    o.stickySidebarPaddingBottom = 1;
150                }
151
152                // We use this to know whether the user is scrolling up or down.
153                o.previousScrollTop = null;
154
155                // Scroll top (value) when the sidebar has fixed position.
156                o.fixedScrollTop = 0;
157
158                // Set sidebar to default values.
159                resetSidebar();
160
161                o.onScroll = function (o) {
162                    // Stop if the sidebar isn't visible.
163                    if (!o.stickySidebar.is(":visible")) {
164                        return;
165                    }
166
167                    // Stop if the window is too small.
168                    if ($('body').width() < o.options.minWidth) {
169                        resetSidebar();
170                        return;
171                    }
172
173                    // Stop if the sidebar width is larger than the container width (e.g. the theme is responsive and the sidebar is now below the content)
174                    if (o.options.disableOnResponsiveLayouts) {
175                        var sidebarWidth = o.sidebar.outerWidth(o.sidebar.css('float') == 'none');
176
177                        if (sidebarWidth + 50 > o.container.width()) {
178                            resetSidebar();
179                            return;
180                        }
181                    }
182
183                    var scrollTop = $(document).scrollTop();
184                    var position = 'static';
185
186                    // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position.
187                    if (scrollTop >= o.sidebar.offset().top + (o.paddingTop - o.options.additionalMarginTop)) {
188                        // The top and bottom offsets, used in various calculations.
189                        var offsetTop = o.paddingTop + options.additionalMarginTop;
190                        var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom;
191
192                        // All top and bottom positions are relative to the window, not to the parent elemnts.
193                        var containerTop = o.sidebar.offset().top;
194                        var containerBottom = o.sidebar.offset().top + getClearedHeight(o.container);
195
196                        // The top and bottom offsets relative to the window screen top (zero) and bottom (window height).
197                        var windowOffsetTop = 0 + options.additionalMarginTop;
198                        var windowOffsetBottom;
199
200                        var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height();
201                        if (sidebarSmallerThanWindow) {
202                            windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight();
203                        }
204                        else {
205                            windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom;
206                        }
207
208                        var staticLimitTop = containerTop - scrollTop + o.paddingTop;
209                        var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom;
210
211                        var top = o.stickySidebar.offset().top - scrollTop;
212                        var scrollTopDiff = o.previousScrollTop - scrollTop;
213
214                        // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate.
215                        if (o.stickySidebar.css('position') == 'fixed') {
216                            if (o.options.sidebarBehavior == 'modern') {
217                                top += scrollTopDiff;
218                            }
219                        }
220
221                        if (o.options.sidebarBehavior == 'stick-to-top') {
222                            top = options.additionalMarginTop;
223                        }
224
225                        if (o.options.sidebarBehavior == 'stick-to-bottom') {
226                            top = windowOffsetBottom - o.stickySidebar.outerHeight();
227                        }
228
229                        if (scrollTopDiff > 0) { // If the user is scrolling up.
230                            top = Math.min(top, windowOffsetTop);
231                        }
232                        else { // If the user is scrolling down.
233                            top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight());
234                        }
235
236                        top = Math.max(top, staticLimitTop);
237
238                        top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight());
239
240                        // If the sidebar is the same height as the container, we won't use fixed positioning.
241                        var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight();
242
243                        if (!sidebarSameHeightAsContainer && top == windowOffsetTop) {
244                            position = 'fixed';
245                        }
246                        else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) {
247                            position = 'fixed';
248                        }
249                        else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) {
250                            // Stuck to the top of the page. No special behavior.
251                            position = 'static';
252                        }
253                        else {
254                            // Stuck to the bottom of the page.
255                            position = 'absolute';
256                        }
257                    }
258
259                    /*
260                     * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change.
261                     * It's way slower to first check if the values have changed.
262                     */
263                    if (position == 'fixed') {
264                        var scrollLeft = $(document).scrollLeft();
265
266                        o.stickySidebar.css({
267                            'position': 'fixed',
268                            'width': getWidthForObject(o.stickySidebar) + 'px',
269                            'transform': 'translateY(' + top + 'px)',
270                            'left': (o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left')) - scrollLeft) + 'px',
271                            'top': '0px'
272                        });
273                    }
274                    else if (position == 'absolute') {
275                        var css = {};
276
277                        if (o.stickySidebar.css('position') != 'absolute') {
278                            css.position = 'absolute';
279                            css.transform = 'translateY(' + (scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom) + 'px)';
280                            css.top = '0px';
281                        }
282
283                        css.width = getWidthForObject(o.stickySidebar) + 'px';
284                        css.left = '';
285
286                        o.stickySidebar.css(css);
287                    }
288                    else if (position == 'static') {
289                        resetSidebar();
290                    }
291
292                    if (position != 'static') {
293                        if (o.options.updateSidebarHeight == true) {
294                            o.sidebar.css({
295                                'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom
296                            });
297                        }
298                    }
299
300                    o.previousScrollTop = scrollTop;
301                };
302
303                // Initialize the sidebar's position.
304                o.onScroll(o);
305
306                // Recalculate the sidebar's position on every scroll and resize.
307                $(document).on('scroll.' + o.options.namespace, function (o) {
308                    return function () {
309                        o.onScroll(o);
310                    };
311                }(o));
312                $(window).on('resize.' + o.options.namespace, function (o) {
313                    return function () {
314                        o.stickySidebar.css({'position': 'static'});
315                        o.onScroll(o);
316                    };
317                }(o));
318
319                // Recalculate the sidebar's position every time the sidebar changes its size.
320                if (typeof ResizeSensor !== 'undefined') {
321                    new ResizeSensor(o.stickySidebar[0], function (o) {
322                        return function () {
323                            o.onScroll(o);
324                        };
325                    }(o));
326                }
327
328                // Reset the sidebar to its default state
329                function resetSidebar() {
330                    o.fixedScrollTop = 0;
331                    o.sidebar.css({
332                        'min-height': '1px'
333                    });
334                    o.stickySidebar.css({
335                        'position': 'static',
336                        'width': '',
337                        'transform': 'none'
338                    });
339                }
340
341                // Get the height of a div as if its floated children were cleared. Note that this function fails if the floats are more than one level deep.
342                function getClearedHeight(e) {
343                    var height = e.height();
344
345                    e.children().each(function () {
346                        height = Math.max(height, $(this).height());
347                    });
348
349                    return height;
350                }
351            });
352        }
353
354        function getWidthForObject(object) {
355            var width;
356
357            try {
358                width = object[0].getBoundingClientRect().width;
359            }
360            catch (err) {
361            }
362
363            if (typeof width === "undefined") {
364                width = object.width();
365            }
366
367            return width;
368        }
369
370        return this;
371    }
372})(jQuery);
373
374jQuery(document).ready(function() {
375    jQuery('.sidebar-wrapper').theiaStickySidebar({
376        // Settings
377        additionalMarginTop: 0,
378        additionalMarginBottom: 0,
379    });
380});
381
382
383
384
385// Back to top button
386(function() {
387    $(document).ready(function() {
388        return $(window).scroll(function() {
389            return $(window).scrollTop() > 600 ? $("#back-to-top").addClass("show") : $("#back-to-top").removeClass("show")
390        }), $("#back-to-top").click(function() {
391            return $("html,body").animate({
392                scrollTop: "0"
393            })
394        })
395    })
396}).call(this);
397
398//test.js
399function testTheiaStickySidebars() {
400    var me = {};
401    me.scrollTopStep = 1;
402    me.currentScrollTop = 0;
403    me.values = null;
404
405    window.scrollTo(0, 1),300;
406    window.scrollTo(0, 0);
407
408    $(window).scroll(function(me) {
409        return function(event) {
410            var newValues = [];
411
412            // Get sidebar offsets.
413            $('.theiaStickySidebar').each(function() {
414                newValues.push($(this).offset().top);
415            });
416
417            if (me.values != null) {
418                var ok = true;
419
420                for (var j = 0; j < newValues.length; j++) {
421                    var diff = Math.abs(newValues[j] - me.values[j]);
422                    if (diff > 1) {
423                        ok = false;
424
425                        console.log('Offset difference for sidebar #' + (j + 1) + ' is ' + diff + 'px');
426
427                        // Highlight sidebar.
428                        $($('.theiaStickySidebar')[j]).css('background', 'yellow');
429                    }
430                }
431
432                if (ok == false) {
433                    // Stop test.
434                    $(this).unbind(event);
435
436                    alert('Bummer. Offset difference is bigger than 1px for some sidebars, which will be highlighted in yellow. Check the logs. Aborting.');
437
438                    return;
439                }
440            }
441
442            me.values = newValues;
443
444            // Scroll to bottom. We don't cache ($(document).height() - $(window).height()) since it may change (e.g. after images are loaded).
445            if (me.currentScrollTop < ($(document).height() - $(window).height()) && me.scrollTopStep == 1) {
446                me.currentScrollTop += me.scrollTopStep;
447                window.scrollTo(0, me.currentScrollTop);
448            }
449            // Then back up.
450            else if (me.currentScrollTop > 0) {
451                me.scrollTopStep = -1;
452                me.currentScrollTop += me.scrollTopStep;
453                window.scrollTo(0, me.currentScrollTop);
454            }
455            // Then stop.
456            else {
457                $(this).unbind(event);
458
459                alert("Great success!");
460            }
461
462        };
463    }(me));
464}
465
466
467
468
469
470
471
472
473
474
475
476

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.