ng-scroll-spy
Version:
Angular directive that tracking the scrolling of the document and highlight the navigation link
95 lines • 11.4 kB
JavaScript
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==