Using Angular to automatically scroll to a section of the page when navigating to a targeted URL fragment

I'm currently working on coding an Angular project and I have a function that smoothly scrolls the page to a specific section. This function works flawlessly when triggered manually by me. However, my goal is to have the page automatically scroll to a designated section whenever a user navigates directly to a URL with a fragment included, such as http://localhost:4200/section-2.

What steps can I take to adjust my existing code or incorporate additional logic to ensure that when a page loads with a specified fragment in the URL, it automatically scrolls to that particular section?

Below is the current implementation of my scrolling function:

public scrollToSection(sectionId: string): void {
  const targetElement = document.getElementById(sectionId);
  if (targetElement) {
    const navHeight = this.navigationService.getNavHeight();
    const yPosition = targetElement.getBoundingClientRect().top + window.scrollY - navHeight;
    window.scrollTo({ top: yPosition, behavior: 'smooth' });
  }
}

Answer №1

  1. It is recommended to create a wrapping component for all sections. Within this component, extract the initial route path and navigate to the corresponding section.

home.component.ts

import { Component } from '@angular/core';
import { Section1Component } from '../section1/section1.component';
import { Section2Component } from '../section2/section2.component';
import { Section3Component } from '../section3/section3.component';
import { Section4Component } from '../section4/section4.component';
import { CommonModule } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { NavigationService } from '../navigation.service';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [
    Section1Component,
    Section2Component,
    Section3Component,
    Section4Component,
    CommonModule,
  ],
  template: `
    <app-section1></app-section1>
    <app-section2></app-section2>
    <app-section3></app-section3>
    <app-section4></app-section4>
  `,
})
export class HomeComponent {
  name = 'Angular';

  constructor(
    private activatedRoute: ActivatedRoute,
    private navigationService: NavigationService
  ) {}

  ngOnInit(): void {
    this.activatedRoute.pathFromRoot &&
      this.activatedRoute.pathFromRoot.length > 0 &&
      this.activatedRoute.pathFromRoot[1].url.subscribe((url) => {
        let elementId = url[0].path;

        this.scrollToSection(elementId);
      });
  }

  public scrollToSection(sectionId: string): void {
    const targetElement = document.getElementById(sectionId);
    if (targetElement) {
      const navHeight = this.navigationService.getNavHeight();
      const yPosition =
        targetElement.getBoundingClientRect().top + window.scrollY - navHeight;
      window.scrollTo({ top: yPosition, behavior: 'smooth' });
    }
  }
}
  1. In your routing configuration, ensure that all section URLs are directed to the HomeComponent.

app.route.ts

import { HomeComponent } from './home/home.component';

export const routes: Routes = [
  { path: 'section-1', component: HomeComponent },
  { path: 'section-2', component: HomeComponent },
  { path: 'section-3', component: HomeComponent },
  { path: 'section-4', component: HomeComponent },
  { path: '**', redirectTo: '' },
];
  1. Include the routes in the provideRouter function.

main.ts

import { routes } from './app/app.routes';

bootstrapApplication(App, {
  providers: [provideAnimations(), provideRouter(routes)],
}).catch((err) => console.error(err));
  1. Make sure to use the routes and render the appropriate component. Add the <router-outlet> in the template within main.ts. Remove the imports of the section components from the App.

main.ts

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    NavComponent,
    CommonModule,
    RouterModule,
  ],
  template: `
  <div class="container">
     <app-nav></app-nav> 
     <router-outlet></router-outlet>
  </div>
  `,
})

View Demo on StackBlitz

Answer №2

Remember: Clarity in your questions can lead to more helpful responses from others.

It seems like you're looking for ways to implement certain methods in Angular. Although I haven't personally tested these codes, they should work as intended.

Utilizing Angular:

If you designate the "section-2" part as a parameter labeled ":elementid" when setting up routes;

Option 1: (Static value that may pose challenges with tracking changes)

let elementId = this.activatedRoute.snapshot.params['elementid'];
this.scrollToSection(elementId);

Option 2: (Allows for better tracking of changes)

this.activatedRoute.params.pipe(
 map(params => params.elementid),
 switchMap(elementId=> this.scrollToSection(elementId))
);

Using Pure JS within Angular:

(There are alternative approaches available.)

let id = window.location.pathname.substring(1);
this.scrollToSection(id);

(In most cases, it's recommended to stick to Angular-specific methods rather than resorting to pure JavaScript within an Angular project.)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Refreshing list after cancelling search not working in Ionic 3

Is there a more efficient way for me to phrase this? I've integrated a search bar in Ionic and it appears to be functioning correctly. However, when I click the cancel icon on the search bar, the lists/contents do not revert back. This is my.html &l ...

"Encountered a 'NextAuth expression cannot be called' error

Recently, I delved into learning about authentication in Next.js using next-auth. Following the documentation diligently, I ended up with my app/api/auth/[...nextauth]/route.ts code snippet below: import NextAuth, { type NextAuthOptions } from "next-a ...

How can I dynamically update the sidebar in Ionic 3 post-login without the need to reload the app or refresh the browser?

I have successfully implemented login and sign up functionality in my ionic 3 app. However, I am facing an issue where the username is not updating in the sidebar instantly after logging in. Currently, I need to refresh the browser or close and reopen the ...

What is the best way to set the minDate and maxDate of the NgbDatePicker in the main component so that the settings can be applied

Within my Angular 4 project, I have integrated Ng-bootstrap (v1.1.0) which includes multiple date pickers across different modules. I am looking to enforce a global maxDate configuration for all these instances. Below is an overview of my folder structure: ...

A function that creates a new object with identical keys as the original input object

I am working on creating a function fn() that has the following specifications: It takes a single argument x which is an object with optional keys "a" and "b" (each field may be numeric for simplicity) The function should return a new object with the same ...

Error message: "Supabase connection is returning an undefined value

I am encountering an issue with my Vercel deployed Remix project that utilizes Supabase on the backend, Postgresql, and Prisma as the ORM. Despite setting up connection pooling and a direct connection to Supabase, I keep receiving the following error whene ...

What is the best way to prevent ticks from extending beyond the left side of the chart?

I am currently using Chart.js to create a multi-line chart. However, I am facing an issue where the first tick extends beyond the left side of the chart, causing the entire chart to shift over. I would like to find a solution where the first tick does not ...

What is the best approach to testing the React Hook "useEffect" that is used to make an API call with Typescript?

Currently, I am working on writing Jest-enzyme tests for a basic React application using Typescript along with the new React hooks. The main issue I am facing is with properly simulating the api call made within the useEffect hook. Within the useEffect, ...

Token and authentication in Angular 2

I am currently working on creating authentication in Angular 2. To do this, I have set up a post service: constructor(http: Http) { this.http = http; let currentUser: IUserTokenType = JSON.parse(localStorage.getItem("currentUser")); this.token ...

Can type inference be utilized with the `forwardRef` method?

I am having an issue with the Type inference not working with the forwardRef in my component. I understand the reason behind this limitation, but I'm wondering if there is a workaround? https://i.sstatic.net/ZO3gI.png This is what I meant by type in ...

A comprehensive guide on constructing a literal object in Typescript by combining an array with an object

Recently, I came across this Typescript code snippet: type SortedList = T[] & {_brand: "sorted" }; function binarySearch<T>(xs: SortedList<T>, x: T): boolean let low = 0; let high = xs.length - 1; while (high ...

Having trouble utilizing the ng-Command in Angular?

Currently, I am attempting to set up Angular in a vagrant-box environment. npm install -g @angular/cli Unfortunately, I encounter an error while trying to use the client: The program 'ng' is currently not installed. You can install it by typin ...

Tips on showing validation error message through a tooltip when hovering over the error icon in Ionic

Currently, I have implemented bootstrap validation within my Ionic application and it is functioning correctly. The error icon appears within the textbox along with the error message below the textbox. However, I am interested in changing this setup so t ...

TS2347: Type arguments cannot be used with untyped function calls

In my bar.ts file, I have the following declarations: declare var angular:any; declare var _:any; declare var $:any; declare var moment:any; declare var ng:any; declare var require:any; And in my bootstrap.ts file, I reference the necessary typings: /// ...

Adding mat-icon dynamically to divs using Angular's renderer2

I'm currently working on a project that involves a lot of HTML divs. I've already created some static divs using renderer2. static HTML (not dynamically generated) example of desired result using renderer2 <div class="time-rowss clearf ...

Turning an array of strings into a multidimensional array

I have a JavaScript string array that I need to convert into a multidimensional array: const names = [ "local://john/doe/blog", "local://jane/smith/portfolio", "as://alexander/wong/resume" ]; The desired output sh ...

Is it possible in Angular Typescript to map the attributes of an array to a class object or generate a new object using the elements of the array?

Here are the specifications of the tools I am currently using: Angular CLI: 10.0.6 Node: 12.18.2 Operating System: win32 x6 Angular Version: 10.0.10 My goal is to retrieve selected rows from ag-grid using a specific method. When I retrieve the row, i ...

Is the ng bootstrap modal within Angular failing to show up on the screen

In the midst of working on my project, I encountered an issue with opening a modal using ng bootstrap. Although I found a similar thread discussing this problem, it did not include bootstrap css. I decided to reference this example in hopes of resolving t ...

Having difficulty employing jest.mock with a TypeScript class

Following the guidelines outlined in the ES6 Class Mocks page of the Jest documentation, I attempted to test a method on a TypeScript class called Consumer. The Consumer class instantiates a Provider object and invokes methods on it, prompting me to mock t ...

Conditioning types for uninitialized objects

Is there a way to create a conditional type that can determine if an object is empty? For instance: function test<T>(a: T): T extends {} ? string : never { return null } let o1: {} let o2: { fox? } let o3: { fox } test(o1) ...