/*! * Theia Sticky Sidebar v1.7.0 * https://github.com/WeCodePixels/theia-sticky-sidebar * * Glues your website's sidebars, making them permanently visible while scrolling. * * Copyright 2013-2016 WeCodePixels and other contributors * Released under the MIT license */ (function ($) { $.fn.theiaStickySidebar = function (options) { var defaults = { 'containerSelector': '', 'additionalMarginTop': 0, 'additionalMarginBottom': 0, 'updateSidebarHeight': true, 'minWidth': 0, 'disableOnResponsiveLayouts': true, 'sidebarBehavior': 'modern', 'defaultPosition': 'relative', 'namespace': 'TSS' }; options = $.extend(defaults, options); // Validate options options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0; options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0; tryInitOrHookIntoEvents(options, this); // Try doing init, otherwise hook into window.resize and document.scroll and try again then. function tryInitOrHookIntoEvents(options, $that) { var success = tryInit(options, $that); if (!success) { console.log('TSS: Body width smaller than options.minWidth. Init is delayed.'); $(document).on('scroll.' + options.namespace, function (options, $that) { return function (evt) { var success = tryInit(options, $that); if (success) { $(this).unbind(evt); } }; }(options, $that)); $(window).on('resize.' + options.namespace, function (options, $that) { return function (evt) { var success = tryInit(options, $that); if (success) { $(this).unbind(evt); } }; }(options, $that)) } } // Try doing init if proper conditions are met. function tryInit(options, $that) { if (options.initialized === true) { return true; } if ($('body').width() < options.minWidth) { return false; } init(options, $that); return true; } // Init the sticky sidebar(s). function init(options, $that) { options.initialized = true; // Add CSS var existingStylesheet = $('#theia-sticky-sidebar-stylesheet-' + options.namespace); if (existingStylesheet.length === 0) { $('head').append($('<style id="theia-sticky-sidebar-stylesheet-' + options.namespace + '">.theiaStickySidebar:after {content: ""; display: table; clear: both;}</style>')); } $that.each(function () { var o = {}; o.sidebar = $(this); // Save options o.options = options || {}; // Get container o.container = $(o.options.containerSelector); if (o.container.length == 0) { o.container = o.sidebar.parent(); } // Create sticky sidebar o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574 o.sidebar.css({ 'position': o.options.defaultPosition, 'overflow': 'visible', // The "box-sizing" must be set to "content-box" because we set a fixed height to this element when the sticky sidebar has a fixed position. '-webkit-box-sizing': 'border-box', '-moz-box-sizing': 'border-box', 'box-sizing': 'border-box' }); // Get the sticky sidebar element. If none has been found, then create one. o.stickySidebar = o.sidebar.find('.theiaStickySidebar'); if (o.stickySidebar.length == 0) { // Remove <script> tags, otherwise they will be run again when added to the stickySidebar. var javaScriptMIMETypes = /(?:text|application)\/(?:x-)?(?:javascript|ecmascript)/i; o.sidebar.find('script').filter(function (index, script) { return script.type.length === 0 || script.type.match(javaScriptMIMETypes); }).remove(); o.stickySidebar = $('<div>').addClass('theiaStickySidebar').append(o.sidebar.children()); o.sidebar.append(o.stickySidebar); } // Get existing top and bottom margins and paddings o.marginBottom = parseInt(o.sidebar.css('margin-bottom')); o.paddingTop = parseInt(o.sidebar.css('padding-top')); o.paddingBottom = parseInt(o.sidebar.css('padding-bottom')); // Add a temporary padding rule to check for collapsable margins. var collapsedTopHeight = o.stickySidebar.offset().top; var collapsedBottomHeight = o.stickySidebar.outerHeight(); o.stickySidebar.css('padding-top', 1); o.stickySidebar.css('padding-bottom', 1); collapsedTopHeight -= o.stickySidebar.offset().top; collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight; if (collapsedTopHeight == 0) { o.stickySidebar.css('padding-top', 0); o.stickySidebarPaddingTop = 0; } else { o.stickySidebarPaddingTop = 1; } if (collapsedBottomHeight == 0) { o.stickySidebar.css('padding-bottom', 0); o.stickySidebarPaddingBottom = 0; } else { o.stickySidebarPaddingBottom = 1; } // We use this to know whether the user is scrolling up or down. o.previousScrollTop = null; // Scroll top (value) when the sidebar has fixed position. o.fixedScrollTop = 0; // Set sidebar to default values. resetSidebar(); o.onScroll = function (o) { // Stop if the sidebar isn't visible. if (!o.stickySidebar.is(":visible")) { return; } // Stop if the window is too small. if ($('body').width() < o.options.minWidth) { resetSidebar(); return; } // 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) if (o.options.disableOnResponsiveLayouts) { var sidebarWidth = o.sidebar.outerWidth(o.sidebar.css('float') == 'none'); if (sidebarWidth + 50 > o.container.width()) { resetSidebar(); return; } } var scrollTop = $(document).scrollTop(); var position = 'static'; // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position. if (scrollTop >= o.sidebar.offset().top + (o.paddingTop - o.options.additionalMarginTop)) { // The top and bottom offsets, used in various calculations. var offsetTop = o.paddingTop + options.additionalMarginTop; var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom; // All top and bottom positions are relative to the window, not to the parent elemnts. var containerTop = o.sidebar.offset().top; var containerBottom = o.sidebar.offset().top + getClearedHeight(o.container); // The top and bottom offsets relative to the window screen top (zero) and bottom (window height). var windowOffsetTop = 0 + options.additionalMarginTop; var windowOffsetBottom; var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height(); if (sidebarSmallerThanWindow) { windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight(); } else { windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom; } var staticLimitTop = containerTop - scrollTop + o.paddingTop; var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom; var top = o.stickySidebar.offset().top - scrollTop; var scrollTopDiff = o.previousScrollTop - scrollTop; // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate. if (o.stickySidebar.css('position') == 'fixed') { if (o.options.sidebarBehavior == 'modern') { top += scrollTopDiff; } } if (o.options.sidebarBehavior == 'stick-to-top') { top = options.additionalMarginTop; } if (o.options.sidebarBehavior == 'stick-to-bottom') { top = windowOffsetBottom - o.stickySidebar.outerHeight(); } if (scrollTopDiff > 0) { // If the user is scrolling up. top = Math.min(top, windowOffsetTop); } else { // If the user is scrolling down. top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight()); } top = Math.max(top, staticLimitTop); top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight()); // If the sidebar is the same height as the container, we won't use fixed positioning. var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight(); if (!sidebarSameHeightAsContainer && top == windowOffsetTop) { position = 'fixed'; } else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) { position = 'fixed'; } else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) { // Stuck to the top of the page. No special behavior. position = 'static'; } else { // Stuck to the bottom of the page. position = 'absolute'; } } /* * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change. * It's way slower to first check if the values have changed. */ if (position == 'fixed') { var scrollLeft = $(document).scrollLeft(); o.stickySidebar.css({ 'position': 'fixed', 'width': getWidthForObject(o.stickySidebar) + 'px', 'transform': 'translateY(' + top + 'px)', 'left': (o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left')) - scrollLeft) + 'px', 'top': '0px' }); } else if (position == 'absolute') { var css = {}; if (o.stickySidebar.css('position') != 'absolute') { css.position = 'absolute'; css.transform = 'translateY(' + (scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom) + 'px)'; css.top = '0px'; } css.width = getWidthForObject(o.stickySidebar) + 'px'; css.left = ''; o.stickySidebar.css(css); } else if (position == 'static') { resetSidebar(); } if (position != 'static') { if (o.options.updateSidebarHeight == true) { o.sidebar.css({ 'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom }); } } o.previousScrollTop = scrollTop; }; // Initialize the sidebar's position. o.onScroll(o); // Recalculate the sidebar's position on every scroll and resize. $(document).on('scroll.' + o.options.namespace, function (o) { return function () { o.onScroll(o); }; }(o)); $(window).on('resize.' + o.options.namespace, function (o) { return function () { o.stickySidebar.css({'position': 'static'}); o.onScroll(o); }; }(o)); // Recalculate the sidebar's position every time the sidebar changes its size. if (typeof ResizeSensor !== 'undefined') { new ResizeSensor(o.stickySidebar[0], function (o) { return function () { o.onScroll(o); }; }(o)); } // Reset the sidebar to its default state function resetSidebar() { o.fixedScrollTop = 0; o.sidebar.css({ 'min-height': '1px' }); o.stickySidebar.css({ 'position': 'static', 'width': '', 'transform': 'none' }); } // 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. function getClearedHeight(e) { var height = e.height(); e.children().each(function () { height = Math.max(height, $(this).height()); }); return height; } }); } function getWidthForObject(object) { var width; try { width = object[0].getBoundingClientRect().width; } catch (err) { } if (typeof width === "undefined") { width = object.width(); } return width; } return this; } })(jQuery); //# sourceMappingURL=maps/theia-sticky-sidebar.js.map