UNPKG

ng-scroll-spy

Version:

Angular directive that tracking the scrolling of the document and highlight the navigation link

95 lines 11.4 kB
import { Directive, HostListener, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { extractElementPosition } from 'ng-html-util'; import * as i0 from "@angular/core"; export class ScrollSpyDirective { constructor(document, el, renderer) { this.document = document; this.el = el; this.renderer = renderer; this.elements = []; this.directNavigation = false; } ngAfterContentInit() { this.collectIds(); } collectIds() { this.elements = []; let elements = this.el.nativeElement.querySelectorAll('a'); for (let i = 0; i < elements.length; i++) { let elem = elements.item(i); let id = ScrollSpyDirective.getId(elem); if (!id) continue; let destination = this._getPeerElement(id); if (!destination) continue; elem.addEventListener('click', this._onLinkClicked.bind(this)); this.elements.push({ id, link: elem, destination }); } } _onLinkClicked(event) { event.preventDefault(); let target = event.currentTarget; let id = ScrollSpyDirective.getId(target); let destination = this._getPeerElement(id); this.directNavigation = true; let position = extractElementPosition(this.document, destination); window.scrollTo({ top: position.top - 25, left: 0, behavior: 'smooth' }); this._cleanCurrentLink(); this._setCurrentLink(target); this.directNavigation = false; } _getPeerElement(id) { let destination = this.document.getElementById(id); if (!destination) return null; return destination; } static getId(elem) { let href = elem.getAttribute('href'); if (!href) return null; return href.replace('#', ''); } onWindowScroll(event) { if (this.directNavigation) return; for (let elem of this.elements) { let top = elem.destination.getBoundingClientRect().top; if (top > 0 && top < 25) { this._cleanCurrentLink(); this._setCurrentLink(elem.link); break; } } } _cleanCurrentLink() { if (!this.currentActiveLink) return; this.renderer.removeClass(this.currentActiveLink, 'active'); } _setCurrentLink(elem) { this.currentActiveLink = elem; this.renderer.addClass(this.currentActiveLink, 'active'); } } ScrollSpyDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: ScrollSpyDirective, deps: [{ token: DOCUMENT }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); ScrollSpyDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: ScrollSpyDirective, selector: "[scroll-spy]", host: { listeners: { "window:scroll": "onWindowScroll($event)" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: ScrollSpyDirective, decorators: [{ type: Directive, args: [{ selector: '[scroll-spy]' }] }], ctorParameters: function () { return [{ type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { onWindowScroll: [{ type: HostListener, args: ["window:scroll", ['$event']] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmctc2Nyb2xsLXNweS5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZGlyZWN0aXZlcy9uZy1zY3JvbGwtc3B5LmRpcmVjdGl2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQW1CLFNBQVMsRUFBYyxZQUFZLEVBQUUsTUFBTSxFQUFZLE1BQU0sZUFBZSxDQUFDO0FBQ3ZHLE9BQU8sRUFBQyxRQUFRLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUV6QyxPQUFPLEVBQUMsc0JBQXNCLEVBQUMsTUFBTSxjQUFjLENBQUM7O0FBS3BELE1BQU0sT0FBTyxrQkFBa0I7SUFNN0IsWUFBc0MsUUFBa0IsRUFDcEMsRUFBYyxFQUNkLFFBQW1CO1FBRkQsYUFBUSxHQUFSLFFBQVEsQ0FBVTtRQUNwQyxPQUFFLEdBQUYsRUFBRSxDQUFZO1FBQ2QsYUFBUSxHQUFSLFFBQVEsQ0FBVztRQU4vQixhQUFRLEdBQUcsRUFBRSxDQUFDO1FBRWQscUJBQWdCLEdBQUcsS0FBSyxDQUFDO0lBS2pDLENBQUM7SUFFTSxrQkFBa0I7UUFDdkIsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3BCLENBQUM7SUFFTyxVQUFVO1FBQ2hCLElBQUksQ0FBQyxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ25CLElBQUksUUFBUSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTNELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3hDLElBQUksSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFNUIsSUFBSSxFQUFFLEdBQUcsa0JBQWtCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3hDLElBQUksQ0FBQyxFQUFFO2dC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LENBQUMsQ0FBQztJQUMzRCxDQUFDOzsrR0F0R1Usa0JBQWtCLGtCQU1ULFFBQVE7bUdBTmpCLGtCQUFrQjsyRkFBbEIsa0JBQWtCO2tCQUg5QixTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSxjQUFjO2lCQUN6QjswREFPaUQsUUFBUTswQkFBM0MsTUFBTTsyQkFBQyxRQUFROzZGQXdFNUIsY0FBYztzQkFEYixZQUFZO3VCQUFDLGVBQWUsRUFBRSxDQUFDLFFBQVEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7QWZ0ZXJDb250ZW50SW5pdCwgRGlyZWN0aXZlLCBFbGVtZW50UmVmLCBIb3N0TGlzdGVuZXIsIEluamVjdCwgUmVuZGVyZXIyfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7RE9DVU1FTlR9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5cbmltcG9ydCB7ZXh0cmFjdEVsZW1lbnRQb3NpdGlvbn0gZnJvbSAnbmctaHRtbC11dGlsJztcblxuQERpcmVjdGl2ZSh7XG4gIHNlbGVjdG9yOiAnW3Njcm9sbC1zcHldJ1xufSlcbmV4cG9ydCBjbGFzcyBTY3JvbGxTcHlEaXJlY3RpdmUgaW1wbGVtZW50cyBBZnRlckNvbnRlbnRJbml0IHtcblxuICBwcml2YXRlIGVsZW1lbnRzID0gW107XG4gIHByaXZhdGUgY3VycmVudEFjdGl2ZUxpbms7XG4gIHByaXZhdGUgZGlyZWN0TmF2aWdhdGlvbiA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKEBJbmplY3QoRE9DVU1FTlQpIHByaXZhdGUgZG9jdW1lbnQ6IERvY3VtZW50LFxuICAgICAgICAgICAgICBwcml2YXRlIGVsOiBFbGVtZW50UmVmLFxuICAgICAgICAgICAgICBwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjIpIHtcbiAgfVxuXG4gIHB1YmxpYyBuZ0FmdGVyQ29udGVudEluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5jb2xsZWN0SWRzKCk7XG4gIH1cblxuICBwcml2YXRlIGNvbGxlY3RJZHMoKTogdm9pZCB7XG4gICAgdGhpcy5lbGVtZW50cyA9IFtdO1xuICAgIGxldCBlbGVtZW50cyA9IHRoaXMuZWwubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCdhJyk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVsZW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBsZXQgZWxlbSA9IGVsZW1lbnRzLml0ZW0oaSk7XG5cbiAgICAgIGxldCBpZCA9IFNjcm9sbFNweURpcmVjdGl2ZS5nZXRJZChlbGVtKTtcbiAgICAgIGlmICghaWQpXG4gICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICBsZXQgZGVzdGluYXRpb24gPSB0aGlzLl9nZXRQZWVyRWxlbWVudChpZCk7XG5cbiAgICAgIGlmICghZGVzdGluYXRpb24pXG4gICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICBlbGVtLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgdGhpcy5fb25MaW5rQ2xpY2tlZC5iaW5kKHRoaXMpKTtcblxuICAgICAgdGhpcy5lbGVtZW50cy5wdXNoKHtcbiAgICAgICAgaWQsXG4gICAgICAgIGxpbms6IGVsZW0sXG4gICAgICAgIGRlc3RpbmF0aW9uXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgX29uTGlua0NsaWNrZWQoZXZlbnQ6IEV2ZW50KTogIHZvaWQge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cbiAgICBsZXQgdGFyZ2V0ID0gZXZlbnQuY3VycmVudFRhcmdldDtcbiAgICBsZXQgaWQgPSBTY3JvbGxTcHlEaXJlY3RpdmUuZ2V0SWQodGFyZ2V0KTtcbiAgICBsZXQgZGVzdGluYXRpb24gPSB0aGlzLl9nZXRQZWVyRWxlbWVudChpZCk7XG4gICAgdGhpcy5kaXJlY3ROYXZpZ2F0aW9uID0gdHJ1ZTtcblxuICAgIGxldCBwb3NpdGlvbiA9IGV4dHJhY3RFbGVtZW50UG9zaXRpb24odGhpcy5kb2N1bWVudCwgZGVzdGluYXRpb24pO1xuXG4gICAgd2luZG93LnNjcm9sbFRvKHt0b3A6IHBvc2l0aW9uLnRvcCAtIDI1LCBsZWZ0OiAwLCBiZWhhdmlvcjogJ3Ntb290aCd9KTtcblxuICAgIHRoaXMuX2NsZWFuQ3VycmVudExpbmsoKTtcbiAgICB0aGlzLl9zZXRDdXJyZW50TGluayh0YXJnZXQpO1xuICAgIHRoaXMuZGlyZWN0TmF2aWdhdGlvbiA9IGZhbHNlO1xuICB9XG5cbiAgcHJpdmF0ZSBfZ2V0UGVlckVsZW1lbnQoaWQpOiBIVE1MRWxlbWVudCB8IG51bGwge1xuXG4gICAgbGV0IGRlc3RpbmF0aW9uID0gdGhpcy5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG5cbiAgICBpZiAoIWRlc3RpbmF0aW9uKVxuICAgICAgcmV0dXJuIG51bGw7XG5cbiAgICByZXR1cm4gZGVzdGluYXRpb247XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBnZXRJZChlbGVtKTogc3RyaW5nIHtcbiAgICBsZXQgaHJlZiA9IGVsZW0uZ2V0QXR0cmlidXRlKCdocmVmJyk7XG5cbiAgICBpZiAoIWhyZWYpXG4gICAgICByZXR1cm4gbnVsbDtcblxuICAgIHJldHVybiBocmVmLnJlcGxhY2UoJyMnLCAnJyk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKFwid2luZG93OnNjcm9sbFwiLCBbJyRldmVudCddKVxuICBvbldpbmRvd1Njcm9sbChldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5kaXJlY3ROYXZpZ2F0aW9uKVxuICAgICAgcmV0dXJuO1xuXG4gICAgZm9yIChsZXQgZWxlbSBvZiB0aGlzLmVsZW1lbnRzKSB7XG4gICAgICBsZXQgdG9wID0gZWxlbS5kZXN0aW5hdGlvbi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3A7XG4gICAgICBpZiAodG9wID4gMCAmJiB0b3AgPCAyNSkge1xuICAgICAgICB0aGlzLl9jbGVhbkN1cnJlbnRMaW5rKCk7XG4gICAgICAgIHRoaXMuX3NldEN1cnJlbnRMaW5rKGVsZW0ubGluayk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgX2NsZWFuQ3VycmVudExpbmsoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmN1cnJlbnRBY3RpdmVMaW5rKVxuICAgICAgcmV0dXJuO1xuXG4gICAgdGhpcy5yZW5kZXJlci5yZW1vdmVDbGFzcyh0aGlzLmN1cnJlbnRBY3RpdmVMaW5rLCAnYWN0aXZlJyk7XG4gIH1cblxuICBwcml2YXRlIF9zZXRDdXJyZW50TGluayhlbGVtKTogdm9pZCB7XG4gICAgdGhpcy5jdXJyZW50QWN0aXZlTGluayA9IGVsZW07XG4gICAgdGhpcy5yZW5kZXJlci5hZGRDbGFzcyh0aGlzLmN1cnJlbnRBY3RpdmVMaW5rLCAnYWN0aXZlJyk7XG4gIH1cbn1cbiJdfQ==