Sticky Sidebar 😎

Originally written by Ahmed Bouhuolia

Sticky Sidebar v2 is a pure JavaScript plugin for making smart and high performance sticky sidebar, works with sidebar if it’s taller or shorter than the viewport, supports jQuery/Zepto and compatible with Firefox, Chrome, Safari, and IE9+. Source can be found on Github.

Sticky Sidebar

  1. Why sticky sidebar is awesome?
  2. Examples
  3. Installation
  4. Usage
  5. Configure Your CSS
  6. Options
  7. Events
  8. Public Methods
  9. Scrolling Performance
  10. Browser Compatibility
  11. jQuery/Zepto No Conflict

Why sticky sidebar is awesome?

Examples

Basic

Just call new StickySidebar('ELEMENT') on the element that you want it to be sticky when scrolling up/down inside their parent.

var sidebar = new StickySidebar('#sidebar', {topSpacing: 20});
Basic Demo

Basic Example

Scrollable Sticky Element

Sticky sidebar is smart enough to handle sidebar when it’s taller than the viewport. You don’t have to worry about content, it will scroll the sidebar independently, up and down.

var sidebar = new stickySidebar('#sidebar', {topSpacing: 20});
Scrollable Sticky Element

Scrollable Sticky Element


Installation

You can install sticky sidebar plugin from Bower, NPM or just simply download it from GitHub then put sticky-sidebar.js file into your project folder.

Bower

If you are using Bower as package manager:

bower install sticky-sidebar-v2

NPM

If you are using NPM as package manager:

npm install sticky-sidebar-v2

Usage

Your website’s HTML structure has to be similar to this in order to work:

<div id="main-content" class="main">
    <div id="sidebar" class="sidebar">
        <div class="sidebar__inner">
            <!-- Content goes here -->
        </div>
    </div>
    <div id="content" class="content">
        <!-- Content goes here -->
    </div>
</div>

Note that inner sidebar wrapper .sidebar__inner is optional but highly recommended, if you don’t write it yourself, the script will create one for you under class name inner-wrapper-sticky, but this may cause many problems.

For the above example, you can use the following JavaScript:

<script type="text/javascript" src="./js/sticky-sidebar.js"></script>

<script type="text/javascript">
    var sidebar = new StickySidebar('#sidebar', {
        containerSelector: '#main-content',
        innerWrapperSelector: '.sidebar__inner',
        topSpacing: 20,
        bottomSpacing: 20
    });
</script>

Usage with jQuery/Zepto

You can configure sticky sidebar as a jQuery plugin, just include jquery.sticky-sidebar.js instead of sticky-sidebar.js file than configure it as any jQuery plugin.

$('#sidebar').stickySidebar({
    topSpacing: 60,
    bottomSpacing: 60
});

Make sure to include jquery.sticky-sidebar.js script file after jquery.js.


Configure Your CSS

Next you are going to need some CSS just to improve performance and prevent repainting on scrolling. Sticky sidebar plugin doesn’t add below styles as inline so you need to add them manually in your stylesheet.

.sidebar{
    will-change: min-height;
}

.sidebar__inner{
    transform: translate(0, 0); /* For browsers don't support translate3d. */
    transform: translate3d(0, 0, 0);
    will-change: position, transform;
}

Options

Sticky sidebar plugin comes with options to configure how it works. All options below are optional. Default values are presented below.

var stickySidebar = new StickySidebar('#sidebar', {
    topSpacing: 0,
    bottomSpacing: 0,
    containerSelector: false,
    innerWrapperSelector: '.sidebar__inner',
    scrollContainer: false',
    stickyClass: 'is-affixed',
    minWidth: 0
});

topSpacing

Additional top spacing of the element when it becomes sticky. Default: 0.

var sidebar = new StickySidebar('.sidebar', {topSpacing: 50});

bottomSpacing

Additional bottom spacing of the element when it becomes sticky. Default: 0.

var sidebar = new StickySidebar('.sidebar', {bottomSpacing: 50});

containerSelector

Specify a container sidebar to limit the begin and end points of sticky element. Defaults to the closest parent of the sticky element. It is highly recommended to specify a container selector.

var sidebar = new StickySidebar('.sidebar', {containerSelector: '.container'});

innerWrapperSelector

Inner wrapper selector of sticky sidebar, if this wrapper is not found inside sidebar element, the plugin will create one for you under class name inner-wrapper-sticky. It is highly recommended to write inner wrapper of sidebar yourself than add its selector to this option. Default: .inner-wrapper-sticky.

var sidebar = new StickySidebar('.sidebar', {innerWrapperSelector: '.sidebar__inner'});

scrollContainer

If your content is inside a fixed-height element with a scrollbar, this element must be specified with the scrollContainer option.

var sidebar = new StickySidebar('.sidebar', {scrollContainer: '#main-viewport'});

stickyClass

The name of CSS class which will be added to the sidebar element when it becomes sticky. Default: is-affixed.

var sidebar = new StickySidebar('.sidebar', {stickyClass: 'is-affixed'});

minWidth

The sidebar returns to its normal position when the width of window is below this value. Default: 0.

var sidebar = new StickySidebar('.sidebar', {minWidth: 300});

Events

Sticky sidebar plugin has various events which are triggered when affix state changes.

affix.top.stickySidebar — Fires immediately before the element has been affixed to the top of the viewport.

affixed.top.stickySidebar — Fires immediately after the element has been affixed to the top of the viewport.

affix.bottom.stickySidebar — Fires immediately before the element has been affixed to the bottom of the viewport.

affixed.bottom.stickySidebar — Fires immediately after the element has been affixed to the bottom of the viewport.

affix.container-bottom.stickySidebar — Fires immediately before the element has been affixed to the bottom of the container.

affixed.container-bottom.stickySidebar — Fires immediately after the element has been affixed to the bottom of the container.

affix.unbottom.stickySidebar — Fires immediately before the element is no longer bottomed out.

affixed.unbottom.stickySidebar — Fires immediately after the element is no longer bottomed out.

affix.static.stickySidebar — Fires immediately before the element has returned to its position.

affixed.static.stickySidebar — Fires immediately after the element has returned to its position.

For example if you want to detect when element sticks to top and bottom we might do:

var sidebar = document.getElementById('sidebar');

var stickySidebar = new StickySidebar(sidebar);

sidebar.addEventListener('affix.top.stickySidebar', function () {
    console.log('Sidebar has stuck top of viewport.');
});

sidebar.addEventListener('affix.bottom.stickySidebar', function (event) {
    console.log('Sidebar has stuck bottom of viewport.');
});

Public Methods

updateSticky - Force re-calculation of all cached dimensions of sidebar, container and viewport and update position of sidebar according to the new dimensions. The same function of trigger event update.sticky, read about events above.

var stickySidebar = new StickySidebar('.sidebar');

stickySidebar.updateSticky();

destroy - remove all inline styles, helper classes and event listeners.

var stickySidebar = new StickySidebar('.sidebar');

stickySidebar.destroy();

Scrolling Performance

Sticky sidebar plugin takes scrolling preformance very seriously, It’s built from the ground up to let you have sticky elements without incurring scroll lag or jank.

The biggest cause of scrolling jank is onScroll has a lot of work. But in this plugin we cached all dimensions as well as adding will-change: transform and working with translate(Y, X) instead of top: Y; Left: X; that increased performance significantly, while building Sticky sidebar plugin we prevent repainting and reflow to make it as smooth as possible.


Browser Compatibility

Sticky sidebar works in all modern browsers including Internet Explorer 9 and above, but if you want it to work with IE9, you should include requestAnimationFrame polyfill before sticky sidebar code. For backwards compatibility, a polyfill for ResizeObserver can also be included.

If you have any issue with browser compatibility, don’t hesitate to Submit an issue.


jQuery/Zepto No Conflict

Sometimes sticky sidebar plugin conflicts with other plugins. In this case, namespace collisions can occasionally occur. If this happens, you may call .noConflict on the plugin to revert the value of $.fn.stickySidebar.

var stickySidebar = $.fn.stickySidebar.noConflict(); // Returns $.fn.stickySidebar assigned value.
$.fn.stickySidebar = stickySidebar; // Give $().stickySidebar functionality.

Author

Ahmed Bouhuolia GitHub/Facebook/Twitter. Øystein Blixhavn GitHub/Twitter.

License

MIT License