Custom overlay-scrollbars with native scrolling mechanism for Angular, it also provides a cross-browser smooth scroll directive.
- Live Demo V4
- Installation
- Usage
- Options
- Scroll Functions
- Styling
- Update scrollbars manually
- Smooth Scroll
- Virtual Scroll
- Development
- Issues
- Author
- More plugins
NPM
npm i ngx-scrollbar @angular/cdk
YARN
yarn add ngx-scrollbar @angular/cdk
Import NgScrollbarModule
in your module
import { NgScrollbarModule } from 'ngx-scrollbar';
@NgModule({
imports: [
// ...
NgScrollbarModule
]
})
In your template
<ng-scrollbar>
<!-- Content -->
</ng-scrollbar>
Here is a stackblitz
-
[trackX]: boolean
Horizontal scrollbar, default
false
-
[trackY]: boolean
Vertical scrollbar, default
true
-
[invertX]: boolean
Invert horizontal scrollbar position, default
false
-
[invertY]: boolean
Invert vertical scrollbar position, default
false
-
[visibility]: 'native' | 'hover' | 'always', default
native
Configure when scrollbars should be shown.
native
: scrollbars are shown when content is scrollable.hover
: scrollbars are shown when mouse is over the view port (and content is scrollable).always
: scrollbars are always shown.
-
[autoUpdate]: boolean
Auto-update scrollbars on content changes, default:
true
-
[viewClass]: string
Add custom class to the view, default:
null
-
[barClass]: string
Add custom class to scrollbars, default:
null
-
[thumbClass]: string
Add custom class to scrollbars' thumbnails, default:
null
-
[compact]: boolean
Make scrollbars position appears over content, default:
false
-
[disabled]: boolean
Disable the custom scrollbars and use the native ones instead, default:
false
-
[scrollToDuration]: number
The smooth scroll duration when a scrollbar is clicked, default
400
. -
[disableOnBreakpoints]: Array of the CDK Breakpoints
Disable custom scrollbars on specific breakpoints, default:
[Breakpoints.HandsetLandscape, Breakpoints.HandsetPortrait]
Because it is not possible to hide the native scrollbars on mobile browsers, the only solution is to fallback to the native scrollbars. To disable this option give it false value.
To use NgScrollbar functions, you will need to get the component reference from the template. this can be done using the @ViewChild
decorator, for example:
@ViewChild(NgScrollbar) scrollRef: NgScrollbar;
Example: Subscribe to NgScrollbar
scroll event
@ViewChild(NgScrollbar) scrollbarRef: NgScrollbar;
ngAfterViewInit() {
this.scrollbarRef.scrollable.elementScrolled().subscribe(e => console.log(e))
}
We use the property ScrollbarRef.scrollable
to get the scroll event, See CdkScrollable API.
Note: in order to avoid hitting change detection for every scroll event, all of the events emitted from this stream will be run outside the Angular zone. If you need to update any data bindings as a result of a scroll event, you have to run the callback using
NgZone.run
.
This means that if you are using the scroll event to update the UI, you will need to use ngZone
.
Example: Change header title on scroll event
@Component({
selector: 'app-page-title',
template: `
<ng-scrollbar>
<div class="page-title" [style.fontSize]="size$ | async">
<h1>Hello World</h1>
</div>
<div [innerHTML]="longScrollableText"></div>
</ng-scrollbar>
`
})
export class PageTitleComponent {
// Stream that will update title font size on scroll down
size$ = new Subject();
// Unsubscriber for elementScrolled stream.
unsubscriber$ = Subscription.EMPTY;
// Get NgScrollbar reference
@ViewChild(NgScrollbar) scrollbarRef: NgScrollbar;
// long text that make ng-scrollbar scrollable
longScrollableText = `...`
constructor(private ngZone: NgZone) {
}
ngAfterViewInit() {
// Subscribe to <ng-scrollbar> scroll event
this.unsubscriber$ = this.scrollbarRef.scrollable.elementScrolled().pipe(
map((e: any) => e.target.scrollTop > 50 ? '0.75em' : '1em'),
tap((size: string) => this.ngZone.run(() => this.size$.next(size)))
).subscribe();
}
ngOnDestroy() {
this.unsubscriber$.unsubscribe();
}
}
Check out the example in this stackblitz
All scroll functions return a cold observable that requires calling subscribe()
, it will emits once scrolling is done and unsubscribe itself, no need to unsubscribe from the function manually.
scrollRef.scrollTo(options: ScrollToOptions).subscribe()
- Left: x position.
- Top: y position.
- Duration: time to reach position in milliseconds, default null.
- EaseFunc: the easing function for the smooth scroll.
scrollRef.scrollToElement(selector, offset?, duration?, easeFunc?).subscribe()
- Selector: target element selector.
- Offset: Set scroll offset, default 0.
- Duration: time to reach position in milliseconds, default null.
- EaseFunc: the easing function for the smooth scroll.
scrollRef.scrollXTo(position, duration?, easeFunc?).subscribe()
scrollRef.scrollYTo(position, duration?, easeFunc?).subscribe()
- Position: scrolling position on Y axis in pixels.
- Duration: time to reach position in milliseconds, default null.
- EaseFunc: the easing function for the smooth scroll.
scrollRef.scrollToTop(duration?, easeFunc?).subscribe()
scrollRef.scrollToBottom(duration?, easeFunc?).subscribe()
scrollRef.scrollToLeft(duration?, easeFunc?).subscribe()
scrollRef.scrollToRight(duration?, easeFunc?).subscribe()
- Duration: time to reach position in milliseconds, default null.
- EaseFunc: the easing function for the smooth scroll.
<ng-scrollbar #scrollbarRef>
<!-- Content -->
</ng-scrollbar>
<button (click)="scrollbarRef.scrollToTop()">Go to top</button>
<ng-scrollbar #scrollbarRef>
<div id="..."></div>
<div id="..."></div>
<div id="..."></div>
<div id="usage"></div>
<div id="..."></div>
</ng-scrollbar>
<button (click)="scrollbarRef.scrollToElement('#usage')">Usage Section</button>
Or using the @ViewChild
decorator
@ViewChild(NgScrollbar) scrollRef: NgScrollbar;
scrollToUsageSection() {
this.scrollRef.scrollToElement('#usage');
}
If you wrap the <router-outlet>
with <ng-scrollbar>
, you can scroll to top on route changes, like the following example:
export class AppComponent {
@ViewChild(NgScrollbar) scrollRef: NgScrollbar;
constructor(router: Router) {
router.events.pipe(
filter(event => event instanceof NavigationEnd)),
filter(() => !!this.scrollRef)),
tap((event: NavigationEnd) => this.scrollRef.scrollToTop())
).subscribe();
}
}
NgScrollbar
component updates properly, but in case something went wrong or you need to update the scrollbar manually, you can call the update method to fix it, e.g. myScrollbar.update()
.
Text area example:
Component({
selector: 'text-area-example',
template: `
<ng-scrollbar>
<textarea [(ngModel)]="text"></textarea>
</ng-scrollbar>
`
})
export class AppComponent implements OnInit {
@ViewChild(NgScrollbar) textAreaScrollbar: NgScrollbar;
setText(value: string) {
this.text = value;
// You might need to give a tiny delay before updating the scrollbar
setTimeout(() => {
this.textAreaScrollbar.update();
}, 200);
}
}
You can also automatically resize the <text-area>
with the CDK Text-field.
<ng-scrollbar>
<textarea cdkTextareaAutosize #autosize="cdkTextareaAutosize" [(ngModel)]="code"></textarea>
</ng-scrollbar>
@ViewChild(NgScrollbar) textAreaScrollbar: NgScrollbar;
@ViewChild(CdkTextareaAutosize) textareaAutosize: CdkTextareaAutosize;
setCode(code: string) {
this.code = code;
this.textareaAutosize.resizeToFitContent();
setTimeout(() => {
this.textAreaScrollbar.update();
}, 200);
}
Since v3.4.0
, you can customize the scrollbar styles from the following CSS variables
<ng-scrollbar class="my-scrollbar">
<!-- content -->
</ng-scrollbar>
.my-scrollbar {
--scrollbar-color: transparent;
--scrollbar-container-color: transparent;
--scrollbar-thumb-color: rgba(0, 0, 0, 0.2);
--scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.3);
--scrollbar-border-radius: 4px;
--scrollbar-size: 6px;
--scrollbar-padding: 8px;
--scroll-view-margin: 0;
--scroll-view-color: transparent;
}
You can also use custom classes to override the styles
<ng-scrollbar barClass="scrollbar" thumbClass="scrollbar-thumbs">
<!-- content -->
</ng-scrollbar>
::ng-deep {
ng-scrollbar.scrollbar {
background-color: rgba(0, 0, 0, 0.4);
border-radius: 4px;
}
ng-scrollbar.scrollbar-thumbs {
background-color: rgba(161, 27, 27, 0.4);
&:hover,
&:active {
background-color: rgba(161, 27, 27, 0.7);
}
}
}
The [smoothScroll]
directive allows you to scroll the host element smoothly using the scroll functions that works on cross-browser.
Since v3.0.0, The SmoothScrollModule
has been added as an independent module, the scrollable element does not have to be <ng-scrollbar>
.
import { SmoothScrollModule } from 'ngx-scrollbar';
@NgModule({
imports: [
// ...
SmoothScrollModule
]
})
<div smoothScroll #scrollable class="scrollable-container}">
<!-- child elements -->
</div>
<button (click)="scrollable.scrollToBottom(500)">Scroll to bottom</button>
See all Scroll Functions.
Subscribe to NgScrollbar
scroll event
Since v4.2.0, NgScrollbar
has added support for virtual scrolling using the CdkVirtualScrollViewport
To use virtual scroll, you will need to add the ngScrollbarView
directive along with smoothScroll
directive on <CdkVirtualScrollViewport>
.
Example:
<ng-scrollbar>
<cdk-virtual-scroll-viewport ngScrollbarView smoothScroll itemSize="50">
<div *cdkVirtualFor="let item of items">{{item}}</div>
</cdk-virtual-scroll-viewport>
</ng-scrollbar>
Here is a stackblitz example
This project uses the Angular CLI for building the library.
$ ng build ngx-scrollbar --prod
or
$ npm run build-lib
If you identify any errors in the library, or have an idea for an improvement, please open an issue.