Scroll to the top on every Angular 5 route change

Currently, I am utilizing Angular 5 for my project. Within the dashboard interface, there are various sections with varying amounts of content. Some sections contain only a small amount of information, while others have large amounts of content. However, when I navigate to the top of the page by changing the router, I encounter an issue where I have to scroll up each time.

Is there a solution available to ensure that my view remains at the top whenever I switch routers?

Answer №1

Make sure to explore all the options available to you :)


Method 1:

When a new component is instantiated in the router outlet, the activate event is emitted, allowing us to use (activate) for scrolling purposes like so:

app.component.html

<router-outlet (activate)="onActivate($event)"></router-outlet>

app.component.ts

onActivate(event) {
   // window.scroll(0,0);

   window.scroll({ 
           top: 0, 
           left: 0, 
           behavior: 'smooth' 
    });

    //or document.body.scrollTop = 0;
    //or document.querySelector('body').scrollTo(0,0)
    ...
}

If smooth scroll doesn't work well in Safari, you can try using this solution:

onActivate(event) {
    let scrollToTop = window.setInterval(() => {
        let pos = window.pageYOffset;
        if (pos > 0) {
            window.scrollTo(0, pos - 20); // how far to scroll on each step
        } else {
            window.clearInterval(scrollToTop);
        }
    }, 16);
}

If you only want certain components to trigger the scrolling, you can check for it in an if statement like this:

onActivate(e) {
    if (e.constructor.name === "login") { // for example
            window.scroll(0,0);
    }
}

Method 2:

Starting from Angular 6.1, you can also utilize

{ scrollPositionRestoration: 'enabled' }
on eagerly loaded modules to apply it to all routes:

RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })

This method already includes smooth scrolling but will be applied to every routing which may not be suitable in all cases.


Method 3:

Another approach is to implement top scrolling during router animations. Include this in every transition where top scroll is desired:

query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 

Answer №2

If you encounter this issue in Angular 6, you can resolve it by including the parameter

scrollPositionRestoration: 'enabled'
in app-routing.module.ts under RouterModule:

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})

Answer №3

UPDATE: To address the issue in Angular 6+, I recommend following Nimesh Nishara Indimagedara's solution which involves adding the following code to your RouterModule configuration:

RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
});

Initial Response:

If you're still facing difficulties, another approach is to insert an empty HTML element (such as a div) at the top or desired scroll location with the id "top" within your template or parent template:

<div id="top"></div>

Then, in your component file:

ngAfterViewInit() {
    // Workaround: Scrolls to the top of the page after view initialization
    let top = document.getElementById('top');
    if (top !== null) {
      top.scrollIntoView();
      top = null;
    }
}

Answer №4

In Angular 6.1, a new feature called scrollPositionRestoration has been introduced to handle scroll position restoration automatically.

To learn more about this, you can check out my detailed explanation on the topic of scrolling to the top when routes change in Angular 2.

Answer №5

Upgrading to Angular Version 6+ Eliminates the Need for window.scroll(0,0)

Starting from Angular version 6+, as mentioned in the official documentation, there are new options available for configuring the router.

interface ExtraOptions {
  enableTracing?: boolean
  useHash?: boolean
  initialNavigation?: InitialNavigation
  errorHandler?: ErrorHandler
  preloadingStrategy?: any
  onSameUrlNavigation?: 'reload' | 'ignore'
  scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'
  anchorScrolling?: 'disabled' | 'enabled'
  scrollOffset?: [number, number] | (() => [number, number])
  paramsInheritanceStrategy?: 'emptyOnly' | 'always'
  malformedUriErrorHandler?: (error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree
  urlUpdateStrategy?: 'deferred' | 'eager'
  relativeLinkResolution?: 'legacy' | 'corrected'
}

The usage of

scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'
is illustrated below:

Example:

RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'|'top' 
});

If manual control over scrolling is necessary, avoid using window.scroll(0,0). Instead, Angular V6 has introduced the ViewPortScoller as a common package.

abstract class ViewportScroller {
  static ngInjectableDef: defineInjectable({ providedIn: 'root', factory: () => new BrowserViewportScroller(inject(DOCUMENT), window) })
  abstract setOffset(offset: [number, number] | (() => [number, number])): void
  abstract getScrollPosition(): [number, number]
  abstract scrollToPosition(position: [number, number]): void
  abstract scrollToAnchor(anchor: string): void
  abstract setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void
}

The implementation is straightforward as shown below:

import { Router } from '@angular/router';
import {  ViewportScroller } from '@angular/common'; //import
export class RouteService {

  private applicationInitialRoutes: Routes;
  constructor(
    private router: Router;
    private viewPortScroller: ViewportScroller//inject
  )
  {
   this.router.events.pipe(
            filter(event => event instanceof NavigationEnd))
            .subscribe(() => this.viewPortScroller.scrollToPosition([0, 0]));
}

Answer №6

While @Vega provides a direct answer to your question, there are some potential issues that arise. One such issue is the disruption of the browser's back/forward button functionality. Should a user click on either of these buttons, they may lose their current position on the page and find themselves scrolled all the way back to the top. This can be frustrating for users who have scrolled far down the page to reach a specific link, only to have their scroll position reset when navigating backward.

In response to this problem, I offer a solution below:

export class AppComponent implements OnInit {
  isPopState = false;

  constructor(private router: Router, private locStrat: LocationStrategy) { }

  ngOnInit(): void {
    this.locStrat.onPopState(() => {
      this.isPopState = true;
    });

    this.router.events.subscribe(event => {
      // Scroll to top if accessing a page, not via browser history stack
      if (event instanceof NavigationEnd && !this.isPopState) {
        window.scrollTo(0, 0);
        this.isPopState = false;
      }

      // Ensures that isPopState is reset
      if (event instanceof NavigationEnd) {
        this.isPopState = false;
      }
    });
  }
}

Answer №7

If you are using mat-sidenav, make sure to assign an id to the router outlet (especially if you have parent and child router outlets) and include the activate function within it:

<router-outlet id="main-content" (activate)="onActivate($event)">
To scroll to the top, utilize the 'mat-sidenav-content' query selector like so:
onActivate(event) {
    document.querySelector("mat-sidenav-content").scrollTo(0, 0);
  }

Answer №8

To implement this feature, simply include the following line in your app.module.ts file :

RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled' //scroll to the top
})

This solution has been tested and proven effective with Angular 11.1.4

Answer №9

For my situation, I simply included

window.scrollTop(0, 0);

inside the ngOnInit() function and it is functioning perfectly.

Answer №10

For Angular versions 6.1 and above:

In newer versions of Angular such as Angular 6.1+, there is a built-in solution that can be used with the option

scrollPositionRestoration: 'enabled'
to achieve the same functionality.

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})

For Angular versions 6.0 and prior:

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { Location, PopStateEvent } from "@angular/common";

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {

    private lastPoppedUrl: string;
    private yScrollStack: number[] = [];

    constructor(private router: Router, private location: Location) { }

    ngOnInit() {
        this.location.subscribe((ev:PopStateEvent) => {
            this.lastPoppedUrl = ev.url;
        });
        this.router.events.subscribe((ev:any) => {
            if (ev instanceof NavigationStart) {
                if (ev.url != this.lastPoppedUrl)
                    this.yScrollStack.push(window.scrollY);
            } else if (ev instanceof NavigationEnd) {
                if (ev.url == this.lastPoppedUrl) {
                    this.lastPoppedUrl = undefined;
                    window.scrollTo(0, this.yScrollStack.pop());
                } else
                    window.scrollTo(0, 0);
            }
        });
    }
}

Note: The expected behavior is for the page to remain scrolled down to the same position when navigating back, but scroll to the top when arriving at a new page.

Answer №11

None of the previous solutions worked for me, so I took a different approach. I inserted an element reference to the top element in app.component.html, and added (activate)=onNavigate($event) to the router-outlet.

<!--app.component.html-->
<div #topScrollAnchor></div>
<app-navbar></app-navbar>
<router-outlet (activate)="onNavigate($event)"></router-outlet>

Next, I defined the child in app.component.ts as type ElementRef, and implemented code that scrolls to it upon activation of the router-outlet.

export class AppComponent  {
  @ViewChild('topScrollAnchor') topScroll: ElementRef;

  onNavigate(event): any {
    this.topScroll.nativeElement.scrollIntoView({ behavior: 'smooth' });
  }
}

For the full implementation, you can view the code on stackblitz

Answer №12

If you need to implement a scroll function, simply add this code snippet and call it whenever necessary:

scrollTop(){

  window.scroll(0,0);
}

Answer №13

Simply Include

 ngAfterViewInit() {
  window.scroll(0,0)
 }

Answer №14

As I continue my search for a pre-existing solution to this issue similar to what AngularJS offers, I have found that the following workaround gets the job done for now. It's straightforward and maintains the functionality of the back button.

app.component.html

<router-outlet (deactivate)="onDeactivate()"></router-outlet>

app.component.ts

onDeactivate() {
  document.body.scrollTop = 0;
  // Alternatively, you can scroll to top by using this other call:
  // window.scrollTo(0, 0)
}

Response courtesy of zurfyx original post

Answer №15

All you have to do is develop a custom function that adjusts the scrolling behavior of your screen.

For instance:

window.scroll(0,0) OR window.scrollTo() by providing the correct parameters.

Expected parameter for window.scrollTo(xpos, ypos).

Answer №16

I found a solution that worked perfectly for my situation:

document.querySelector('.container')[0].scrollTo(0, 0);

This code snippet has successfully scrolled to the top in various versions of angular such as 8, 9, and 10.

Answer №17

simply include

window.scrollTo({ top: 0 });

in the ngOnInit() method

Answer №18

If you want a solution that only scrolls to the top of each Component when it is visited for the first time, you can implement the following:

Within each Component:

export class MyComponent implements OnInit {

firstVisit: boolean = true;

...

ngOnInit() {

  if(this.firstVisit) {
    window.scroll(0,0);
    this.firstVisit = false;
  }
  ...
}

Answer №19

Give this a shot:

main.component.ts

import {Component, OnInit, OnDestroy} from '@angular/core';
import {Router, NavigationEnd} from '@angular/router';
import {filter} from 'rxjs/operators';
import {Subscription} from 'rxjs';

@Component({
    selector: 'main-app',
    templateUrl: './main.component.html',
    styleUrls: ['./main.component.scss'],
})
export class MainComponent implements OnInit, OnDestroy {
    sub: Subscription;

    constructor(private routerService: Router) {
    }

    ngOnInit() {
        this.sub = this.routerService.events.pipe(
            filter(event => event instanceof NavigationEnd)
        ).subscribe(() => window.scrollTo(0, 0));
    }

    ngOnDestroy() {
        this.sub.unsubscribe();
    }
}

Answer №20

export class AppComponent {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
      if (val instanceof NavigationEnd) {
        window.scrollTo(0, 0);
      }
    });
  }

}

Answer №21

<p class="text-center" (click)="scrollToTop()"> Return to the beginning
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-up-short" viewBox="0 0 16 16">
  <path fill-rule="evenodd" d="M8 12a.5.5 0 0 0 .5-.5V5.707l2.146 2.147a.5.5 0 0 0 .708-.708l-3-3a.5.5 0 0 0-.708 0l-3 3a.5.5 0 1 0 .708.708L7.5 5.707V11.5a.5.5 0 0 0 .5.5z"/>
</svg>
</p>


scrollToTop(): void {
window.scroll(0, 0);
}

Answer №22

@ViewChild('scrollTop') scrollTop: ElementRef;

ngAfterViewInit()
{
      interval(1000).pipe(
      switchMap(() => of(this.scrollTop)),
      filter(response => response instanceof ElementRef),
      take(1))
      .subscribe((element: ElementRef) => {
           element.nativeElement.scrollTop = 0;
       });
 }

This method worked perfectly for me. It's the angular-recommended approach.

Answer №23

Enhancement: Instead of triggering an action in the template and scrolling on NavigationEnd, subscribe to all routing events to ensure smooth scroll only on successful navigation. This method prevents unnecessary firing of actions on bad navigations or blocked routes.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {

  router$: Subscription;

  constructor(private router: Router) {}

  ngOnInit() {
    this.router$ = this.router.events.subscribe(next => this.onRouteUpdated(next));
  }

  ngOnDestroy() {
    if (this.router$ != null) {
      this.router$.unsubscribe();
    }
  }

  private onRouteUpdated(event: any): void {
    if (event instanceof NavigationEnd) {
      this.smoothScrollTop();
    }
  }

  private smoothScrollTop(): void {
    const scrollToTop = window.setInterval(() => {
      const pos: number = window.pageYOffset;
      if (pos > 0) {
          window.scrollTo(0, pos - 20); // define scroll distance per step
      } else {
          window.clearInterval(scrollToTop);
      }
    }, 16);
  }

}

HTML

<router-outlet></router-outlet>

Answer №24

give this a shot

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'top'
  })],
  exports: [RouterModule]
})

this snippet is compatible with Angular versions 6 and above

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

Develop a type definition utilizing dotted paths from a recursive object model

Working with TypeScript, I am dealing with a nested object structure of functions defined as: type CallbackFn = (args: any) => any type CallbackObj = { [key: string]: CallbackFn | CallbackObj } const callbacks = { foo: function(args: { x: num }): st ...

Is it possible to incorporate several modules into nodeJS simultaneously?

I'm a beginner when it comes to NodeJS. I was wondering if it's possible for me to call 2 different JavaScript files using NodeJS and ExpressJS. The idea is to split the work, where I can focus on one file while my partner works on another. So, I ...

Is there a way to make my for loop search for the specific element id that I have clicked on?

When trying to display specific information from just one array, I'm facing an issue where the for loop is also iterating through other arrays and displaying their names. Is there a way to only show data from the intended array? const link = document. ...

Seeking assistance with managing Yarn workspaces

My current project involves working on a React Native application for a client. After I had already written a significant amount of code, my client requested a new feature to be added. This feature should have been simple to implement, but due to the compl ...

Unable to determine the data type of the JSON object during the

I'm having trouble reading an Object type of json... Here is the json I'm working with: body: { "111": { "name": "name1", "status": 10000 }, "222": { "name": "name2", "status": 20000 }, "333": ...

What is the process for sending Raw Commands to a Receipt Printer using Node.JS?

My Current Project I am currently working on integrating a receipt printer that supports ESC/P raw printing into an app for remote printing of receipts. The Approach I Am Taking To achieve this, I am utilizing the PrintNodes API to send data from my app ...

Angular and Express are not communicating properly when attempting to make a GET request

My Angular application is supposed to make an HTTP-Get Request, and the Express server (which also hosts the Angular app) should send a JSON object to the Angular app. However, for some reason, it's not working and I'm unsure why. The first conso ...

Retrieving attributes from a reactive object in TypeScript

I have a question regarding accessing values in Typescript. Whenever I load my website, I make a call to a service that fetches user data. private currentUserSource = new ReplaySubject<IUser>(1); currentUser$ = this.currentUserSource.asObservable ...

Unable to access a text file while executing a javascript file as a systemd service

Running a javascript file using node on a Raspberry Pi at startup automatically through a systemd service has been giving me some trouble. I followed a guide similar to this one to set it up. When I manually run the javascript file by entering the comman ...

What could be the reason behind the malfunctioning of $.getjson?

I've been facing issues trying to access remote json data. Initially, I resorted to using as a temporary fix but it's no longer serving the purpose for me. As of now, I am back at square one trying to resolve why I am unable to retrieve the remo ...

Retrieve a data value from a JSON object by checking if the ID exists within a nested object

I am facing the challenge of navigating through a JSON object where child objects contain IDs that need to be matched with parent properties. The JSON structure looks like this: { id: 1, map: "test", parameter_definitions: [{ID: 1, pa ...

When trying to use the `map: texture` with a new `THREE.Texture(canvas)

i'm trying to apply the texture from new THREE.Texture(canvas) to the PointsMaterial, but it's not working as expected. The canvas appears on the top right corner, however, only white points are visible in the CubeGeometry. var texture = new ...

Following the recent update to IntelliJ IDEA 2022.1.3, the use of ::ng-deep has been marked

After updating the version of IntelliJ, I noticed that the ::ng-deep angular material selector is now marked as deprecated. Here's an example: <mat-form-field class="register-custom-select"> <mat-select formControlName="gende ...

What is the best way to implement the settimeout method in react?

I need assistance on how to effectively utilize the setTimeout() method in my code. Specifically, I am looking to trigger a click event on an element after a certain delay and execute a function afterwards. Here is the current implementation of my code: ...

Troubleshooting the issue of AngularJs location.path not successfully transferring parameters

My page has a Login section. Login.html <ion-view view-title="Login" name="login-view"> <ion-content class="padding"> <div class="list list-inset"> <label class="item item-input"> <input type="te ...

Convert an enum value to a String representation

I need assistance with converting my enum value to a string format export enum Roles { manager = "manager", user = "user" } console.log(" Roles.manager: ", Roles[Roles.manager]); The following is displayed in the console: Roles.manager: manage ...

Display a button in a single row table

Here is my table markup: <table class="table table-condensed"> <thead> <tr> <th>id</th> <th>task</th> <th>date</th> </tr> </thead> ...

Can anyone explain how to successfully implement AJAX data in a PHP file?

I've been struggling to transfer data from my JavaScript file to a PHP variable. I've spent hours searching for solutions, but none seem to apply to my unique code. Here's the JavaScript code I'm working with... $(function(){ ...

Facing Hurdles in Starting my Debut Titanium Mobile Project

I recently started using Titanium Studio (version 2.1.0GA on WindowsXP) and I have added the Android SDK to it. However, I am encountering an error when trying to run my first mobile project. The console is displaying the following message: Can anyone off ...

Can a button be incorporated into an accordion feature using DevExtreme Angular?

I am using devextreme-angular and I would like to include two buttons such as DELETE and EDIT to accordion. The accordions receive the posts I typed so i have title and content for each accordion. What I need now is to add these buttons to each of the cont ...