Angular 5 scroll to top when change route năm 2024

By default, when a new route is activated, Angular’s router doesn’t touch the scroll position. At least this is still true of Angular 9, even if it’ll probably change in the future.

Show

This is problematic in some cases. For instance, if you are on a page that ends up being longer that the viewport, scroll down and navigate to a new route (e.g., using a routerLink), then if the new page is also longer than the viewport, the scrollbar will not be at the top. Not that great since you won’t see the top of the page.

Also, if you navigate back/forward using the browser’s buttons, the scroll position will not be restored either. A sad situation indeed…

Fortunately, there are different solutions to fix that. Let’s see how.

Bazooka approach

When a new route is activated, an “activated” event is emitted, which you can react to. So you could do something like this:

In the example App component above, notice that I’ve added a `

Content` anchor so that I can access the ## element from the controller (using the ViewChild decorator).

Also, I’ve attached an event handler to the activate event. This event fires whenever a new route has been activated.

Let’s now look at what we can do with this in the controller:

In the controller’s onActivate method, I’ve simply retrieved the `

Content` ## element from the DOM and set the nativeElement’s scrollTop property to 0, effectively bringing the scrollbar back to the top of the page.

This ensures that the actual content pane of the application is scrolled back on top whenever we arrive on a new route.

It does work for my use case but is far from perfect. Most notably, if I navigate back to a previous page, it’ll also be scrolled back on top, which is not always what you might want.

So, how can we improve this?

First of all, if you have pages that take a while to load, you could make use of the Window.scrollTo API to scroll to where you want (e.g., an anchor or something), or using the ViewportScroller of Angular, but this would be a nightmare to

tain as it would have to be handled page per page. Moreover, it would only work if your page layout relies on the ## viewport (i.e., if the whole layout scrolls).

Ideally, if you need to remember and restore scroll positions when navigating back/forward, it’s probably better to have a more powerful solution.

Angular’s built-in (but broken?) solution

Since version 6, Angular has built-in support for restoring the scroll position when navigating using the Angular router. Note that, as of Angular 9, that feature still isn’t enabled by default.

To enable (as it is called), you need to adapt your app-routing.module.ts file (i.e., the place where you should find the RouterModule.forRoot call):

In the example above, I’ve enabled the scrollPositionRestoration feature of Angular, which takes care of restoring the scroll position (heh) when navigating.

That setting accepts multiple values:

  • disabled: default, which does nothing
  • top: force scroll position to be (0,0) on all navigation
  • `

    Content`0

When it is set to `

Content`0, it:

  • Scrolls to top whenever you navigate to a new route
  • Restores the previous scroll position when you navigate back to a previous route

Unfortunately, even if it sounds perfect, as explained in this issue, it doesn’t solve all the problems and doesn’t work in all situations.

For instance, in my case, it didn’t help one bit because it only takes care of the scroll position of the ## viewport. Since my application’s layout has a fixed top/left bar, only the content pane (contained in my “##” element) can scroll. This means that scrolling to the top of the page can’t be done simply by using Window.scrollTo(…). In practice, the scroll position restoration feature of Angular doesn’t do anything useful for my application.

Another case where it won’t be able to help is when your content takes time to load. If that’s the case then, depending on the timing, the Angular support for scroll position restoration might fail and put the page on top even if should have restored the scroll position lower.. The thing is that it isn’t aware of your content at all.

Also, it isn’t able to tell the difference between route changes that replace the entire viewport of only change a subset of the page (e.g., changing the contents of a secondary router outlet).

In Angular’s defense, restoring the scroll position isn’t that easy and certainly hard to handle for all cases alike. Other libraries like React and Vue also face the same kinds of issues.

Check out the github issue for other improvement ideas.

Bummer. Let’s try something else.

Custom router scroll service

As far as I’m concerned the bazooka and built-in solution of Angular didn’t cut it. I’ve thus started looking for alternatives.

After searching for a bit, I’ve stumbled upon a great (as usual) article from Angular In Depth which proposed to leverage RxJS and router events to keep track of scroll positions and restore those if/when needed.

Based on that article, its comments and some personal wizardry, I’ve written an Angular service that:

  • keeps track of the scroll position of the ## viewport (just like Angular can do, which is fine for some cases)
  • optionally keeps track of the scroll position of some additional HTML element (e.g., my

    Content ## element, which is my application’s real content viewport)can be configured programmatically using the service’s APIcan be configured declaratively through custom route data definitionsworks great with lazy loaded modules

    Here are its interfaces:

    Let’s go through these step by step.

    The `

    Content`2:

    • `

      Content`3: by default, the service keeps track of the scroll position of the default viewport (i.e., just like Angular does). This method allows disabling that, meaning that it won’t track/adapt its scrolling position`

      Content`4: this method can be called to tell the service to keep track of & adapt the scroll position of a specific HTML element. I’ll show an example usage afterwards`

      Content`5: way to programmatically register a route path and define the expected scroll behavior for matching routes…

      As I’ve explained above, the `

      Content`6 service not only supports programmatic configuration (using `

      Content`5), but also supports declarative configuration through the route’s data. To that end the `

      Content`8 interface defines an optional `

      Content`9 field for application routes and can be used to strongly type the route data (I’ll write an article about that subject later on :p).

      Here’s an example showing how these interfaces can be leveraged while defining the routes:

      In the example above, I’ve defined a route leveraging the ViewChild`0 type and specifying the

      Content`9 for that route as `ViewChild`2, meaning that its scrolling position should be tracked/restored when needed.

      Here’s the implementation of the service:

      This service follows the general algorithm proposed by Jason Awbrey, with a few twists, so make sure to check out his article for implementation details.

      Note that this service must be declared (as I’ve separated interface/implementation and defined an injection token).

      Here’s how to declare it, just in case:

      With this service declared and loaded (through a `ViewChild`3 call, then service can easily be injected and programmatically configured like this:

      With the above in place (and still considering the same AppComponent template presented earlier in this article), the service is now configured to also keep track of the scroll position of the `

      Content` ## element of the page. This ensures that this specific viewport’s scroll position will also be recorded/restored whenever needed.

      This solution is much more powerful than what I could achieve with the bazooka approach (i.e., always scroll to top) or with Angular’s built-in support for scrolling. Now, I can easily adapt the configuration route per route. To define the behavior for each route, I’m using the declarative approach, which feels cleanest.

      There’s still tons of room for improvement though. For instance, Ben Nadel has created a much more advanced solution, capable of keeping track of the scroll position of various elements in the DOM. He explains his approach in detail here. With a bit of effort, the service above could be refactored to do something similar.

      At the very least, it could be improved to keep track of more than a single additional viewport’s scroll position, which would be nice for applications relying on multiple router outlets.

      Conclusion

      In this article, I’ve explained what I’ve learned recently about scroll position handling in Angular using the Angular router.

      As I’ve explained, there are different ways to handle the scrolling behavior when navigating between routes. Some more or less powerful, some with quirks/bugs.

      There are certainly tons of other ways to do all this but, for now, the service that I’ve created and shared here (standing on the shoulder of giants) does what I need and is simple enough to use/configure.

      If you want to learn more, make sure to check out the Angular In Depth article of Jason Awbrey as well as Ben Nadel’s awesome blog.

      How do I scrollTo the top of a route change?

      Approach to implement scroll to top: We initialize a variable by the name routePath and store the value of the current URL in it which is returned with useLocation() hooks. Now a function onTop is created which loads the webpage from the top whenever it is called.

      How to scrollTo the top in Angular?

      By using the built-in directives and event listeners in Angular, you can create a button that scrolls to the top of the page when clicked. To implement this feature in Angular, you can use various approaches such as using a simple CSS style, implementing a directive or a component, or using a third-party library.

      How do you maintain scroll position in Angular?

      Scroll position is maintained on navigation. 'top'- Sets the scroll position to x = 0, y = 0 on all navigation. 'enabled'- Restores the previous scroll position on backward navigation, else sets the position to the anchor if one is provided, or sets the scroll position to [0, 0] (forward navigation).

      How do you scrollTo the top in jQuery?

      In jQuery, the scrollTo() method is used to set or return the vertical scrollbar position for a selected element. This behavior can be used to scroll to the top of the page by applying this method on the window property. Setting the position parameter to 0 scrolls the page to the top.