JAVASCRIPT   90

theia-sticky-sidebar.js

Guest on 6th August 2021 06:45:15 PM

  1. /*!
  2.  * Theia Sticky Sidebar v1.3.0
  3.  * https://github.com/WeCodePixels/theia-sticky-sidebar
  4.  *
  5.  * Glues your website's sidebars, making them permanently visible while scrolling.
  6.  *
  7.  * Copyright WeCodePixels and other contributors
  8.  * Released under the MIT license
  9.  */
  10.  
  11. (function ($) {
  12.         $.fn.theiaStickySidebar = function (options) {
  13.                 var defaults = {
  14.                         'containerSelector': '',
  15.                         'additionalMarginTop': 0,
  16.                         'additionalMarginBottom': 0,
  17.                         'updateSidebarHeight': false,
  18.                         'minWidth': 0,
  19.                         'sidebarBehavior': 'modern'
  20.                 };
  21.                 options = $.extend(defaults, options);
  22.  
  23.                 // Validate options
  24.                 options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0;
  25.                 options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0;
  26.  
  27.                 tryInitOrHookIntoEvents(options, this);
  28.  
  29.                 // Try doing init, otherwise hook into window.resize and document.scroll and try again then.
  30.                 function tryInitOrHookIntoEvents(options, $that) {
  31.                         var success = tryInit(options, $that);
  32.  
  33.                         if (!success) {
  34.                                 console.log('TST: Body width smaller than options.minWidth. Init is delayed.');
  35.  
  36.                                 $(document).scroll(function (options, $that) {
  37.                                         return function (evt) {
  38.                                                 var success = tryInit(options, $that);
  39.  
  40.                                                 if (success) {
  41.                                                         $(this).unbind(evt);
  42.                                                 }
  43.                                         };
  44.                                 }(options, $that));
  45.                                 $(window).resize(function (options, $that) {
  46.                                         return function (evt) {
  47.                                                 var success = tryInit(options, $that);
  48.  
  49.                                                 if (success) {
  50.                                                         $(this).unbind(evt);
  51.                                                 }
  52.                                         };
  53.                                 }(options, $that))
  54.                         }
  55.                 }
  56.  
  57.                 // Try doing init if proper conditions are met.
  58.                 function tryInit(options, $that) {
  59.                         if (options.initialized === true) {
  60.                                 return true;
  61.                         }
  62.  
  63.                         if ($('body').width() < options.minWidth) {
  64.                                 return false;
  65.                         }
  66.  
  67.                         init(options, $that);
  68.  
  69.                         return true;
  70.                 }
  71.  
  72.                 // Init the sticky sidebar(s).
  73.                 function init(options, $that) {
  74.                         options.initialized = true;
  75.  
  76.                         // Add CSS
  77.                         $('head').append($('<style>.theiaStickySidebar:after {content: ""; display: table; clear: both;}</style>'));
  78.  
  79.                         $that.each(function () {
  80.                                 var o = {};
  81.  
  82.                                 o.sidebar = $(this);
  83.  
  84.                                 // Save options
  85.                                 o.options = options || {};
  86.  
  87.                                 // Get container
  88.                                 o.container = $(o.options.containerSelector);
  89.                                 if (o.container.size() == 0) {
  90.                                         o.container = o.sidebar.parent();
  91.                                 }
  92.  
  93.                                 // Create sticky sidebar
  94.                                 o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574
  95.                                 o.sidebar.css({
  96.                                         'position': 'relative',
  97.                                         'overflow': 'visible',
  98.                                         // 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.
  99.                                         '-webkit-box-sizing': 'border-box',
  100.                                         '-moz-box-sizing': 'border-box',
  101.                                         'box-sizing': 'border-box'
  102.                                 });
  103.  
  104.                                 // Get the sticky sidebar element. If none has been found, then create one.
  105.                                 o.stickySidebar = o.sidebar.find('.theiaStickySidebar');
  106.                                 if (o.stickySidebar.length == 0) {
  107.                                         o.sidebar.find('script').remove(); // Remove <script> tags, otherwise they will be run again on the next line.
  108.                                         o.stickySidebar = $('<div>').addClass('theiaStickySidebar').append(o.sidebar.children());
  109.                                         o.sidebar.append(o.stickySidebar);
  110.                                 }
  111.  
  112.                                 // Get existing top and bottom margins and paddings
  113.                                 o.marginTop = parseInt(o.sidebar.css('margin-top'));
  114.                                 o.marginBottom = parseInt(o.sidebar.css('margin-bottom'));
  115.                                 o.paddingTop = parseInt(o.sidebar.css('padding-top'));
  116.                                 o.paddingBottom = parseInt(o.sidebar.css('padding-bottom'));
  117.  
  118.                                 // Add a temporary padding rule to check for collapsable margins.
  119.                                 var collapsedTopHeight = o.stickySidebar.offset().top;
  120.                                 var collapsedBottomHeight = o.stickySidebar.outerHeight();
  121.                                 o.stickySidebar.css('padding-top', 1);
  122.                                 o.stickySidebar.css('padding-bottom', 1);
  123.                                 collapsedTopHeight -= o.stickySidebar.offset().top;
  124.                                 collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight;
  125.                                 if (collapsedTopHeight == 0) {
  126.                                         o.stickySidebar.css('padding-top', 0);
  127.                                         o.stickySidebarPaddingTop = 0;
  128.                                 }
  129.                                 else {
  130.                                         o.stickySidebarPaddingTop = 1;
  131.                                 }
  132.  
  133.                                 if (collapsedBottomHeight == 0) {
  134.                                         o.stickySidebar.css('padding-bottom', 0);
  135.                                         o.stickySidebarPaddingBottom = 0;
  136.                                 }
  137.                                 else {
  138.                                         o.stickySidebarPaddingBottom = 1;
  139.                                 }
  140.  
  141.                                 // We use this to know whether the user is scrolling up or down.
  142.                                 o.previousScrollTop = null;
  143.  
  144.                                 // Scroll top (value) when the sidebar has fixed position.
  145.                                 o.fixedScrollTop = 0;
  146.  
  147.                                 // Set sidebar to default values.
  148.                                 resetSidebar();
  149.  
  150.                                 o.onScroll = function (o) {
  151.                                         // Stop if the sidebar isn't visible.
  152.                                         if (!o.stickySidebar.is(":visible")) {
  153.                                                 return;
  154.                                         }
  155.  
  156.                                         // Stop if the window is too small.
  157.                                         if ($('body').width() < o.options.minWidth) {
  158.                                                 resetSidebar();
  159.                                                 return;
  160.                                         }
  161.  
  162.                                         // 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)
  163.                                         if (o.sidebar.outerWidth(true) + 50 > o.container.width()) {
  164.                                                 resetSidebar();
  165.                                                 return;
  166.                                         }
  167.  
  168.                                         var scrollTop = $(document).scrollTop();
  169.                                         var position = 'static';
  170.  
  171.                                         // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position.
  172.                                         if (scrollTop >= o.container.offset().top + (o.paddingTop + o.marginTop - o.options.additionalMarginTop)) {
  173.                                                 // The top and bottom offsets, used in various calculations.
  174.                                                 var offsetTop = o.paddingTop + o.marginTop + options.additionalMarginTop;
  175.                                                 var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom;
  176.  
  177.                                                 // All top and bottom positions are relative to the window, not to the parent elemnts.
  178.                                                 var containerTop = o.container.offset().top;
  179.                                                 var containerBottom = o.container.offset().top + getClearedHeight(o.container);
  180.  
  181.                                                 // The top and bottom offsets relative to the window screen top (zero) and bottom (window height).
  182.                                                 var windowOffsetTop = 0 + options.additionalMarginTop;
  183.                                                 var windowOffsetBottom;
  184.  
  185.                                                 var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height();
  186.                                                 if (sidebarSmallerThanWindow) {
  187.                                                         windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight();
  188.                                                 }
  189.                                                 else {
  190.                                                         windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom;
  191.                                                 }
  192.  
  193.                                                 var staticLimitTop = containerTop - scrollTop + o.paddingTop + o.marginTop;
  194.                                                 var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom;
  195.  
  196.                                                 var top = o.stickySidebar.offset().top - scrollTop;
  197.                                                 var scrollTopDiff = o.previousScrollTop - scrollTop;
  198.  
  199.                                                 // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate.
  200.                                                 if (o.stickySidebar.css('position') == 'fixed') {
  201.                                                         if (o.options.sidebarBehavior == 'modern') {
  202.                                                                 top += scrollTopDiff;
  203.                                                         }
  204.                                                 }
  205.  
  206.                                                 if (o.options.sidebarBehavior == 'legacy') {
  207.                                                         top = windowOffsetBottom - o.stickySidebar.outerHeight();
  208.                                                         top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight());
  209.                                                 }
  210.  
  211.                                                 if (scrollTopDiff > 0) { // If the user is scrolling up.
  212.                                                         top = Math.min(top, windowOffsetTop);
  213.                                                 }
  214.                                                 else { // If the user is scrolling down.
  215.                                                         top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight());
  216.                                                 }
  217.  
  218.                                                 top = Math.max(top, staticLimitTop);
  219.  
  220.                                                 top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight());
  221.  
  222.                                                 // If the sidebar is the same height as the container, we won't use fixed positioning.
  223.                                                 var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight();
  224.  
  225.                                                 if (!sidebarSameHeightAsContainer && top == windowOffsetTop) {
  226.                                                         position = 'fixed';
  227.                                                 }
  228.                                                 else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) {
  229.                                                         position = 'fixed';
  230.                                                 }
  231.                                                 else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) {
  232.                                                         // Stuck to the top of the page. No special behavior.
  233.                                                         position = 'static';
  234.                                                 }
  235.                                                 else {
  236.                                                         // Stuck to the bottom of the page.
  237.                                                         position = 'absolute';
  238.                                                 }
  239.                                         }
  240.  
  241.                                         /*
  242.                                          * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change.
  243.                                          * It's way slower to first check if the values have changed.
  244.                                          */
  245.                                         if (position == 'fixed') {
  246.                                                 o.stickySidebar.css({
  247.                                                         'position': 'fixed',
  248.                                                         'width': o.sidebar.width(),
  249.                                                         'top': top,
  250.                                                         'left': o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left'))
  251.                                                 });
  252.                                         }
  253.                                         else if (position == 'absolute') {
  254.                                                 var css = {};
  255.  
  256.                                                 if (o.stickySidebar.css('position') != 'absolute') {
  257.                                                         css.position = 'absolute';
  258.                                                         css.top = scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom;
  259.                                                 }
  260.  
  261.                                                 css.width = o.sidebar.width();
  262.                                                 css.left = '';
  263.  
  264.                                                 o.stickySidebar.css(css);
  265.                                         }
  266.                                         else if (position == 'static') {
  267.                                                 resetSidebar();
  268.                                         }
  269.  
  270.                                         if (position != 'static') {
  271.                                                 if (o.options.updateSidebarHeight == true) {
  272.                                                         o.sidebar.css({
  273.                                                                 'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom
  274.                                                         });
  275.                                                 }
  276.                                         }
  277.  
  278.                                         o.previousScrollTop = scrollTop;
  279.                                 };
  280.  
  281.                                 // Initialize the sidebar's position.
  282.                                 o.onScroll(o);
  283.  
  284.                                 // Recalculate the sidebar's position on every scroll and resize.
  285.                                 $(document).scroll(function (o) {
  286.                                         return function () {
  287.                                                 o.onScroll(o);
  288.                                         };
  289.                                 }(o));
  290.                                 $(window).resize(function (o) {
  291.                                         return function () {
  292.                                                 o.stickySidebar.css({'position': 'static'});
  293.                                                 o.onScroll(o);
  294.                                         };
  295.                                 }(o));
  296.  
  297.                                 // Reset the sidebar to its default state
  298.                                 function resetSidebar() {
  299.                                         o.fixedScrollTop = 0;
  300.                                         o.sidebar.css({
  301.                                                 'min-height': '1px'
  302.                                         });
  303.                                         o.stickySidebar.css({
  304.                                                 'position': 'static',
  305.                                                 'width': ''
  306.                                         });
  307.                                 }
  308.  
  309.                                 // 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.
  310.                                 function getClearedHeight(e) {
  311.                                         var height = e.height();
  312.  
  313.                                         e.children().each(function () {
  314.                                                 height = Math.max(height, $(this).height());
  315.                                         });
  316.  
  317.                                         return height;
  318.                                 }
  319.                         });
  320.                 }
  321.         }
  322. })(jQuery);

Raw Paste


Login or Register to edit or fork this paste. It's free.