jsScrollbar

What is jsScrollbar?

jsScrollbar is a lightweight script (4K minified and gzipped) that enables you to override default system scrollbars on your website with beautiful, progressively-enhanced javascript scrollbars. It has no external dependencies, and can be included on any website, regardless of what other library you might be using. jsScrollbars are styled completely with CSS and can be customized to your heart's content.

Download

You can download jsScrollbar here.

Initialization

Initializing a new jsScrollbar is a breeze. First you have to set up some sort of container, which is usually just a div with a set height, width, and maybe a border. Inside the container, insert another div with a class of .jssb-content.

<div id="container">
    <div class="jssb-content">
        <!-- SCROLLING CONTENT GOES HERE -->
    </div>
</div>

Once your content has loaded, just pass your container(s) to a jsScrollbar() call in one of three ways: as a string of comma-separated IDs, an HTMLElement, or an array of HTMLElements.

// Comma separated IDs, pound sign optional
jsScrollbar('container, #container2')

// Element reference
var el = document.getElementById('container');
jsScrollbar(el);

// Array of elements through a jQuery call
jsScrollbar( $('.container') );

jsScrollbar() also takes a second optional argument for preferences, but we'll get to that in a later section.

You can also save a reference of the returned jsScrollbar and call its methods.

// Save a reference
var sb = jsScrollbar('#container');

// Call methods
sb.scrollTo(0, 120);
sb.recalc();

Styling

There are a wide variety of CSS hooks available for styling your jsScrollbar. For a detailed look at what exactly you need to do to setup your CSS, take a look at the CSS files included. They are heavily commented and can be used as a template for your own styles. A run down of useful hooks are detailed below.

.jssb-applied
This is probably the most important hook. It is added to the container element passed to jsScrollbar() upon a successful initialization.
.jssb-scrollx
Added to the container element when content overflows on the x-axis.
.jssb-scrolly
Added to the container element when content overflows on the y-axis.
.jssb-focus
Added to the container element when the users clicks on the container to bring it focus, enabling keyboard navigation. It is removed when the user clicks anywhere else on the document.
.jssb-content
A child of the container element. All scrolling content resides here.
.jssb-x, .jssb-y
The scrollbar elements. Children of the container element.
.jssb-x-left, .jssb-x-right, .jssb-y-up, .jssb-y-down
The scrolling directional buttons. Children of their respective scrollbar element.
.jssb-x-track, .jssb-y-track
The scrollbar track elements. Children of their respective scrollbar element.
.jssb-x-thumb, .jssb-y-thumb
The draggable scrollbar thumb elements. Children of their respective scrollbar track element.
jssb-x-*-click, jssb-y-*-click
Each of the scrollbar items has a class appended to it when the user clicks on it. For example, when the user clicks on a .jssb-y-thumb element, it will receive an additional .jssb-y-thumb-click class.

In the default template, there are also a few nonessential classes for purely visual purposes that include:

.jssb-x-track-mid, .jssb-x-track-end, .jssb-y-track-mid, .jssb-y-track-end
These are children of their respective track element. These are in place to enable expanding background images.
.jssb-x-thumb-mid, .jssb-x-thumb-end, .jssb-y-thumb-mid, .jssb-y-thumb-end
These are children of their respective thumb element. Again, these are in place to enable expanding background images.

Preferences

Preferences are passed to a new jsScrollbar as key-value pairs. You can also override the default settings so you don't have to change the preferences for each instance.

// Preferences are passed as the second argument
jsScrollbar('#container', {
    horizontalScrolling: false,
    scrollSpeed: 20
});

// Or you can override the defaults
jsScrollbar.defaults.horizontalScrolling = false;
jsScrollbar.defaults.scrollSpeed = 20;
jsScrollbar('#container');

A rundown of available preferences is listed below.

scrollSpeed (Integer)
The time in milliseconds between each scroll frame. This applies to when the user presses the directional buttons. The default is25.
scrollDistance (Integer)
The number of pixels between each scroll frame when using the directional buttons. The default is 10.
wheelDistance (Integer)
The number of pixels the content scrolls when using the mouse wheel. The default is 40.
tweenDuration (Integer)
The number of milliseconds the tweening animation lasts. The default is 300.
tweenFn (Function)

An easing function. This function is passed a value between 0 and 1, representing the current position within the animation (0 = start, 0.5 = halfway, 1 = end). Modifying this value results in different kinds of easing. Most easing equations out there are based off of Robert Penner's work and are freely available, but you can also create your own with a little trial and error and a graphing calculator. Once you plot your function, anything between (0,0) and (1,1) is what will serve as your animation. Setting this to null will result in a linear animation. The default easing function is:

function (pos) {
    return -Math.pow((pos-1), 4) + 1;
}
disableTweening (Boolean)
Disables animations when clicking on the track. It will instead cause content to scroll by pages like system scrollbars. The default is false.
horizontalScrolling (Boolean)
Enables horizontal scrolling and scrollbar. The default is true.
verticalScrolling (Boolean)
Enables vertical scrolling and scrollbar. The default is true.
fixedThumb (Boolean)
Enable this if you don't want the thumb to be resized relative to the amount of overflow content. The default is false.
template (String)

HTML to use when generating the scrollbar. The default is shown below.

<div class="jssb">
    <div class="jssb-up"></div>
    <div class="jssb-track">
        <div class="jssb-track-mid"></div>
        <div class="jssb-track-end"></div>
        <div class="jssb-thumb">
            <div class="jssb-thumb-mid"></div>
            <div class="jssb-thumb-end"></div>
        </div>
    </div>
    <div class="jssb-down"></div>
</div>

The template tries to be directionally unbiased (except for 'up' and 'down') so the axis is left out of the class name. Alternatively, you can also change this on a case by case basis by including it in the HTML of the container, but the elements must have their full class name with the axis. Decorative elements must keep the prefix of the element they decorate so the event handler know what they belong to.

Compatibility

The javascript powering jsScrollbar is fairly simplistic and will work in any remotely modern browser (including IE6). It's the CSS you use that might cause compatibility issues.

To easily prevent jsScrollbars from initializing, you can set the .jssb-content's overflow to auto.

/* Use default system scrollbars */
.jssb-content { 
    overflow: auto; 
    ...
}

/* Enable jsScrollbars in capable browsers */
.jssb-applied > .jssb-content { 
    overflow: hidden;
    ...
}

All of the included demos use this technique to serve IE6 the default system scrollbars.

The demos included make extensive use of dimensions-by-positioning wherein declaring:

.jssb-y {
    position: absolute;
    top: 5px;
    bottom: 5px;
}

would cause the vertical scrollbar to expand to the height of the container minus 5 pixels above and below it. This allows for scrollbars to easily expand to any size without having explicitly set dimensions for each scrollbar. IE6 does not support this. You could enable complete cross-browser compatibility by using hard coded dimensions, but you lose all flexibility.

Opera also displays some minor quirks when using this technique on a liquid container. It fails to resize the scrollbar when the size of the container changes. This, however, can be easily fixed by calling a javascript function that forces Opera to redraw the container and its children.

function forceOperaRedraw (sb) {
    if (!window.opera) return;
    if (!sb.length) sb = [sb];
    var i = sb.length, o, r;
    while (i--) {
        o = sb[i].parent;
        if (o.offsetWidth > 0 && o.offsetHeight > 0) {
            o.style.display = 'none';
            r = o.offsetHeight;
            o.style.display = '';
        }
    }
}
// Store instance of jsScrollbar
var sb = jsScrollbar('#container');

//Force a redraw
forceOperaRedraw(sb);

This particular function accepts a single jsScrollbar instance or an array. It will do nothing in other browsers. See the included demos for examples of its usage.

IE7 has issues with liquid containers in general. For some reason, accessing an element's offset dimensions inside the container occasionally causes IE to break rendering on the container. Sometimes this can be mitigated by wrapping the recalc() call in a setTimeout() with 0 delay, but not always.

function recalcScrollbar () {
    setTimeout( function () { scrollbar.recalc(); }, 0 );
}

Of course, you can always just disable it entirely in IE7 like we do in IE6.

<!--[if lt IE 8]>
<style type="text/css">
    /* Disable jsScrollbar in IE7 */
    .jssb-applied > .jssb-content { overflow: auto; }
</style>
<![endif]-->

API

jsScrollbar

jsScrollbar(container, [preferences])
View the initialization section above for instruction on its usage. This returns a single jsScrollbar instance or an array if called on multiple elements.
jsScrollbar.scrollbars
A collection of all instanciated jsScrollbars.

Instances

parent
A reference to the container element passed to jsScrollbar(). If you modify this element beyond simple styles, you will likely need to call recalc() to avoid buggy behavior.
content
A reference to the .jssb-content element. As with parent, if you modify this element beyond simple styles, you will likely need to call recalc() afterwards.
scrollTo(coordA, [coordB])
The parameters correspond to an XY coordinate pair, but the second value can be omitted if an axis is disabled by setting horizontalScrolling or verticalScrolling to false. You can pass a null value to ignore an axis.
scrollBy(distA, [distB])
This works similarly to scrollTo, but with relative distances instead of coordinates.
tweenTo(coordA, [coordB])
Same asscrollTo, but with an animation.
tweenBy(distA, [distB])
Same asscrollBy, but with an animation.
disable()
This removes all CSS hooks and javascript events, restoring the system scrollbars, but does not remove the scrollbar elements from the parent.
enable()
This reapplies the CSS hooks and events, restoring the jsScrollbar.
prefs(preference)
This returns the specified preference value. preference is a string.
prefs(preference, value)
This sets the specified preference value.
prefs(preference_values)
This sets the specified preference values, defined as key-value pairs like when initializing the jsScrollbar.
recalc()
This basically reinitializes the jsScrollbar. If something goes wrong or is acting strange, calling this will probably fix it. For example, if you modify the container element's innerHTML, the script would lose references to the scrollbar components. Calling recalc() would find the lost elements again. Also, if you modify the content of the .jssb-content or resize the container, recalc() would recalculate the thumb size.

Demos

Custom Tweening
A demo showing custom scrollbar animations. Notice how the scrollbar elements are floated. jsScrollbar requires the container elements to have some sort of positioning. If none is specified, it will automatically add relative positioning to it.
Simple Tabs
This demo shows a simple implementation of tabbed jsScrollbarss. It maintains history states, and is viewable without javascript.
Fixed Thumbs
This demo shows scrollbars with fixed thumbs. It also shows how you can add other decorative elements to the container. Using the CSS hooks provided, it fades the content out on the sides when a jsScrollbar is applied to it.
Flexible Containers
A demo showing what to do when you want scrollbars that change with the size of the window.
Fullscreen Container
An extreme version of a liquid container. This fills all available space, replacing the normal document scrollbars. This is not recommended, but nevertheless, it's still possible.