smooth-scrollbar-loco

Customize scrollbar in modern browsers with smooth scrolling experience.

Usage no npm install needed!

<script type="module">
  import smoothScrollbarLoco from 'https://cdn.skypack.dev/smooth-scrollbar-loco';
</script>

README

Smooth Scrollbar

npm npm npm Travis

Smooth scrolling in modern browsers.

Table of Contents

Install

Via npm:

npm install smooth-scrollbar --save

Via bower:

bower install smooth-scrollbar --save

Browser Compatibility

Browser Version
IE 10+
Chrome 22+
Firefox 16+
Safari 8+
Android Browser 4+
Chrome for Android 32+
iOS Safari 7+

Demo

http://idiotwu.github.io/smooth-scrollbar/

Why is native scrolling slow?

As is explained in this article, browser repaint every frame in scrolling. Less painting is better.

To avoid repainting, I use translate3d in scroll content to create composite layers and force hardware accelerating.

Usage

Smooth scrollbar is defined as an UMD module which named Scrollbar, you can use any loader to load it:

import Scrollbar from 'smooth-scrollbar';

Or get it from window object:

var Scrollbar = window.Scrollbar;

Don't forget to include the stylesheet in your page:

<link rel="stylesheet" href="dist/smooth-scrollbar.css">

There are three ways to tell the plugin which element should be a smooth scrollbar:

  1. As an element:

    <scrollbar>...</scrollbar>
    
  2. As an attribute:

    <section scrollbar>...</section>
    
  3. As a data attribute

    <section data-scrollbar>...</section>
    

Then init all scrollbars:

Scrollbar.initAll(options);

Or you can call Scrollbar.init(elem, options) to manually init the scrollbar:

Scrollbar.init(document.getElementById('my-scrollbar'), options);

Available Options for Scrollbar

parameter type default description
speed Number 1 Scrolling speed scale.
damping Number 0.1 Delta reduction damping, a float value between (0, 1), the lower the value is, the more smooth the scrolling will be.
thumbMinSize Number 20 Minimal size for scrollbar thumb.
syncCallbacks Boolean false Execute listeners in synchronous or asynchronous.
renderByPixels Boolean true Render scrolling by integer pixels, set to true to improve performance.
alwaysShowTracks Boolean false Keep scrollbar tracks visible whether it's scrolling or not.
continuousScrolling Boolean|String 'auto' Whether allow upper scrollable content to continue scrolling when current scrollbar reaches edge. When set to 'auto', it will be enabled on nested scrollbars, and disabled on first-class scrollbars.
overscrollEffect Boolean|String false Experimental overscroll effect, 'bounce' for iOS style effect and 'glow' for Android style effect. Be careful when you enable this feature!
overscrollEffectColor String '#87ceeb' Canvas paint color with 'glow' effect.
overscrollDamping Number 0.2 The same as damping, but for overscrolling.

Confusing with the option field? Try edit tool here!

DOM Structure

Following is the DOM structure that Scrollbar yields:

<scrollbar>
    <article class="scroll-content">
        your contents here...
    </article>
    <aside class="scrollbar-track scrollbar-track-x">
        <div class="scrollbar-thumb scrollbar-thumb-x"></div>
    </aside>
    <aside class="scrollbar-track scrollbar-track-y">
        <div class="scrollbar-thumb scrollbar-thumb-y"></div>
    </aside>
</scrollbar>

APIs

In this documentation, we are using Scrollbar (in capitalized) to represent the constructor and scrollbar (in lowercase) for the instance object.

Static Methods

Scrollbar.init()

Scrollbar.init(elem, options?): SmoothScrollbar
Param Type Description
elem Element The DOM element that you want to initialize scrollbar to.
options object, optional Initial options, see Available Options for Scrollbar section above.

Initializes a scrollbar on the given element and returns scrollbar instance:

var scrollbar = Scrollbar.init(document.body, { speed: 0.75 });

Scrollbar.initAll()

Scrollbar.init(options): [SmoothScrollbar]
Param Type Description
options object, optional Initial options, see Available Options for Scrollbar section above.

Automatically init scrollbar on all elements refer to the selector scrollbar, [scrollbar], [data-scrollbar], returns an array of scrollbars collection:

<scrollbar> ... </scrollbar>

<article scrollbar> ... </article>

<article data-scrollbar> ... </article>
Scrollbar.initAll(); // [ SmoothScrollbar, SmoothScrollbar, SmoothScrollbar ]

Scrollbar.has()

Scrollbar.has(elem): boolean
Param Type Description
elem Element The DOM element that you want to check.

Check if scrollbar exists on given element:

Scrollbar.init(document.body);

Scrollbar.has(document.body); // true

Scrollbar.get()

Scrollbar.get(elem): SmoothScrollbar
Param Type Description
elem Element The DOM element that you want to check.

Get scrollbar on the given element, if no scrollbar instance exsits, return undefined:

var scrollbar = Scrollbar.init(document.body);

Scrollbar.get(document.body) === scrollbar; // true

Scrollbar.get(document.documentElement); // undefined

Scrollbar.getAll()

Scrollbar.getAll(): [SmoothScrollbar]

Return an array that contains all scrollbar instances:

Scrollbar.getAll(); // [ SmoothScrollbar, SmoothScrollbar, ... ]

Scrollbar.destroy()

Scrollbar.destroy(elem): void
Param Type Description
elem Element The DOM element that scrollbar is initialized to.

Remove scrollbar on the given element.

Scrollbar.destroyAll()

Scrollbar.destroyAll(): void

Remove all scrollbar instances from current page.

Instance Properties and Methods

A scrollbar instance refer to what you get from Scrollbar.init() method:

var scrollbar = Scrollbar.init(elem)

scrollbar.targets

  • Type: object

An object that contains main parts of current scrollbar:

{
    container,
    content,
    xAxis: { track, thumb },
    yAxis: { track, thumb }
}

scrollbar.offset

  • Type: object

Current scrolling offset:

console.log(scrollbar.offset); // { x: 123, y: 456 }

scrollbar.limit

  • Type: object

Max-allowed scrolling distance:

console.log(scrollbar.limit); // { x: 1000, y: 1000 }

scrollbar.scrollTop

  • Type: number

Alias for scrollbar.offset.y:

console.log(scrollbar.scrollTop); // 123

console.log(scrollbar.scrollTop === scrollbar.offset.y); // true

scrollbar.scrollLeft

  • Type: number

Alias for scrollbar.offset.x:

console.log(scrollbar.scrollLeft); // 456

console.log(scrollbar.scrollLeft === scrollbar.offset.x); // true

scrollbar.wheelReversed

  • Type: boolean

Whether event wheel is reversed:

console.log(scrollbar.wheelReversed); // true or false

scrollbar.reverseWheel()

scrollbar.reverseWheel(reverse): void
Param Type Description
reverse boolean true for reverse axis.

Invert the axis of the wheel event.

scrollbar.reverseWheel(true);
console.log(scrollbar.reverseWheel); // true

scrollbar.update()

scrollbar.update(async?): void
Param Type Description
async boolean, optional Force an asynchronous update.

Forces scrollbar to update metrics.

By default, scrollbars are automatically updated with 100ms debounce (or childList changes if MutationObserver is supported). You can call this method to force an update when you modified contents inside scrollbar:

// append some contents to the scrollbar element...

// force an update
scrollbar.update();

// force an asynchronous update
scrollbar.update(true);

scrollbar.getSize()

scrollbar.getSize(): object

Return the size of scrollbar container element and scroll content element, it may be something like this:

{
    container: {
        width: 600,
        height: 400
    },
    content: {
        width: 1000,
        height: 3000
    }
}

scrollbar.setPosition()

scrollbar.setPosition(x, y): void
Param Type Description
x number Scrolling offset in x-axis.
y number Scrolling offset in y-axis.

Likes the window.scrollTo() method, use this method to set the scrollbar to the given offset without easing:

// limit = { x: 500, y: 500 }
scrollbar.setPosition(100, 100);
console.log(scrollbar.offset); // { x: 100, y: 100 }

If the offset is out of limitation, it will be shrinked:

// limit = { x: 0, y: 100 }
scrollbar.setPosition(1024, 1024);
console.log(scrollbar.offset); // { x: 0, y: 100 }

scrollbar.scrollTo()

scrollbar.scrollTo(x, y, duration?, callback?): void
Param Type Description
x number Scrolling offset in x-axis.
y number Scrolling offset in y-axis.
duration number, optional Easing duration, default is 0 (non-easing).
callback function, optional Callback function that will be fired on the end of scrolling.

Scrolls to given position with quadratic easing function:

scrollbar.scrollTo(100, 100, 300, function (scrollbar) {
    // ...
});

scrollbar.addListener()

scrollbar.addListener(listener): void
Param Type Description
listener (status) => void Scrolling event listener.

Since scrollbars will not fire a native scroll event, we need to registers thought scrollbar.addListener() method.

Notice: the callback functions will be invoked in every small scrolling, so be careful not to add time-consuming listeners that will slow down scrolling.

The status object contains following properties:

{
    direction: {
        // scrolling direction compares to the last offset
        x: 'none' | 'right' | 'left',
        y: 'none' | 'up' | 'down',
    },
    offset: {
        // current scrolling offset, equivalent to `scrollbar.offset`
        x: number,
        y: number,
    },
    limit: {
        // max-allowed scrolling distance, equivalent to `scrollbar.limit`
        x: number,
        y: number,
    }
}

scrollbar.scrollIntoView()

scrollbar.scrollIntoView(elem, options?): void
Param Type Description
elem Element Target element.
options object, optional Scrolling options, see the table below.
Property type default description
alignToTop boolean true Whether to align to the top or the bottom edge of container.
offsetTop number 0 Offset to top edge of container (used only if alignToTop is true).
offsetLeft number 0 Offset to left edge of container.
offsetBottom number 0 Offset to bottom edge of container (used only if alignToTop is false).
onlyScrollIfNeeded boolean false Whether to scroll container when target element is visible.

Scrolls the target element into visible area of scrollbar, like DOM method element.scrollIntoView(). This will be helpful when you want to create some anchors.

scrollbar.scrollIntoView(document.getElementById('a-section'), {
    offsetTop: 12,
    offsetLeft: 34,
    onlyScrollIfNeeded: true,
});

scrollbar.isVisible()

scrollbar.isVisible(elem): boolean
Param Type Description
elem Element Target element.

Checks if an element is visible in the current view area.

scrollbar.isVisible(document.getElementById('greet'))

scrollbar.infiniteScroll()

scrollbar.infiniteScroll(handler, threshold?): void
Param Type Description
handler (status) => void Infinite scrolling handler.
threshold number, optional Represents the trigger offset to the bottom edge of current scrollbar, default is 50.

This is another useful method when you want to make infinite scrolling. Handlers will be invoked with the status object when you scrolling down over the given threshold:

instance.infiniteScroll(function (status) {
    // ...
}, 100);

scrollbar.stop()

scrollbar.stop(): void

Clears all pending movements and stops scrolling immediately.

// offset = { x: 0, y: 0 }
scrollbar.scrollTo(100, 100);
scrollbar.stop();

console.log(scrollbat.offset); // { x: 0, y: 0 }

scrollbar.destroy()

scrollbar.destroy(): void

Remove this scrollbar instance:

Before:

<section data-scrollbar style="overflow: hidden">
    <article class="scroll-content">
        ...
    </article>

    <aside class="scrollbar-track scrollbar-track-x">
        <div class="scrollbar-thumb scrollbar-thumb-x"></div>
    </aside>
    <aside class="scrollbar-track scrollbar-track-y">
        <div class="scrollbar-thumb scrollbar-thumb-y"></div>
    </aside>
</section>

After:

<section data-scrollbar>
    ...
</section>

Common mistakes

Initialize a scrollbar without a limited width or height

Likes the native scrollbars, a scrollable area means the content insides it is larger than the container itself, for example, a 500*500 area with a content which size is 1000*1000.

Therefore, it necessary to set the width or height for the container element:

#my-scrollbar {
    width: 500px;
    height: 500px;
    overflow: auto;
}

FAQ

How to listen to the scroll event?

Since scrollbars will not fire a native scroll event, we need to registers thought scrollbar.addListener() method:

scrollbar.addListener(function (status) {
    // ...
});

Related Projects

Contributing

  • Create an issue with an example on jsbin.com, including latest version of smooth-scrollbar through:

    <link rel="stylesheet" href="https://unpkg.com/smooth-scrollbar@latest/dist/smooth-scrollbar.css">
    
    <script src="https://unpkg.com/smooth-scrollbar@latest/dist/smooth-scrollbar.js"></script>
    
  • Run your forks locally with following steps:

    1. Clone the repo: git clone https://github.com/idiotWu/smooth-scrollbar.git,
    2. Install dependencies: npm run install,
    3. Start server: npm start.

Before making pull requests, make sure you have run npm test!

License

MIT.