Angular 5每次单击路线时滚动到顶部


99

我使用的是angular5。我有一个仪表板,其中的部分内容很少,而内容的内容如此之多,以至于在更改路由器的顶部时都遇到问题。每次我需要滚动到顶部时。任何人都可以帮助我解决此问题,以便当我更换路由器时,我的看法始终停留在顶部。

提前致谢。


Answers:


212

有一些解决方案,请确保将其全部检查:)

activate每当实例化新组件时,路由器插座都会发出事件,因此我们可以使用(activate)(例如)滚动到顶部:

app.component.html

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

app.component.ts

onActivate(event) {
    window.scroll(0,0);
    //or document.body.scrollTop = 0;
    //or document.querySelector('body').scrollTo(0,0)
    ...
}

或使用此答案来平滑滚动

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

如果希望有所选择,说不是每个组件都应触发滚动,则可以检查一下:

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


从Angular6.1开始,我们还可以 { scrollPositionRestoration: 'enabled' }在热切加载的模块上使用,它将应用于所有路由:

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

它还将进行平滑滚动。然而,这在每个路由上都存在不便。


另一种解决方案是对路由器动画进行顶部滚动。在您要滚动到顶部的每个过渡中添加此代码:

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

4
它的工作。感谢您的回答。您为我节省了很多时间
raihan

2
window对象上的滚动事件在角度5中不起作用。有什么猜测吗?
萨希尔·巴巴尔

2
一个真正的英雄。节省了无聊的时间。谢谢!
Anjana Silva '18

1
@AnjanaSilva,谢谢您的正面评价!我很高兴它可以为您提供帮助:)
Vega

2
延迟加载模块有什么办法吗?
Pankaj Prakash

53

如果您在Angular 6中遇到此问题,可以通过将参数添加scrollPositionRestoration: 'enabled'到app-routing.module.ts的RouterModule中来解决此问题:

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

7
请不要张贴代码图片。只需将代码本身直接复制粘贴到您的答案中即可。
mypetlion

2
当然。下次我会的。我是这里的新手。:)
Nimezzz

4
请注意,至少从2019年11月6日开始使用Angular 8,该scrollPositionRestoration属性不适用于动态页面内容(即页面内容异步加载的地方):请参见以下Angular错误报告:github.com/angular/angular / issues / 24547
dbeachy1

25

编辑:对于Angular 6+,请使用Nimesh Nishara Indimagedara的答案提及:

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

原始答案:

如果全部失败,则在模板(或父模板)上以id =“ top”在顶部(或所需的滚动到位置)创建一些空的HTML元素(例如div):

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

在组件中:

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

2
此解决方案对我有效(已在Chrome和Edge上进行了测试)。公认的解决方案不适用于我的项目(Angular5)
Rob van Meeuwen

@RobvanMeeuwen,如果我的回答不起作用,可能是您没有以相同的方式实现它。此解决方案直接操纵了既不正确也不安全的DOM
Vega,

@Vega,这就是为什么我称其为hack。您的解决方案是正确的。这里的某些人无法实现您的要求,因此我提供了后备黑客程序。他们应该根据此时的版本来重构代码。
GeoRover

从所有这些解决方案为我工作。感谢@GeoRover
Gangani Roshan

对于Angular 6+,请使用Nimesh Nishara Indimagedara的答案。
GeoRover


6

尽管@Vega提供了您问题的直接答案,但仍有问题。它破坏了浏览器的后退/前进按钮。如果用户单击浏览器的后退或前进按钮,他们将失去位置并在顶部滚动。如果您的用户不得不向下滚动以获取链接并决定仅单击返回以查找滚动条已重置为顶部,则这可能会使您的用户感到痛苦。

这是我解决问题的方法。

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

2
感谢您提供高级代码和完善的解决方案。但有时@Vega解决方案更好,因为它解决了动画和动态页面高度方面的许多问题。如果您的内容页长且路由动画简单,则解决方案很好。我在页面上尝试了许多动画和动力学块,但效果似乎不太好。我认为有时候我们可以为我们的应用牺牲“后退位置”。但是,如果没有,您的解决方案将是Angular最好的解决方案。再次感谢您
丹尼斯Savenko


4

从Angular版本6+开始无需使用window.scroll(0,0)

对于6+来自@的Angular版本, 代表用于配置路由器的选项。docs

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

一个可以用scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'

例:

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

并且,如果需要手动控制滚动,则无需使用window.scroll(0,0) Angular V6通用包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
}

用法很简单 例如:

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

4

如果您使用mat-sidenav给路由器插座提供一个ID(如果您有父路由器和子路由器插座),并在其中使用激活功能, <router-outlet id="main-content" (activate)="onActivate($event)"> 然后使用此“ mat-sidenav-content”查询选择器滚动顶部 onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }


即使不使用id router-outlet我的应用上只有一个),效果也很好。我也以更“有角度的方式” @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }
做到了

3

我一直在寻找一个内置的解决方案,就像AngularJS一样。但是直到那时,该解决方案才对我有用,它很简单,并且保留了后退按钮的功能。

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

来自zurfyx 原始帖子的答案


3

您只需要创建一个包含调整屏幕滚动的功能即可

例如

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

window.scrollTo(xpos,ypos)->预期参数。


2

这是一个解决方案,仅在首次访问每个EACH组件时才会滚动到Component的顶部(以防您需要对每个组件执行不同的操作):

在每个组件中:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

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

2

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

}


2

Angular 6.1及更高版本:

您可以使用Angular 6.1+中提供的内置解决方案带有选项scrollPositionRestoration: 'enabled'来实现相同的目的。

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

Angular 6.0及更早版本:

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

注意:预期的行为是,当您导航回到页面时,它应该向下滚动到与单击链接时相同的位置,但是在到达每个页面时都滚动到顶部。



2

对于某些正在寻找滚动功能的人,只需添加功能并在需要时调用

scrollbarTop(){

  window.scroll(0,0);
}

1

试试这个:

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

1

组件:订阅所有路由事件,而不是在模板中创建动作,然后在NavigationEnd b / c上滚动,否则,您将在不良导航或路线阻塞等情况下触发此操作。成功导航到一条路线,然后轻松滚动。否则,什么都不做。

@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

试试这个

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

该代码支持角度6 <=

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.