New rendered view automatically scrolled down? [Solved]

I have noticed that the (routed) views in my Aurelia application are not automatically scrolled to top if the previous view was scrolled down.

I have made a demo app that illustrates the issue. The demo app starts with page 1 (showing a Lorem Ipsum). At the bottom of the page, there is a link to go to page 2. Page 2 is an almost identical copy of page 1, but it refers back to page 1.

When I scroll down in page 1 and click the link for page 2, page 2 is also immediately scrolled down… I would prefer pages 1 and 2 to be scrolled to the top after rendering.

Is this behavior deliberately designed this way? What is the easiest and/or most straightforward way to automatically scroll up all newly rendered views?

1 Like

Hi. In aurelia, if you need this behaviour of scrolling to the top after navigating to a route, the router has a way to do it.
Notice that this is just one of the many ways it musb be available to do it.
In your case, one way is to add the postcomplete step to the router pipeline, which is executed after activating a new route.
In your gist, app.js could be:

import { PLATFORM } from 'aurelia-framework';
import { Router, RouterConfiguration } from 'aurelia-router';

export class App {
  configureRouter(config: RouterConfiguration, router: Router) {
    config.addPipelineStep('postcomplete', PostCompleteStep);
    config.options.pushState = true;
    config.map([
      { route: '', name: 'root', redirect: 'page1' },
      { route: 'page1', moduleId: PLATFORM.moduleName('page1.html') },
      { route: 'page2', moduleId: PLATFORM.moduleName('page2.html') }
    ]);
  }
}

class PostCompleteStep {
  run(routingContext, next) {
      window.scrollTo(0, 0);
      return next();
  }
}

And voila!

Regards.

2 Likes

Hi @ormasoftchile,

Thanks! Of course, the router pipeline. I didn’t think of that. :blush: That works perfectly indeed. :+1:

2 Likes

This is an elegant solution for Aurelia integration. The curious thing is that it’s only possible to define this at the root router config, and the pipeline class seems to only execute on the top level route, eliminating more fine grain scrollTo at specific child routes. I tried adding a scrollReset setting to the routes, and only when I added it to the top-level route did my pipeline pick it up:

Root routes:

import { PLATFORM } from 'aurelia-pal';
import { Router, RouterConfiguration, ConfiguresRouter, PipelineStep, NavigationInstruction, Next } from 'aurelia-router';

class PostNavigationScrollUp implements PipelineStep {
  run(instruction: NavigationInstruction, next: Next) {
      console.log(instruction);
      if (instruction.config.settings.scrollReset) {
        window.scrollTo(0, 0);
      }
      return next();
  }
}

export class App implements ConfiguresRouter {

  private router: Router;

  configureRouter(config: RouterConfiguration, router: Router) {
    config.options.pushState = true;
    config.options.root = '/';
    config.map([
      { route: ['home'], name: 'home', moduleId: PLATFORM.moduleName('./home/index'), nav: true, title: 'Home' },
    /* "scrollReset: true" WORKS HERE  */
      { route: ['u/:username'], name: 'user', moduleId: PLATFORM.moduleName('./user/index'), nav: false, title: 'User', settings: { scrollReset: true}},
      { route: '', redirect: 'home' }
    ]);

    this.router = router;    
    config.addPipelineStep('postcomplete', PostNavigationScrollUp);

  }
} 

Child routes:

import { PLATFORM } from 'aurelia-pal';
import { RouterConfiguration, Router, RouteConfig } from 'aurelia-router';

export class UserRootView {

  private router: Router;

  configureRouter(config: RouterConfiguration, router: Router) {

    this.router = router;

    config.map([
      { route: [''], name: 'profile', moduleId: PLATFORM.moduleName('./profile.component'), nav: false, title: 'Timeline'},
    /* "scrollReset: true" DOES NOT WORK HERE  */
      { route: ['p/:slug'], name: 'post', moduleId: PLATFORM.moduleName('./post.component'), nav: false, title: 'Post', settings: { scrollReset: true}}
    ]);

  }
}

There is only one console.log output, and that’s for the parent route:

NavigationInstruction {plan: null, options: {…}, fragment: '/u/someuser/p/random-post-slug', queryString: '', config: {…}, …}
config:
hasChildRouter: true
moduleId: "./user/index"
name: "user"
nav: false
navModel: NavModel {isActive: true, title: 'User', href: undefined, relativeHref: 'u/:username', settings: {…}, …}
navigationStrategy: undefined
route: "u/:username/*childRoute"
settings: {scrollReset: true}
title: "User"
viewPorts: {default: {…}}
[[Prototype]]: Object
fragment: "/u/someuser/p/random-post-slug"

Alternatively, window.scrollTo() can just be called in the activate() method of the end view component.

That’s by experience with this.