PS v1 is still in heavy development and everything can be discussed and changed later.
It's being developed in the next branch,
so please take a look if interested.
The following code shows how PS v1 will be used. For the detailed information,
some parts are tagged with a number following *.
The details are described below with the indexing number.
import ps from 'perfect-scrollbar';
// initialise
const instance = ps('#container', { // *1
mount: '#scrollbar-mount',
emulators: [
... // *2
]
});
// instance management
instance.update(); // *3
instance.destroy(); // *4
// events
instance.addEventListener('reach-start-x', () => { }); // *5The main function will return a perfect-scrollbar instance. Until now, PS assign each initialisation an random ID, and saves its data in a separated object with using the ID as a key. It was good to make PS work similar to how jQuery works, but make its data and event management extremly complicated. Now, everything will be done in this instance, which doesn't depend on weird random IDs or separated data structures.
The option emulators contains scroll emulators, such as rail clicking and dragging, keyboard, wheel, or touch.
I'm thinking about making the emulators as separated NPM modules, which make it easy for contributors to maintain
each emulator, and also make users to create and use their own scroll emulators.
The option's value will have a type Array<?> but the ? is not decided yet. When using String for each emulator,
it will work in the similar way to how Webpack loads loaders.
ps('#container', {
mount: '#mount',
emulators: ['wheel', 'touch'],
});The code above will load ps-emulator-wheel and ps-emulator-touch for example. But the problem is when we need to
specify options for each emulator, for example stopPropagate for wheel.
ps('#container', {
mount: '#mount',
emulators: [
['wheel', { stopPropagate: false }], // eslint style
'touch',
],
});The code above doesn't look so consistent to me.
Another way is to use the module itself.
import wheel from 'ps-emulator-wheel';
import touch from 'ps-emulator-touch';
ps('#container', {
mount: '#mount',
emulators: [
wheel({ stopPropagate: false }),
touch, // the same as touch()
],
});The code above looks okay to me, but it's a little bit verbose.
If you have any idea or suggestion, please feel free to leave a comment here or on the PS v1 issue.
The update method works quite similarly to how it has been worked. It can still be called
manually as we've done so far, but it will automatically be called when possible.
- MutationObserver will be
watching a container element.
A case an observer can't observe is computed style changes. For example, when geometry is changed because of the change of parent'sclassName, viewport, etc. These indirect changes cannot be caught by the observer and users should callupdatemanually. scrollTopandscrollLeftchanges are captured byscrollevent.
Also, it won't manipulate scrollbar geometry directly. It will only set a flag dirty to true.
The actual manipulation will be done in a separated event loop, fired
by requestAnimationFrame,
only when dirty is true.
It will make PS easier to maintain, as well as performant.
The destroy process consists of the following subprocesses.
- Destroy emulators
- Destroy
MutationObserver - Destroy
requestAnimationFrameevent loop
Now, the PS instance will work as a EventEmitter. It makes it easy to manage events under an instance, without polluting the original element's event namespace.

Todos
render()to actually calculate scrollbar geometriesdestroy()