Angular 5ルートをクリックするたびに一番上までスクロールします


99

Angular 5を使用しています。ダッシュボードで、コンテンツが小さいセクションとコンテンツが大きいセクションがいくつかあり、上に行くときにルーターを変更すると問題が発生します。トップに戻るためにスクロールする必要があるたびに。誰かがこの問題を解決するのを手伝ってくれるので、ルータを変更しても私の見解が常にトップに留まります。

前もって感謝します。


回答:


210

いくつかの解決策がありますが、すべてを確認してください:)

ルーターアウトレットは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
その働き。ご回答有難うございます。あなたは私のために多くの時間を節約します
ライハン

2
windowオブジェクトのスクロールイベントが角度5で機能しません。理由は何でしょうか?
Sahil Babbar、

2
ここに本当のヒーロー。フラストレーションの可能な時間を節約しました。ありがとうございました!
Anjana Silva

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
少なくともAngular 8を使用する2019年11月6日以降、この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、2018年

@RobvanMeeuwen、私の答えがうまくいかない場合は、おそらく同じ方法で実装していません。このソリューションは、安全でも正しくないDOMを直接操作しています
Vega

@ベガ、それが私がハックと呼んだ理由です。あなたの解決策は適切なものです。ここの一部の人々はあなたのものを実装することができなかったので、私はフォールバックハックを提案しました。この時点でのバージョンに基づいて、コードをリファクタリングする必要があります。
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に見たものの中で最高です。ありがとうございました
Denis Savenko 2018年


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

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

以下は、初めてコンポーネントごとにアクセスした場合にのみコンポーネントの一番上までスクロールするソリューションです(コンポーネントごとに何かを行う必要がある場合)。

各コンポーネントで:

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以降:

あなたは使用することができ、溶液中建てで利用可能な角度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 <=をサポートしました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.