Forked from gangadharjannu/infinite-scroll.directive.ts
Created
June 19, 2020 00:08
-
-
Save pauloantonelli/9ad82d0b91c0c40f524364d7d0b0b6af to your computer and use it in GitHub Desktop.
This is an Angular 2 infinite scroll directive. It is simple, easy to use and very CPU-efficient.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// USAGE: | |
// | |
// When you attach the infiniteScroll directive to an element, it will emit the infiniteScrollAction | |
// @Output() event every time the user has scrolled to the bottom of the element. Your loadMoreArticles | |
// function can make an HTTP call and append the results to the articles list, for example. In doing this, | |
// you effectively increase the height of the element and thus begin the process of the infiniteScroll directive | |
// again, over and over until the element height stops increasing. | |
// | |
// <div class="container" infiniteScroll (infiniteScrollAction)="loadMoreArticles()"> | |
// <div class="article" *ngFor="let article of articles"> | |
// ... | |
// </div> | |
// </div> | |
// | |
// <div class="container" infiniteScroll [infiniteScrollContext]="'self'" (infiniteScrollAction)="loadMoreArticles()"> | |
// <div class="article" *ngFor="let article of articles"> | |
// ... | |
// </div> | |
// </div> | |
import { Directive, Input, Output, EventEmitter, ElementRef, HostListener, OnInit } from '@angular/core'; | |
export interface Viewport { | |
h: number; | |
w: number; | |
} | |
// InfiniteScrollContext represents the context in which the directive will run. | |
// | |
// The default is 'document' and this will trigger your action when the end of | |
// your element has been reached relative to the documents scrollbar. | |
// | |
// If you use 'self', your action will be triggered when the end of your element | |
// has been reached relative to your elements own scrollbar. | |
export type InfiniteScrollContext = 'self' | 'document'; | |
@Directive({ | |
selector: '[infiniteScroll]', | |
}) | |
export class InfiniteScrollDirective implements OnInit { | |
el: any; | |
viewport: Viewport; | |
canTriggerAction: boolean = true; | |
@Input() infiniteScrollContext: InfiniteScrollContext = 'document'; | |
@Output() infiniteScrollAction: EventEmitter<any> = new EventEmitter(); | |
@HostListener('scroll', ['$event']) onElementScroll() { | |
if (this.infiniteScrollContext === 'self') { | |
if (this.elementEndReachedInSelfScrollbarContext() && this.canTriggerAction) { | |
this.triggerAction(); | |
} | |
} | |
} | |
constructor(private element: ElementRef) { | |
this.el = element.nativeElement; | |
this.viewport = this.getViewport(window); | |
} | |
ngOnInit() { | |
if (this.infiniteScrollContext === 'document') { | |
document.addEventListener('scroll', () => { | |
if (this.elementEndReachedInDocumentScrollbarContext(window, this.el) && this.canTriggerAction) { | |
this.triggerAction(); | |
} | |
}); | |
} | |
} | |
triggerAction() { | |
this.canTriggerAction = false; | |
this.infiniteScrollAction.emit(null); | |
} | |
elementEndReachedInSelfScrollbarContext(): boolean { | |
if (this.el.scrollTop + this.el.offsetHeight >= this.el.scrollHeight) { | |
this.canTriggerAction = true; | |
return true; | |
} | |
return false; | |
} | |
elementEndReachedInDocumentScrollbarContext(win: Window, el: any): boolean { | |
const rect = el.getBoundingClientRect(); | |
const elementTopRelativeToViewport = rect.top; | |
const elementTopRelativeToDocument = elementTopRelativeToViewport + win.pageYOffset; | |
const scrollableDistance = el.offsetHeight + elementTopRelativeToDocument; | |
const currentPos = win.pageYOffset + this.viewport.h; | |
if (currentPos > scrollableDistance) { | |
this.canTriggerAction = true; | |
return true; | |
} | |
return false; | |
} | |
private getViewport(win: Window): Viewport { | |
// This works for all browsers except IE8 and before | |
if (win.innerWidth != null) { | |
return { | |
w: win.innerWidth, | |
h: win.innerHeight | |
}; | |
} | |
// For IE (or any browser) in Standards mode | |
let d = win.document; | |
if (document.compatMode == 'CSS1Compat') { | |
return { | |
w: d.documentElement.clientWidth, | |
h: d.documentElement.clientHeight | |
}; | |
} | |
// For browsers in Quirks mode | |
return { | |
w: d.body.clientWidth, | |
h: d.body.clientHeight | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment