Angular 5 Scroll to top on every Route click

222,596

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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.

Solution 5

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]));
}
Share:
222,596

Related videos on Youtube

raihan
Author by

raihan

Updated on January 13, 2022

Comments

  • raihan
    raihan over 2 years

    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?

  • Denis Savenko
    Denis Savenko about 6 years
    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
  • Rob van Meeuwen
    Rob van Meeuwen about 6 years
    This solution worked for me (tested on Chrome as well as Edge). The accepted solution didn't work for my project (Angular5)
  • Sahil Babbar
    Sahil Babbar about 6 years
    scroll events on window object are not working in angular 5. Any guesses why?
  • Vega
    Vega about 6 years
    @SahilBabbar, Check the body css, overflow:hiden ? what is its height?
  • Sahil Babbar
    Sahil Babbar about 6 years
    @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.
  • Vega
    Vega about 6 years
    Try document.body.scrollTop = 0; or with old js document.querySelector('body').scrollTo(0,0), etc.. If those don't work provide an MCVE
  • Zub
    Zub over 5 years
    you are the best, Thank you so much!
  • rzvAmmar
    rzvAmmar over 5 years
    Thanks for the answer, solved my problem. Also I placed the code in my ngOnInit() method to make it work for Tabs navigation.
  • Vega
    Vega over 5 years
    @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
  • GeoRover
    GeoRover over 5 years
    @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.
  • Manwal
    Manwal about 5 years
    This is what I was looking for RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' }). Thanks
  • Gangani Roshan
    Gangani Roshan about 5 years
    From all this solution is work for me. Thanks @GeoRover
  • Pankaj Prakash
    Pankaj Prakash almost 5 years
    Is there any way for lazyloaded module?
  • GeoRover
    GeoRover almost 5 years
    For Angular 6+, please use Nimesh Nishara Indimagedara's answer.
  • dbeachy1
    dbeachy1 over 4 years
    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: github.com/angular/angular/issues/24547
  • GCSDC
    GCSDC about 4 years
    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 }); }
  • Akitha_MJ
    Akitha_MJ about 4 years
    window.scroll(0,0)
  • Vega
    Vega over 3 years
    @PankajPrakash, I think other that configuring via the router solutions should work. Have you tried the via animation one?
  • Sajib Acharya
    Sajib Acharya over 3 years
    Weirdly, every solution works in some cases and fails in others. Angular scrolling to top is seriously flawed.
  • Boat
    Boat about 3 years
    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.
  • Code Name Jack
    Code Name Jack over 2 years
    I was able to inject ViewportScroller into my component. It woked.
  • rom5jp
    rom5jp about 2 years
    scrollTo(0, 0) did'nt work for me, but scrollIntoView() did. Thanks anyway!
  • Bruno T. Abrahão
    Bruno T. Abrahão about 2 years
    Thanks! This solved my problem of scrolling to top after some action I had on my page, not exactly related to navigation.