ngx-hide-on-scroll
Version:
Hide an element on scroll down or up in Angular.
172 lines (166 loc) • 7.13 kB
JavaScript
import { EventEmitter, Directive, ElementRef, Renderer2, Inject, PLATFORM_ID, Input, Output, NgModule } from '@angular/core';
import { Subject, fromEvent } from 'rxjs';
import { takeUntil, throttleTime, map, pairwise, distinctUntilChanged, share, filter } from 'rxjs/operators';
import { isPlatformServer } from '@angular/common';
// Inspired by: https://netbasal.com/reactive-sticky-header-in-angular-12dbffb3f1d3
/**
* The `ngxHideOnScroll` directive allows you to hide an html element (e.g. navbar) on scroll down and show it again on scroll up.
*/
class NgxHideOnScrollDirective {
constructor(elementRef, renderer2, platformId) {
this.elementRef = elementRef;
this.renderer2 = renderer2;
this.platformId = platformId;
/**
* `'Down'`: The element will be hidden on scroll down and it will be shown again on scroll up.<br/>`Up`: The element will be hidden on scroll up and it will be shown again on scroll down.
*/
this.hideOnScroll = 'Down';
/**
* CSS class name added to the element to hide it. When this property is set, `propertyUsedToHide`, `valueWhenHidden`, and `valueWhenShown` have not effect.
*/
this.classNameWhenHidden = '';
/**
* The CSS property used to hide/show the element.
*
* @default
* 'transform'
*/
this.propertyUsedToHide = 'transform';
/**
* The value of `propertyUsedToHide` when the element is hidden.
*
* @default
* 'translateY(-100%)'
*/
this.valueWhenHidden = 'translateY(-100%)';
/**
* The value of `propertyUsedToHide` when the element is shown.
*
* @default
* 'translateY(0)'
*/
this.valueWhenShown = 'translateY(0)';
/**
* The selector of the element you want to listen the scroll event, in case it is not the default browser scrolling element (`document.scrollingElement` or `document.documentElement`). For example [` .mat-sidenav-content`]( https://stackoverflow.com/a/52931772/12954396) if you are using [Angular Material Sidenav]( https://material.angular.io/components/sidenav)
*/
this.scrollingElementSelector = '';
/**
* Emitted when the element is hidden.
*/
this.eventElementHidden = new EventEmitter();
/**
* Emitted when the element is shown.
*/
this.eventElementShown = new EventEmitter();
this.unsubscribeNotifier = new Subject();
}
ngAfterViewInit() {
if (isPlatformServer(this.platformId)) {
return;
}
let elementToListenScrollEvent;
let scrollingElement;
if (!this.scrollingElementSelector) {
elementToListenScrollEvent = window;
scrollingElement = this.getDefaultScrollingElement();
}
else {
scrollingElement = document.querySelector(this.scrollingElementSelector);
if (!scrollingElement) {
console.error(`NgxHideOnScroll: scrollingElementSelector\nElement with selector: "${this.scrollingElementSelector}" not found.`);
return;
}
elementToListenScrollEvent = scrollingElement;
}
const scroll$ = fromEvent(elementToListenScrollEvent, 'scroll').pipe(takeUntil(this.unsubscribeNotifier), throttleTime(50), // only emit every 50 ms
map(() => scrollingElement.scrollTop), // get vertical scroll position
pairwise(), // look at this and the last emitted element
// compare this and the last element to figure out scrolling direction
map(([y1, y2]) => (y2 < y1 ? ScrollDirection.Up : ScrollDirection.Down)), distinctUntilChanged(), // only emit when scrolling direction changed
share() // share a single subscription to the underlying sequence in case of multiple subscribers
);
const scrollUp$ = scroll$.pipe(filter(direction => direction === ScrollDirection.Up));
const scrollDown$ = scroll$.pipe(filter(direction => direction === ScrollDirection.Down));
let scrollUpAction;
let scrollDownAction;
if (this.hideOnScroll === 'Up') {
scrollUpAction = () => this.hideElement();
scrollDownAction = () => this.showElement();
}
else {
scrollUpAction = () => this.showElement();
scrollDownAction = () => this.hideElement();
}
scrollUp$.subscribe(() => scrollUpAction());
scrollDown$.subscribe(() => scrollDownAction());
}
ngOnDestroy() {
this.unsubscribeNotifier.next();
this.unsubscribeNotifier.complete();
}
hideElement() {
const nativeElement = this.elementRef.nativeElement;
if (this.classNameWhenHidden) {
this.renderer2.addClass(nativeElement, this.classNameWhenHidden);
}
else {
this.renderer2.setStyle(nativeElement, this.propertyUsedToHide, this.valueWhenHidden);
}
this.eventElementHidden.emit();
}
showElement() {
const nativeElement = this.elementRef.nativeElement;
if (this.classNameWhenHidden) {
this.renderer2.removeClass(nativeElement, this.classNameWhenHidden);
}
else {
this.renderer2.setStyle(nativeElement, this.propertyUsedToHide, this.valueWhenShown);
}
this.eventElementShown.emit();
}
getDefaultScrollingElement() {
return (document.scrollingElement || document.documentElement);
}
}
NgxHideOnScrollDirective.decorators = [
{ type: Directive, args: [{
selector: '[ngxHideOnScroll]'
},] }
];
NgxHideOnScrollDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: String, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }
];
NgxHideOnScrollDirective.propDecorators = {
hideOnScroll: [{ type: Input }],
classNameWhenHidden: [{ type: Input }],
propertyUsedToHide: [{ type: Input }],
valueWhenHidden: [{ type: Input }],
valueWhenShown: [{ type: Input }],
scrollingElementSelector: [{ type: Input }],
eventElementHidden: [{ type: Output }],
eventElementShown: [{ type: Output }]
};
var ScrollDirection;
(function (ScrollDirection) {
ScrollDirection["Up"] = "Up";
ScrollDirection["Down"] = "Down";
})(ScrollDirection || (ScrollDirection = {}));
class NgxHideOnScrollModule {
}
NgxHideOnScrollModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgxHideOnScrollDirective],
imports: [],
exports: [NgxHideOnScrollDirective]
},] }
];
/*
* Public API Surface of ngx-hide-on-scroll
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxHideOnScrollDirective, NgxHideOnScrollModule };
//# sourceMappingURL=ngx-hide-on-scroll.js.map