Vuebar
Vue 2 directive for custom scrollbar that uses native scroll behavior.
Lightweight, performant, customizable and without dependencies.
About
The first Vue 2 custom scrollbar library that only enhances scrolling instead of reimplementing it with custom scroll behavior. Created by Dominik Serafin.
It uses native scroll events to detect and synchronize scrollbar position. This makes possible to hack into the native events of scrolling element without hassle.
It was built for GGather.com where it plays nicely with a lot of dynamic content changes and most importantly infinite scrolling and drag and drop features. And now after testing, fixes, improvements and added customization options - it's ready to be shared with the world.
- Directive instead of a custom component, which saves a lot of headaches.
- Native scroll events, no jankiness, no hijacking.
- Simple to use, lightweight & performant.
- Works in browser and also build (webpack, etc.) environments.
- Compatible with all major browsers including IE9 and above.
- Useful customization options.
- No 3rd party dependencies.
- Tested in production.
Installation
NPM
npm install vuebar --save
CDN
https://unpkg.com/vuebar
Manual
Download the .js file directly from latest commit and include manually in your project
vuebar.jsAlternative #1: Include in browser
<script src="vuebar.js"></script>
Alternative #2: Include in your build
import Vuebar from 'vuebar';
Vue.use(Vuebar);
Usage
Basic Markup
<div v-bar> <!-- el1 -->
<div> <!-- el2 -->
<!-- your scrollable content -->
</div>
<!-- dragger will be automatically added here -->
</div>
Markup with example options
<div v-bar="{
preventParentScroll: true,
scrollThrottle: 30,
}"> <!-- el1 -->
<div> <!-- el2 -->
<!-- your scrollable content -->
</div>
<!-- dragger will be automatically added here -->
</div>
How it works
Every Vuebar scrollable content needs to be wrapped in parent element el1
that hides the native browser scrollbar of the second parent element el2
and it also contains the custom scrollbar element (referenced further as dragger
) which gets appended automatically on the Vuebar initialization.
New in 0.0.6: Instead of hiding the scrollbar using el1
Vuebar now has a useScrollbarPseudo
feature that hides the scrollbar using pseudo element selector ::-webkit-scrollbar
. Due to browsers limitations for now it works only on desktop Chrome & desktop Safari at the moment.
The Vuebar internals listen to el2
scroll event and synchronize dragger
position when you scroll. And vice versa - when you directly use dragger
it listens to mouse events to detect the position of dragger and sets scroll position of el2
.
Other than setting the scroll position when you use dragger
the Vuebar doesn't interfere at all with native browser scrolling and you can listen to scroll/wheel/etc. events as you would normally do.
This also means the scroll behavior will remain unchanged. You'll still be able to use mouse wheel click scrolling or touch scrolling with momentum. It doesn't try to reimplement the scrolling from scratch like most of similar libraries try (with bad results).
Public methods (experimental & work in progress)
You can access them from your component context: this.$vuebar
. Alternatively you can access them from your main Vue instance: Vue.vuebar
.
Every method needs to have passed Vuebar scroll container element as the first argument. You can do that easily using $refs
.
-
initScrollbar(DOM Element, options)
-
getState(DOM Element)
-
refreshScrollbar(DOM Element, options)
-
destroyScrollbar(DOM Element)
Tips
-
If you want to access internal Vuebar state (like scrollbar height, scrollbar top or even
dragger
element reference) then you can access_vuebarState
property of element 1 with v-bar directive. Or you can usegetState
method. -
Element
el1
has programmatically added stylesposition: relative
andoverflow: hidden
so you can't change them without using!important
. But you shouldn't do it anyway - your scrollbar will break. -
Element
el2
has programmatically added stylesoverflow-x: hidden
,overflow-y: scroll
,height: 100%
,-ms-overflow-style: scrollbar
andwidth
which value depends on browser. -
If the original browser scrollbar is floated/overlayed (
-ms-autohiding-scrollbar
, touch devices, etc.) thenel2
also haspadding-right
changed. -
Similar thing with
dragger
- it has programmatically added stylesposition: absolute
andheight
withtop
that have values in pixels relative to scroll position.
Styling
dragger
element. That element also has its own child - a dragger-styler
element that's purely there for you to style it any way you want. You can also style the original dragger
element - just remember it has position: absolute
style always applied and height
and top
styles dynamically added & changed when scrolling.
Default Style
If you were looking for this library it means you want to style your scrollbars as you want - so Vuebar doesn't include default styles internally. But if you e.g. want to quickly try or understand how you should style Vuebar scrollbars then below are prepared default styles that you can copy-paste into your project.
.vb > .vb-dragger {
z-index: 5;
width: 12px;
right: 0;
}
.vb > .vb-dragger > .vb-dragger-styler {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: rotate3d(0,0,0,0);
transform: rotate3d(0,0,0,0);
-webkit-transition:
background-color 100ms ease-out,
margin 100ms ease-out,
height 100ms ease-out;
transition:
background-color 100ms ease-out,
margin 100ms ease-out,
height 100ms ease-out;
background-color: rgba(48, 121, 244,.1);
margin: 5px 5px 5px 0;
border-radius: 20px;
height: calc(100% - 10px);
display: block;
}
.vb.vb-scrolling-phantom > .vb-dragger > .vb-dragger-styler {
background-color: rgba(48, 121, 244,.3);
}
.vb > .vb-dragger:hover > .vb-dragger-styler {
background-color: rgba(48, 121, 244,.5);
margin: 0px;
height: 100%;
}
.vb.vb-dragging > .vb-dragger > .vb-dragger-styler {
background-color: rgba(48, 121, 244,.5);
margin: 0px;
height: 100%;
}
.vb.vb-dragging-phantom > .vb-dragger > .vb-dragger-styler {
background-color: rgba(48, 121, 244,.5);
}
Customization options
Name | Value Type | Value Default | Description |
---|---|---|---|
preventParentScroll | Boolean | false | Prevent parent taking over scrolling after reaching bottom or top of Vuebar element. This feature is in beta - right now it supports only detecting mouse wheel event and doesn't support touch scrolling. Pull requests welcome. |
unselectableBody | Boolean | true | Disable user select on body when using dragger. If this value is false then user select will be only disabled on Vuebar element. |
useScrollbarPseudo | Boolean | false | This is an experimental feature that works only in desktop Google Chrome and desktop Apple Safari. Instead of hiding scrollbars by using hidden overflow this feature will hide them using ::-webkit-scrollbar pseudo element selectors. |
overrideFloatingScrollbar | Boolean | true | Show dragger if the element has a floating/overlaying scrollbar. It happens on touch devices and on IE & Edge with -ms-autohiding-scrollbar . |
scrollThrottle | Integer | 10 | How frequently in ms should dragger position calculations take place on scroll event. Setting this above 30 will make dragger position refresh choppy, but may help with performance in extreme situations. |
draggerThrottle | Integer | 10 | How frequently in ms should scroll position update when using dragger. Setting this above 30 will make scroll synchronization choppy, but may help with performance in extreme situations. |
observerThrottle | Integer | 100 | How frequently in ms should scroll position update when there's a DOM change detected from MutationObserver. It fires in 3 scenarios; subtree change, childList change and characterData change. |
resizeRefresh | Boolean | true | Refresh dragger positions on window resize. |
resizeDebounce | Integer | 100 | Debounce value in ms controlling how frequently resize refresh is fired. Relevant only if resizeRefresh is enabled. |
scrollingPhantomDelay | Integer | 1000 | How long in ms should phantom scrolling class stay applied to element. |
draggingPhantomDelay | Integer | 1000 | How long in ms should phantom dragging class stay applied to element. Warning - setting this below value of scrollThrottle may have some unforeseen effects. |
Customization options (custom classes)
Name | Value Type | Value Default | Description |
---|---|---|---|
el1Class | String | 'vb' | Element 1 class. |
el1ScrollVisibleClass | String | 'vb-visible' | Added dynamically when the scrollbar is visible. |
el1ScrollInvisibleClass | String | 'vb-invisible' | Added dynamically when the scrollbar is invisible. |
el1ScrollingClass | String | 'vb-scrolling' | Added dynamically on scrolling. |
el1ScrollingPhantomClass | String | 'vb-scrolling-phantom' | Added dynamically on scrolling and removed after value of scrollingPhantomDelay. |
el1DraggingClass | String | 'vb-dragging' | Added dynamically when the dragger is dragged or pressed. |
el1DraggingPhantomClass | String | 'vb-dragging-phantom' | Added dynamically when the dragger is dragged or pressed and removed after value of draggingPhantomDelay. |
el2Class | String | 'vb-content' | Element 2 class. |
draggerClass | String | 'vb-dragger' | Dragger class. |
draggerStylerClass | String | 'vb-dragger-styler' | Dragger styler class. |
Examples
Other than examples below - this site itself is an example of Vuebar usage. If you look to the right it has a custom white scrollbar that showcases itself pretty nicely.
1. JSFiddle
2. Default without options
3. Option preventParentScroll
set to true
.
4. Option overrideFloatingScrollbar
set to false
. Difference visible only on touch devices and on IE & Edge with -ms-autohiding-scrollbar
style.
5. Option useScrollbarPseudo
set to true
It works only on Chrome & Safari. Inspect el2
to see the difference.
6. Choppy but performant. Options scrollThrottle
and draggerThrottle
set to 100
.
7. Dynamic Container
8. Scrollbar invisible. Content is not tall enough.
9. Max-Height example
Contributing & Credits
Contributing
There's still a lot that can be introduced to Vuebar. If you're willing you can help out with the issues below.
- [High priority] Support for horizontal scrolling.
- [Medium priority] Support for Vue 1.x.
- [Medium priority] Automated tests for pull requests & general development.
- [Medium priority] Support for compatibility of Vuebar element classes with component scoped CSS selectors. (Is it even possible?).
-
[Medium priority]
preventParentScroll
supporting scrolling with scrolling while dragging cursor, touch scrolling and also keyboard keys (space, shift+space, arrows, page up, pagedown). Maybe with options which ones to prevent and which ones to leave out. - [Low priority] Support for scrollbar refresh on orientationchange event.
- [Low priority] Support for dragging dragger using touch events.
- [Low priority] Utility methods like; scroll to top/bottom/element/etc., force disable scrolling, dragger active class flash. And (disableable) events emit for e.g. when content was scrolled to top/bottom/element/etc.
-
[Low priority]
useScrollbarPseudo
styles added with custom class instead ofel1Class
.
Credits
Created by Dominik Serafin for GGather.com.
Inspired by nanoScroller and Antiscroll.