angulartypescriptscroll

Angular - How to position a fixed navbar correctly when scrolling to sections with the same ID


I'm working on an Angular project where I have multiple sections on a page, each with a unique ID. I also have a fixed-position navbar that allows users to navigate to these sections. My goal is to ensure that when a user clicks on a menu item (e.g., Section2), the navbar aligns correctly as shown in the image below.

Nav position by clicking Section2

To illustrate the issue, I've created a StackBlitz demo. However, I had to comment out the following line <!-- <app-nav></app-nav> --> in the StackBlitz demo to make it compile:

Stackblitz Demo

I couldn't fix the compilation error, but that's not my main concern. My primary issue is how to correctly position the navbar so that it behaves as expected when scrolling to different sections.

app.routes.ts

export const routes: Routes = [
  { path: 'section-1', component: Section1Component },
  { path: 'section-2', component: Section2Component },
  { path: 'section-3', component: Section3Component },
  { path: 'section-4', component: Section4Component },
  { path: '**', redirectTo: '' },
];

main.ts

<div class="container">
  <!-- <app-nav></app-nav> -->
  <app-section1></app-section1>
  <app-section2></app-section2>
  <app-section3></app-section3>
  <app-section4></app-section4>
</div>

nav.component.ts

export class NavComponent {
  constructor(private viewportScroller: ViewportScroller) {}

  public scrollToSection(sectionId: string): void {
    this.viewportScroller.scrollToAnchor(sectionId);
  }
}

Solution

  • As mentioned in the comment, you should scroll by calculating the position to place the selected section after the navbar instead of scrolling to the element by ID.

    Note that, this approach has a limitation when the scroll reaches the end of the page, the section is unable to pin after the navbar such as <app-section-3> and <app-section-4> (You can see the StackBlitz link and open the Preview in the new tab for full screen).

    public scrollToSection(sectionId: string): void {
      const element = document.getElementById(sectionId)!;
      const navElement = document.getElementsByTagName('nav')[0]!;
    
      const navHeight = navElement.offsetHeight;
    
      const targetPosition = element.getBoundingClientRect().top + window.scrollY;
    
      this.viewportScroller.scrollToPosition([0, targetPosition - navHeight]);    
    }
    

    Alternatively, you may look for scrollTo which has the same functionality and includes the scroll animation.

    window.scrollTo({
     left: 0,
     top: targetPosition - navHeight,
     behavior: 'smooth'
    });
    

    Demo @ StackBlitz