Smooth Scrollbar
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:
-
As an element:
<scrollbar>...</scrollbar>
-
As an attribute:
<section scrollbar>...</section>
-
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]
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();
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);
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;
Scrollbar.get(document.documentElement);
Scrollbar.getAll()
Scrollbar.getAll(): [SmoothScrollbar]
Return an array that contains all scrollbar instances:
Scrollbar.getAll();
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
An object that contains main parts of current scrollbar:
{
container,
content,
xAxis: { track, thumb },
yAxis: { track, thumb }
}
scrollbar.offset
Current scrolling offset:
console.log(scrollbar.offset);
scrollbar.limit
Max-allowed scrolling distance:
console.log(scrollbar.limit);
scrollbar.scrollTop
Alias for scrollbar.offset.y
:
console.log(scrollbar.scrollTop);
console.log(scrollbar.scrollTop === scrollbar.offset.y);
scrollbar.scrollLeft
Alias for scrollbar.offset.x
:
console.log(scrollbar.scrollLeft);
console.log(scrollbar.scrollLeft === scrollbar.offset.x);
scrollbar.wheelReversed
Whether event wheel
is reversed:
console.log(scrollbar.wheelReversed);
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);
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:
scrollbar.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:
scrollbar.setPosition(100, 100);
console.log(scrollbar.offset);
If the offset is out of limitation, it will be shrinked:
scrollbar.setPosition(1024, 1024);
console.log(scrollbar.offset);
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: {
x: 'none' | 'right' | 'left',
y: 'none' | 'up' | 'down',
},
offset: {
x: number,
y: number,
},
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.
scrollbar.scrollTo(100, 100);
scrollbar.stop();
console.log(scrollbat.offset);
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:
- Clone the repo:
git clone https://github.com/idiotWu/smooth-scrollbar.git
, - Install dependencies:
npm run install
, - Start server:
npm start
.
Before making pull requests, make sure you have run npm test
!
License
MIT.