178

I am using Angular 5. I have a dashboard where I have few sections with small content and few sections with so large content that I am facing a problem when changing router while going to top. Every time I need to scroll to go to top.

How can I solve this issue so that when I change the router, my view always stay at the top?

Rafael Tavares
  • 5,678
  • 4
  • 32
  • 48
raihan
  • 2,225
  • 4
  • 14
  • 19
  • 1
    Possible duplicate of [Angular 2 Scroll to top on Route Change](https://stackoverflow.com/questions/39601026/angular-2-scroll-to-top-on-route-change) – Cerbrus May 29 '19 at 09:43

23 Answers23

331

There are some solutions, make sure to check them all :)


Option1:

The router outlet will emit the activate event any time a new component is being instantiated, so we could use (activate) to scroll (for example) to the top:

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)
    ...
}

As the smooth scroll is not implemented well in Safari, use, for exemple, this solution for a smooth scroll:

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 wish to be selective, say not every component should trigger the scrolling, you can check it in an if statement like this:

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

Option2:

Since Angular6.1, we can also use { scrollPositionRestoration: 'enabled' } on eagerly loaded modules and it will be applied to all routes:

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

It will also do the smooth scrolling, already. However this has the inconvenient for doing it on every routing.


Option3:

An other solution is to do the top scrolling on router animation. Add this in every transition where you want to scroll to the top:

query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 
Vega
  • 27,856
  • 27
  • 95
  • 103
  • 2
    scroll events on `window` object are not working in angular 5. Any guesses why? – Sahil Babbar May 21 '18 at 04:54
  • @SahilBabbar, Check the body css, overflow:hiden ? what is its height? – Vega May 21 '18 at 07:53
  • @Vega no. The height of the body is default and nothing is hard coded inside, as it is a normal Angular 5 application. Moreover take a look at Angular docs where they say that `scroll` events are blacklisted by ngzones. – Sahil Babbar May 21 '18 at 09:24
  • Try document.body.scrollTop = 0; or with old js document.querySelector('body').scrollTo(0,0), etc.. If those don't work provide an MCVE – Vega May 21 '18 at 11:56
  • Thanks for the answer, solved my problem. Also I placed the code in my ngOnInit() method to make it work for Tabs navigation. – rzv Dec 15 '18 at 17:29
  • This is what I was looking for `RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })`. Thanks – Manwal Mar 16 '19 at 17:21
  • 2
    Is there any way for lazyloaded module? – Pankaj Prakash Jun 02 '19 at 17:12
  • @PankajPrakash, I think other that configuring via the router solutions should work. Have you tried the via animation one? – Vega Oct 17 '20 at 13:27
  • Thanks! This solved my problem of scrolling to top after some action I had on my page, not exactly related to navigation. – Bruno T. Abrahão May 10 '22 at 13:48
  • I wonder why `{ scrollPositionRestoration: 'enabled' }` is not yet the default. It worked for my lazyloaded module though @PankajPrakash – BrunoElo Jul 15 '22 at 12:34
70

If you face this problem in Angular 6, you can fix it by adding the parameter scrollPositionRestoration: 'enabled' to app-routing.module.ts 's RouterModule:

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})
s.alem
  • 12,579
  • 9
  • 44
  • 72
Nimezzz
  • 1,814
  • 14
  • 15
  • 6
    Note that as of 6-Nov-2019 using Angular 8, at least, the `scrollPositionRestoration` property does not work with dynamic page content (i.e., where the page content is loaded asynchronously): see this Angular bug report: https://github.com/angular/angular/issues/24547 – dbeachy1 Nov 06 '19 at 20:47
41

EDIT: For Angular 6+, please use Nimesh Nishara Indimagedara's answer mentioning:

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

Original Answer:

If all fails, then create some empty HTML element (eg: div) at the top (or desired scroll to location) with id="top" on template (or parent template):

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

And in component:

  ngAfterViewInit() {
    // Hack: Scrolls to top of Page after page view initialized
    let top = document.getElementById('top');
    if (top !== null) {
      top.scrollIntoView();
      top = null;
    }
  }
GeoRover
  • 409
  • 4
  • 5
  • 2
    This solution worked for me (tested on Chrome as well as Edge). The accepted solution didn't work for my project (Angular5) – Rob van Meeuwen May 07 '18 at 15:20
  • @RobvanMeeuwen, If my answer didn't work it probably you did not implement it the same way. This solution is manipulating directely the DOM which is not correct either safe – Vega Jan 22 '19 at 06:54
  • @Vega, that's why I called it a hack. Your solution is the proper one. Some folks here were not able to implement yours so I offered the fallback hack. They should refactor their code based on the version they are on at this point. – GeoRover Jan 23 '19 at 21:51
  • From all this solution is work for me. Thanks @GeoRover – Gangani Roshan May 17 '19 at 07:05
  • For Angular 6+, please use Nimesh Nishara Indimagedara's answer. – GeoRover Jun 05 '19 at 21:55
14

Now there's a built in solution available in Angular 6.1 with scrollPositionRestoration option.

See my answer on Angular 2 Scroll to top on Route Change.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
Abdul Rafay
  • 3,241
  • 4
  • 25
  • 50
12

From Angular Version 6+ No need to use window.scroll(0,0)

For Angular version 6+ from @docs
Represents options to configure 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'
}

One can use scrollPositionRestoration?: 'disabled' | 'enabled' | 'top' in

Example:

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

And if one requires to manually control the scrolling, No need to use window.scroll(0,0) Instead from Angular V6 common package has introduced ViewPortScoller.

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
}

Usage is pretty Straightforward Example:

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]));
}
Vikas
  • 11,859
  • 7
  • 45
  • 69
  • 2
    Weirdly, every solution works in some cases and fails in others. Angular scrolling to top is seriously flawed. – Sajib Acharya Jan 29 '21 at 21:48
  • 1
    I was able to inject ViewportScroller into my component. It woked. – Code Name Jack Oct 21 '21 at 08:45
  • 2
    Using viewPortScroller.scrollToPosition([0, 0] didn't always scroll to the top. Changed parameter to [0, -50] and it works every time. In my case, there is a page header component around 50px high that is above in app.component.html. Theorizing that scrollToPosition scrolls with respect to . – Paul Evans Oct 24 '22 at 17:09
7

Although @Vega provides the direct answer to your question, there are issues. It breaks the browser's back/forward button. If you're user clicks the browser back or forward button, they lose their place and gets scrolled way at the top. This can be a bit of a pain for your users if they had to scroll way down to get to a link and decided to click back only to find the scrollbar had been reset to the top.

Here's my solution to the problem.

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;
      }
    });
  }
}
Molasses
  • 661
  • 9
  • 12
  • 2
    Thank you for advanced code and nice solution. But sometimes @Vega solution is better, because it solve many problems with animation and dynamic page height. You solution is good if you have long page with content and simple routing animation. I try it on page with many animation and dynamics block and it's looks not so good. I think sometimes we can sacrifice 'back position' for our app. But if not - you solution is the best what I see for Angular. Thank you again – Denis Savenko Mar 31 '18 at 07:56
6

In my case I just added

window.scroll(0,0);

in ngOnInit() and its working fine.

Zohab Ali
  • 8,426
  • 4
  • 55
  • 63
6

Angular 6.1 and later:

You can use built in solution available in Angular 6.1+ with option scrollPositionRestoration: 'enabled' to achieve the same.

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

Angular 6.0 and earlier:

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 that when you navigate back to the page, it should remain scrolled down to the same location it was when you clicked on the link, but scrolling to the top when arriving at every page.

s sharif
  • 724
  • 1
  • 10
  • 19
6

Just add this line on app.module.ts file :

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

I'm using Angular 11.1.4 and its worked for me

Anis KCHAOU
  • 830
  • 1
  • 11
  • 11
5

if your using mat-sidenav give an id to the router outlet( if you have a parent and child router outlets) and use activate function in it <router-outlet id="main-content" (activate)="onActivate($event)"> and use this 'mat-sidenav-content' query selector to scroll top onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }

ivin antony
  • 51
  • 1
  • 1
  • Works great even without using an `id ` (I have a single `router-outlet` on my app). I also did it on a more 'angular way': `@ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }` – GCSDC Mar 10 '20 at 00:05
5

None of the above worked for me for some reason :/, so I added an element ref to a top element in app.component.html, and (activate)=onNavigate($event) to the router-outlet.

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

Then I added the child to the app.component.ts file to the type of ElementRef, and had it scroll to it on activation of the router-outlet.

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

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

Here's the code in stackblitz

Ria Pacheco
  • 151
  • 1
  • 3
  • This was the only one working for me as well. I am using ion-split-pane, and window.scroll doesn't seem to work for it. – Boat Mar 16 '21 at 12:48
4

For some one who is looking for scroll function just add the function and call when ever needed

scrollbarTop(){

  window.scroll(0,0);
}
Akitha_MJ
  • 3,882
  • 25
  • 20
3

I keep looking for a built in solution to this problem like there is in AngularJS. But until then this solution works for me, It's simple, and preserves back button functionality.

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)
}

Answer from zurfyx original post

Jared Whipple
  • 1,111
  • 3
  • 17
  • 38
3

You just need to create a function which contains adjustment of scrolling of your screen

for example

window.scroll(0,0) OR window.scrollTo() by passing appropriate parameter.

window.scrollTo(xpos, ypos) --> expected parameter.

Vijay Barot
  • 352
  • 3
  • 8
3

just add

window.scrollTo({ top: 0 });

to ngOnInit()

Tsvetan Ganev
  • 8,246
  • 4
  • 26
  • 43
Max Hesari
  • 631
  • 6
  • 10
3

The solution that worked for me:

document.getElementsByClassName('layout-content')[0].scrollTo(0, 0);

It's worked in angular 8, 9 and 10.

Breno Gomes
  • 105
  • 3
2

Here is a solution that only scrolls to top of Component if first time visiting for EACH component (in case you need to do something different per component):

In each Component:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

  if(this.firstLoad) {
    window.scroll(0,0);
    this.firstLoad = false;
  }
  ...
}
0_tr0jan_0
  • 53
  • 6
2

Try this:

app.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: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {
    subscription: Subscription;

    constructor(private router: Router) {
    }

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

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}
Eray T
  • 739
  • 6
  • 13
2

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

}
irhetoric
  • 362
  • 2
  • 9
2

Just Add

 ngAfterViewInit() {
  window.scroll(0,0)
 }
Kareem Adel
  • 371
  • 2
  • 10
1

Component: Subscribe to all routing events instead of creating an action in the template and scroll on NavigationEnd b/c otherwise you'll fire this off on bad navs or blocked routes, etc... This is a sure fire way to know that if a route successfully is navigated to, then sooth scroll. Otherwise, do nothing.

@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); // how far to scroll on each step
      } else {
          window.clearInterval(scrollToTop);
      }
    }, 16);
  }

}

HTML

<router-outlet></router-outlet>
1

try this

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

this code supported angular 6<=

Rokive
  • 745
  • 9
  • 7
1
<p class="text-center" (click)="scrollToTop()"> Back to top
<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);
}

Andre Dias
  • 41
  • 2