Mujahid
Mujahid

Reputation: 1237

Scrollspy effect in Angular router

I'm trying to add the Scrollspy effect to the Angular project. When a user click a link, rather than show it as a new page, that component should scroll up and the next component should come to that place. Is there a way to do it in Angular with routing? When a link get clicked, the page should animate to scrolltop as with the following jquery code

$('html, body').animate({scrollTop: offsetTop}, 'normal');

Is there any libraries or features in Angular?

Upvotes: 3

Views: 3246

Answers (2)

ibenjelloun
ibenjelloun

Reputation: 7733

I was looking for something very simple to handle scrolling, I ended up making my own directive :

@Directive({
  selector: '[scrollSpy]'
})
export class ScrollSpyDirective {
  @Input() public spiedTags = [];
  @Output() public sectionChange = new EventEmitter<string>();
  private currentSection: string;

  constructor(private _el: ElementRef) {}

  @HostListener('scroll', ['$event'])
  onScroll(event: any) {
    let currentSection: string;
    const children = this._el.nativeElement.children;
    const scrollTop = event.target.scrollTop;
    const parentOffset = event.target.offsetTop;
    for (let i = 0; i < children.length; i++) {
      const element = children[i];
      if (this.spiedTags.some(spiedTag => spiedTag === element.tagName)) {
        if (element.offsetTop - parentOffset <= scrollTop) {
          currentSection = element.id;
        }
      }
    }
    if (currentSection !== this.currentSection) {
      this.currentSection = currentSection;
      this.sectionChange.emit(this.currentSection);
    }
  }
}

Here is a demo link with routing integrated. For some reason, the stackblitz edition version breaks the stackblitz editor scrolling.

Upvotes: 4

Carlos Silva
Carlos Silva

Reputation: 391

Here is a working version for Angular 9:

import { Directive, Input, EventEmitter, Output, ElementRef, HostListener } from '@angular/core';

@Directive({
    selector: '[scrollSpy]'
})

export class ScrollSpyDirective {
    @Input() public spiedTags = [];
    @Output() public sectionChange = new EventEmitter<string>();
    private currentSection: string;

    constructor(private _el: ElementRef) {}

    @HostListener('window:scroll', ['$event'])
    onScroll(event: any) {
        let currentSection: string;
        const children = this._el.nativeElement.children;
        const scrollTop = event.target.scrollingElement.scrollTop;
        const parentOffset = event.target.scrollingElement.offsetTop;
        for (let i = 0; i < children.length; i++) {
            const element = children[i];
            if (this.spiedTags.some(spiedTag => spiedTag === element.tagName)) {
                if ((element.offsetTop - parentOffset) <= scrollTop) {
                    currentSection = element.id;
                }
            }
        }

        if (currentSection !== this.currentSection) {
            this.currentSection = currentSection;
            this.sectionChange.emit(this.currentSection);
        }
    }
}

Upvotes: 1

Related Questions