簡體   English   中英

Angular 5 在每次路線點擊時滾動到頂部

[英]Angular 5 Scroll to top on every Route click

我正在使用 Angular 5。我有一個儀表板,其中有幾個部分內容很小,而很少有部分內容如此之大,以至於在轉到頂部時更換路由器時遇到問題。 每次我需要滾動到 go 到頂部。

如何解決此問題,以便在更改路由器時,我的視圖始終保持在頂部?

有一些解決方案,請確保全部檢查:)


路由器插座將在實例化新組件時發出activate事件,因此我們可以使用(activate)滾動(例如)到頂部:

應用程序組件.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);
    }

如果你希望有選擇性,說不是每個組件都應該觸發滾動,你可以在這樣的if語句中檢查它:

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

如果您在 Angular 6 中遇到這個問題,您可以通過將參數scrollPositionRestoration: 'enabled'到 app-routing.module.ts 的 RouterModule 來解決它:

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

編輯:對於 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;
    }
  }

現在,Angular 6.1 中有一個內置解決方案,帶有scrollPositionRestoration選項。

請參閱Angular 2 的回答Scroll to top on Route Change

盡管@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;
      }
    });
  }
}

從 Angular 版本 6+ 開始不需要使用 window.scroll(0,0)

對於來自@ docs Angular 版本6+
表示配置路由器的選項。

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' scrollPositionRestoration?: 'disabled' | 'enabled' | 'top' 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]));
}

就我而言,我剛剛添加了

window.scroll(0,0);

ngOnInit()和它的工作正常。

如果您使用 mat-sidenav 為路由器插座提供一個 ID(如果您有父路由器插座和子路由器插座)並在其中使用激活功能<router-outlet id="main-content" (activate)="onActivate($event)">並使用這個 'mat-sidenav-content' 查詢選擇器滾動頂部onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); } onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }

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

注意:預期的行為是,當您導航回頁面時,它應該保持向下滾動到與單擊鏈接時相同的位置,但在到達每個頁面時滾動到頂部。

我一直在尋找解決這個問題的內置解決方案,就像在 AngularJS 中一樣。 但在那之前,這個解決方案對我有用,它很簡單,並且保留了后退按鈕的功能。

應用程序組件.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 原帖的回答

您只需要創建一個包含調整屏幕滾動的功能

例如

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

window.scrollTo(xpos, ypos) --> 預期參數。

只需添加

window.scrollTo({ top: 0);

到 ngOnInit()

對於正在尋找滾動功能的人,只需添加該功能並在需要時調用

scrollbarTop(){

  window.scroll(0,0);
}

由於某種原因,以上都不適合我:/,所以我在app.component.html的頂部元素中添加了一個元素引用,並將(activate)=onNavigate($event)router-outlet

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

然后我說孩子的app.component.ts文件類型ElementRef ,並且有它滾動到它的路由器出口的激活。

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

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

這是stackblitz中的代碼

這是一個僅在第一次訪問每個組件時滾動到組件頂部的解決方案(以防您需要為每個組件做一些不同的事情):

在每個組件中:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

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

試試這個:

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

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

組件:訂閱所有路由事件而不是在模板中創建一個動作並在 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>

試試這個

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

此代碼支持角度 6<=

對我有用的解決方案:

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

它適用於角度 8、9 和 10。

只需在app.module.ts文件中添加這一行:

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

我正在使用 Angular 11.1.4,它對我有用

只需添加

 ngAfterViewInit() {
  window.scroll(0,0)
 }

剛剛想通了。

路由驅動到的組件:

  ngAfterViewInit(): void {
    this.commonService.scrollTo('header', BEHAVIOR.auto)
  }

服務:

  scrollTo(element: string, behavior: BEHAVIOR): void {
    (document.getElementById(element) as HTMLElement).scrollIntoView({behavior: behavior, block: "start", inline: "nearest"});
  }

枚舉:

export enum BEHAVIOR {
  smooth = 'smooth',
  auto = 'auto'
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM