Angular 2 Di chuyển lên đầu trên Thay đổi tuyến đường


296

Trong ứng dụng Angular 2 của tôi khi tôi cuộn xuống một trang và nhấp vào liên kết ở cuối trang, nó sẽ thay đổi tuyến đường và đưa tôi đến trang tiếp theo nhưng nó không cuộn lên đầu trang. Kết quả là, nếu trang đầu tiên dài và trang thứ 2 có ít nội dung, nó sẽ tạo ấn tượng rằng trang thứ 2 thiếu nội dung. Vì nội dung chỉ hiển thị nếu người dùng cuộn lên đầu trang.

Tôi có thể cuộn cửa sổ lên trên cùng của trang trong ngInit của thành phần nhưng, có giải pháp nào tốt hơn có thể tự động xử lý tất cả các tuyến đường trong ứng dụng của tôi không?


20
Kể từ Angular 6.1, chúng tôi có thể sử dụng {scrollPocationRestoration: 'enable'} trên các mô-đun được tải háo hức hoặc chỉ trong app.module và nó sẽ được áp dụng cho tất cả các tuyến. RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })
Manwal 16/03/19

Muito obrigado sua solução funcionou perfeitamente para mim :)
Raphael Godoi

không một người đề cập tập trung? điều quan trọng hơn bao giờ hết là hỗ trợ chính xác khả năng truy cập / trình đọc màn hình và nếu bạn chỉ cần cuộn lên trên cùng mà không xem xét tiêu điểm thì phím nhấn tab tiếp theo có thể nhảy xuống dưới cùng của màn hình.
Simon_Weaver

Câu trả lời:


386

Bạn có thể đăng ký một trình lắng nghe thay đổi tuyến đường trên thành phần chính của bạn và cuộn lên đầu các thay đổi tuyến đường.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {
    constructor(private router: Router) { }

    ngOnInit() {
        this.router.events.subscribe((evt) => {
            if (!(evt instanceof NavigationEnd)) {
                return;
            }
            window.scrollTo(0, 0)
        });
    }
}

11
window.scrollTo(0, 0)là một document.body.scrollTop = 0;IMO ngắn gọn hơn và dễ đọc hơn.
Đánh dấu E. Haase

10
Có ai để ý, rằng ngay cả sau khi thực hiện điều này, vấn đề vẫn tồn tại trong trình duyệt safari của Iphone. có suy nghĩ gì không?
rgk

1
@mehaase Hình như câu trả lời của bạn là câu trả lời hay nhất. window.body.scrollTop không hoạt động với tôi trên máy tính để bàn Firefox. Cảm ơn !
KCarnaille

3
Điều này làm việc cho tôi, nhưng nó phá vỡ hành vi nút "trở lại" mặc định. Quay trở lại nên nhớ vị trí cuộn trước đó.
JackKalish

6
Điều này đã làm việc !! Mặc dù tôi đã thêm $("body").animate({ scrollTop: 0 }, 1000);chứ không phải là window.scrollTo(0, 0)hoạt hình di chuyển mượt mà lên đầu
Manubhargav

360

Góc 6.1 trở lên :

Angular 6.1 (phát hành vào 2018-07-25) đã thêm hỗ trợ tích hợp để xử lý vấn đề này, thông qua một tính năng gọi là "Phục hồi vị trí cuộn bộ định tuyến". Như được mô tả trong blog Angular chính thức , bạn chỉ cần kích hoạt tính năng này trong cấu hình bộ định tuyến như thế này:

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

Hơn nữa, blog viết "Dự kiến ​​rằng điều này sẽ trở thành mặc định trong một phiên bản chính trong tương lai". Cho đến nay điều này đã không xảy ra (kể từ Angular 8.2), nhưng cuối cùng bạn sẽ không cần phải làm bất cứ điều gì trong mã của mình và điều này sẽ hoạt động chính xác.

Bạn có thể xem thêm chi tiết về tính năng này và cách tùy chỉnh hành vi này trong các tài liệu chính thức .

Góc 6.0 trở về trước :

Mặc dù câu trả lời tuyệt vời của @ GuilhermeMeirele đã khắc phục vấn đề ban đầu, nhưng nó lại đưa ra một câu hỏi mới, bằng cách phá vỡ hành vi bình thường mà bạn mong đợi khi bạn điều hướng lùi hoặc tiến (bằng các nút trình duyệt hoặc thông qua mã Địa điểm). Hành vi dự kiến ​​là khi bạn điều hướng trở lại trang, nó sẽ vẫn được cuộn xuống cùng vị trí khi bạn nhấp vào liên kết, nhưng cuộn lên đầu khi đến mỗi trang rõ ràng sẽ phá vỡ kỳ vọng này.

Mã bên dưới mở rộng logic để phát hiện loại điều hướng này bằng cách đăng ký chuỗi PopStateEvent của Location và bỏ qua logic cuộn lên trên cùng nếu trang mới đến là kết quả của sự kiện đó.

Nếu trang bạn điều hướng trở lại đủ dài để bao quát toàn bộ chế độ xem, vị trí cuộn được khôi phục tự động, nhưng như @JordanNelson đã chỉ ra chính xác, nếu trang ngắn hơn, bạn cần theo dõi vị trí cuộn y ban đầu và khôi phục lại vị trí cuộn y ban đầu và khôi phục nó rõ ràng khi bạn quay trở lại trang. Phiên bản cập nhật của mã cũng bao gồm trường hợp này, bằng cách luôn khôi phục rõ ràng vị trí cuộn.

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
Điều này sẽ đi trực tiếp trong thành phần ứng dụng hoặc trong một thành phần duy nhất được sử dụng trong đó (và do đó được chia sẻ bởi toàn bộ ứng dụng). Chẳng hạn, tôi đã đưa nó vào một thành phần thanh điều hướng hàng đầu. Bạn không nên bao gồm trong tất cả các thành phần của bạn.
Fernando Echeverria

3
Bạn có thể làm điều đó và nó sẽ làm cho mã tương thích rộng rãi hơn với các nền tảng khác, không phải trình duyệt. Xem stackoverflow.com/q 432177221/2858481 để biết chi tiết thực hiện.
Fernando Echeverria

3
Nếu bạn nhấp và giữ nút quay lại / chuyển tiếp trong các trình duyệt hiện đại, một menu sẽ xuất hiện cho phép bạn điều hướng đến các vị trí khác ngoài vị trí trước đó / kế tiếp của bạn. Giải pháp này phá vỡ nếu bạn làm điều đó. Đó là một trường hợp cạnh cho hầu hết, nhưng đáng nói.
adamdport


1
Có cách nào để bật "Phục hồi vị trí cuộn bộ định tuyến" cho các phần tử lồng nhau hay nó chỉ hoạt động cho body?
âm hộ

61

Từ Angular 6.1, giờ đây bạn có thể tránh được rắc rối và chuyển extraOptionsđến RouterModule.forRoot()tham số thứ hai của mình và có thể chỉ định scrollPositionRestoration: enabledđể bảo Angular di chuyển lên trên bất cứ khi nào tuyến đường thay đổi.

Theo mặc định, bạn sẽ tìm thấy điều này trong app-routing.module.ts:

const routes: Routes = [
  {
    path: '...'
    component: ...
  },
  ...
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      scrollPositionRestoration: 'enabled', // Add options right here
    })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Tài liệu chính thức góc cạnh


3
Mặc dù câu trả lời ở trên mang tính mô tả nhiều hơn nhưng tôi thích câu trả lời này cho tôi biết chính xác nơi cần đến
ryanovas

32

Bạn có thể viết điều này ngắn gọn hơn bằng cách tận dụng filterphương pháp quan sát được :

this.router.events.filter(event => event instanceof NavigationEnd).subscribe(() => {
      this.window.scrollTo(0, 0);
});

Nếu bạn gặp sự cố khi cuộn lên trên cùng khi sử dụng sidenav Vật liệu góc 2, điều này sẽ giúp ích. Cửa sổ hoặc thân tài liệu sẽ không có thanh cuộn, do đó bạn cần lấy bộ sidenavchứa nội dung và cuộn phần tử đó, nếu không hãy thử cuộn cửa sổ làm mặc định.

this.router.events.filter(event => event instanceof NavigationEnd)
  .subscribe(() => {
      const contentContainer = document.querySelector('.mat-sidenav-content') || this.window;
      contentContainer.scrollTo(0, 0);
});

Ngoài ra, Angular CDK v6.x hiện có gói cuộn có thể giúp xử lý cuộn.


2
Tuyệt quá! Đối với tôi đã làm việc -document.querySelector('.mat-sidenav-content .content-div').scrollTop = 0;
Amir Tugi

Một điều tuyệt vời ... tại mtpultz & @AmirTugi. Đối phó với điều này ngay bây giờ, và bạn đóng đinh nó cho tôi, chúc mừng! Có lẽ chắc chắn sẽ kết thúc việc điều hướng bên của tôi vì Vật liệu 2 không chơi tốt khi thanh công cụ md ở vị trí: cố định (ở trên cùng). Trừ khi các bạn có ý tưởng .... ????
Tim Harker

Có thể đã tìm thấy câu trả lời của tôi ... stackoverflow.com/a/40394105/3389046
Tim Harker

16

Nếu bạn có kết xuất phía máy chủ, bạn nên cẩn thận không chạy mã bằng windowsmáy chủ, nơi biến đó không tồn tại. Nó sẽ dẫn đến việc phá mã.

export class AppComponent implements OnInit {
  routerSubscription: Subscription;

  constructor(private router: Router,
              @Inject(PLATFORM_ID) private platformId: any) {}

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.routerSubscription = this.router.events
        .filter(event => event instanceof NavigationEnd)
        .subscribe(event => {
          window.scrollTo(0, 0);
        });
    }
  }

  ngOnDestroy() {
    this.routerSubscription.unsubscribe();
  }
}

isPlatformBrowserlà một chức năng được sử dụng để kiểm tra xem nền tảng hiện tại nơi ứng dụng được hiển thị có phải là trình duyệt hay không. Chúng tôi cho nó tiêm platformId.

Cũng có thể kiểm tra sự tồn tại của biến windows, để an toàn, như thế này:

if (typeof window != 'undefined')

1
Bạn không cần phải tiêm PLATFORM_IDvào constructorvà đưa giá trị này làm tham số trong isPlatformBrowserphương thức de ?
Poul Kruijt

1
@PierreDuc Vâng, câu trả lời là sai. isPlatformBrowserlà một chức năng và sẽ luôn luôn trung thực. Tôi đã chỉnh sửa nó bây giờ.
Lazar Ljubenović

Cảm ơn! Bây giờ là chính xác! Chỉ cần xác minh API: github.com/angular/angular/blob/ từ
Raptor

13

chỉ cần làm điều đó dễ dàng với hành động nhấp chuột

trong thành phần chính html của bạn, hãy tham khảo #scrollContainer

<div class="main-container" #scrollContainer>
    <router-outlet (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

trong thành phần chính .ts

onActivate(e, scrollContainer) {
    scrollContainer.scrollTop = 0;
}

Phần tử được cuộn có thể không nằm trong scrollContainernút đầu tiên, bạn có thể cần phải đào một chút trong đối tượng, đối với tôi, cái mà nó thực sự hoạt động làscrollContainer .scrollable._elementRef.nativeElement.scrollTop = 0
Byron Lopez

13

Angular gần đây đã giới thiệu một tính năng mới, bên trong mô đun định tuyến góc thực hiện các thay đổi như dưới đây

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

12

Câu trả lời hay nhất nằm trong cuộc thảo luận Angular GitHub ( Thay đổi tuyến đường không cuộn lên đầu trong trang mới ).

Có thể bạn chỉ muốn lên đầu trong các thay đổi của bộ định tuyến gốc (không phải ở trẻ em, vì bạn có thể tải các tuyến đường với tải lười biếng trong một tabset)

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

Tín dụng đầy đủ cho JoniJnm ( bài gốc )



7

Kể từ Angular 6.1, bộ định tuyến cung cấp tùy chọn cấu hình được gọi scrollPositionRestoration, cái này được thiết kế để phục vụ cho kịch bản này.

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

4

Nếu bạn chỉ cần cuộn trang lên trên cùng, bạn có thể làm điều này (không phải là giải pháp tốt nhất, nhưng nhanh chóng)

document.getElementById('elementId').scrollTop = 0;

4

Đây là một giải pháp mà tôi đã đưa ra. Tôi đã ghép nối LocationStrargety với các sự kiện Router. Sử dụng LocationStrargety để đặt boolean để biết khi nào người dùng hiện đang duyệt qua lịch sử trình duyệt. Bằng cách này, tôi không phải lưu trữ một loạt dữ liệu URL và cuộn y (dù sao nó cũng không hoạt động tốt, vì mỗi dữ liệu được thay thế dựa trên URL). Điều này cũng giải quyết trường hợp cạnh khi người dùng quyết định giữ nút quay lại hoặc chuyển tiếp trên trình duyệt và quay lại hoặc chuyển tiếp nhiều trang thay vì chỉ một.

PS Tôi chỉ thử nghiệm trên phiên bản IE, Chrome, FireFox, Safari và Opera mới nhất (tính đến thời điểm này).

Hi vọng điêu nay co ich.

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

4

Giải pháp này dựa trên giải pháp của @ FernandoEcheverria và @ GuilhermeMeirele, nhưng nó ngắn gọn hơn và hoạt động với các cơ chế popstate mà Bộ định tuyến Angular cung cấp. Điều này cho phép lưu trữ và khôi phục cấp độ cuộn của nhiều điều hướng liên tiếp.

Chúng tôi lưu trữ các vị trí cuộn cho từng trạng thái điều hướng trong bản đồ scrollLevels. Khi có sự kiện popstate, ID của trạng thái sắp được khôi phục được cung cấp bởi Bộ định tuyến góc : event.restoredState.navigationId. Điều này sau đó được sử dụng để có được mức cuộn cuối cùng của trạng thái đó từ đó scrollLevels.

Nếu không có mức cuộn được lưu trữ cho tuyến đường, nó sẽ cuộn lên trên cùng như bạn mong đợi.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class AppComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit() {
    const scrollLevels: { [navigationId: number]: number } = {};
    let lastId = 0;
    let restoredId: number;

    this.router.events.subscribe((event: Event) => {

      if (event instanceof NavigationStart) {
        scrollLevels[lastId] = window.scrollY;
        lastId = event.id;
        restoredId = event.restoredState ? event.restoredState.navigationId : undefined;
      }

      if (event instanceof NavigationEnd) {
        if (restoredId) {
          // Optional: Wrap a timeout around the next line to wait for
          // the component to finish loading
          window.scrollTo(0, scrollLevels[restoredId] || 0);
        } else {
          window.scrollTo(0, 0);
        }
      }

    });
  }

}

Tuyệt vời. Tôi đã phải tạo một phiên bản tùy chỉnh một chút để cuộn div chứ không phải cửa sổ, nhưng nó đã hoạt động. Một khác biệt quan trọng là scrollTopvs scrollY.
BBaysinger

4

Ngoài câu trả lời hoàn hảo được cung cấp bởi @Guilherme Meirele như được hiển thị bên dưới, bạn có thể điều chỉnh việc thực hiện của mình bằng cách thêm cuộn trơn tru như hiển thị bên dưới

 import { Component, OnInit } from '@angular/core';
    import { Router, NavigationEnd } from '@angular/router';

    @Component({
        selector: 'my-app',
        template: '<ng-content></ng-content>',
    })
    export class MyAppComponent implements OnInit {
        constructor(private router: Router) { }

        ngOnInit() {
            this.router.events.subscribe((evt) => {
                if (!(evt instanceof NavigationEnd)) {
                    return;
                }
                window.scrollTo(0, 0)
            });
        }
    }

sau đó thêm đoạn trích bên dưới

 html {
      scroll-behavior: smooth;
    }

để style.css của bạn


1

đối với safari iphone / ios, bạn có thể gói với setTimeout

setTimeout(function(){
    window.scrollTo(0, 1);
}, 0);

trong trường hợp của tôi, nó cũng yêu cầu phần tử gói trang css được đặt thành; height: 100vh + 1px;
tubbsy

1

Xin chào các bạn, cái này hoạt động với tôi ở góc 4. Bạn chỉ cần tham khảo cha mẹ để cuộn trên bộ định tuyến thay đổi`

layout.component.pug

.wrapper(#outlet="")
    router-outlet((activate)='routerActivate($event,outlet)')

layout.component.ts

 public routerActivate(event,outlet){
    outlet.scrollTop = 0;
 }`

1
tha thứ cho sự lười biếng của tôi trong việc không học pug, nhưng bạn có thể dịch sang HTML không?
CodyBugstein

0

@Fernando Echeverria tuyệt vời! nhưng mã này không hoạt động trong bộ định tuyến băm hoặc bộ định tuyến lười biếng. bởi vì họ không kích hoạt thay đổi vị trí. có thể thử điều này:

private lastRouteUrl: string[] = []
  

ngOnInit(): void {
  this.router.events.subscribe((ev) => {
    const len = this.lastRouteUrl.length
    if (ev instanceof NavigationEnd) {
      this.lastRouteUrl.push(ev.url)
      if (len > 1 && ev.url === this.lastRouteUrl[len - 2]) {
        return
      }
      window.scrollTo(0, 0)
    }
  })
}


0

Sử dụng Routerchính nó sẽ gây ra các vấn đề mà bạn không thể khắc phục hoàn toàn để duy trì trải nghiệm trình duyệt nhất quán. Theo tôi phương pháp tốt nhất là chỉ sử dụng một tùy chỉnh directivevà để điều này thiết lập lại cuộn khi nhấp chuột. Điều tốt về điều này, là nếu bạn ở trên cùng urlkhi bạn nhấp vào, trang cũng sẽ cuộn trở lại đầu trang. Điều này phù hợp với các trang web bình thường. Cơ bản directivecó thể trông giống như thế này:

import {Directive, HostListener} from '@angular/core';

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @HostListener('click')
    onClick(): void {
        window.scrollTo(0, 0);
    }
}

Với cách sử dụng sau:

<a routerLink="/" linkToTop></a>

Điều này sẽ đủ cho hầu hết các trường hợp sử dụng, nhưng tôi có thể tưởng tượng một vài vấn đề có thể phát sinh từ điều này:

  • Không hoạt động universalvì sử dụngwindow
  • Tác động tốc độ nhỏ đến phát hiện thay đổi, bởi vì nó được kích hoạt bởi mỗi lần nhấp
  • Không có cách nào để vô hiệu hóa chỉ thị này

Thực sự khá dễ dàng để khắc phục những vấn đề này:

@Directive({
  selector: '[linkToTop]'
})
export class LinkToTopDirective implements OnInit, OnDestroy {

  @Input()
  set linkToTop(active: string | boolean) {
    this.active = typeof active === 'string' ? active.length === 0 : active;
  }

  private active: boolean = true;

  private onClick: EventListener = (event: MouseEvent) => {
    if (this.active) {
      window.scrollTo(0, 0);
    }
  };

  constructor(@Inject(PLATFORM_ID) private readonly platformId: Object,
              private readonly elementRef: ElementRef,
              private readonly ngZone: NgZone
  ) {}

  ngOnDestroy(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.elementRef.nativeElement.removeEventListener('click', this.onClick, false);
    }
  }

  ngOnInit(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.ngZone.runOutsideAngular(() => 
        this.elementRef.nativeElement.addEventListener('click', this.onClick, false)
      );
    }
  }
}

Điều này đưa hầu hết các trường hợp sử dụng vào tài khoản, với cùng cách sử dụng như trường hợp cơ bản, với ưu điểm là bật / tắt nó:

<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->

quảng cáo, đừng đọc nếu bạn không muốn được quảng cáo

Một cải tiến khác có thể được thực hiện để kiểm tra xem trình duyệt có hỗ trợ passivecác sự kiện hay không . Điều này sẽ làm phức tạp mã hơn một chút và hơi tối nghĩa nếu bạn muốn thực hiện tất cả những điều này trong các chỉ thị / mẫu tùy chỉnh của mình. Đó là lý do tại sao tôi viết một thư viện nhỏ mà bạn có thể sử dụng để giải quyết những vấn đề này. Để có chức năng tương tự như trên và với passivesự kiện được thêm vào , bạn có thể thay đổi chỉ thị của mình thành điều này, nếu bạn sử dụng ng-event-optionsthư viện. Logic nằm bên trong click.pnbngười nghe:

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @Input()
    set linkToTop(active: string|boolean) {
        this.active = typeof active === 'string' ? active.length === 0 : active;
    }

    private active: boolean = true;

    @HostListener('click.pnb')
    onClick(): void {
      if (this.active) {
        window.scrollTo(0, 0);
      }        
    }
}

0

Điều này làm việc cho tôi tốt nhất cho tất cả các thay đổi điều hướng bao gồm cả điều hướng băm

constructor(private route: ActivatedRoute) {}

ngOnInit() {
  this._sub = this.route.fragment.subscribe((hash: string) => {
    if (hash) {
      const cmp = document.getElementById(hash);
      if (cmp) {
        cmp.scrollIntoView();
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
}

0

Ý tưởng chính đằng sau mã này là giữ tất cả các url được truy cập cùng với dữ liệu cuộn tương ứng trong một mảng. Mỗi khi người dùng từ bỏ một trang (NavigationStart), mảng này được cập nhật. Mỗi khi người dùng vào một trang mới (NavigationEnd), chúng tôi quyết định khôi phục vị trí Y hoặc không phụ thuộc vào cách chúng tôi truy cập trang này. Nếu một giới thiệu trên một số trang đã được sử dụng, chúng tôi sẽ cuộn đến 0. Nếu các tính năng quay lại / chuyển tiếp của trình duyệt được sử dụng, chúng tôi sẽ cuộn đến Y được lưu trong mảng của chúng tôi. Xin lỗi vì tiếng Anh của tôi :)

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Location, PopStateEvent } from '@angular/common';
import { Router, Route, RouterLink, NavigationStart, NavigationEnd, 
    RouterEvent } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'my-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {

  private _subscription: Subscription;
  private _scrollHistory: { url: string, y: number }[] = [];
  private _useHistory = false;

  constructor(
    private _router: Router,
    private _location: Location) {
  }

  public ngOnInit() {

    this._subscription = this._router.events.subscribe((event: any) => 
    {
      if (event instanceof NavigationStart) {
        const currentUrl = (this._location.path() !== '') 
           this._location.path() : '/';
        const item = this._scrollHistory.find(x => x.url === currentUrl);
        if (item) {
          item.y = window.scrollY;
        } else {
          this._scrollHistory.push({ url: currentUrl, y: window.scrollY });
        }
        return;
      }
      if (event instanceof NavigationEnd) {
        if (this._useHistory) {
          this._useHistory = false;
          window.scrollTo(0, this._scrollHistory.find(x => x.url === 
          event.url).y);
        } else {
          window.scrollTo(0, 0);
        }
      }
    });

    this._subscription.add(this._location.subscribe((event: PopStateEvent) 
      => { this._useHistory = true;
    }));
  }

  public ngOnDestroy(): void {
    this._subscription.unsubscribe();
  }
}

0

window.scrollTo()không hoạt động với tôi trong Angular 5, vì vậy tôi đã sử dụng document.body.scrollTopnhư,

this.router.events.subscribe((evt) => {
   if (evt instanceof NavigationEnd) {
      document.body.scrollTop = 0;
   }
});

0

Nếu bạn đang tải các thành phần khác nhau với cùng một tuyến đường thì bạn có thể sử dụng ViewportScroller để đạt được điều tương tự.

import { ViewportScroller } from '@angular/common';

constructor(private viewportScroller: ViewportScroller) {}

this.viewportScroller.scrollToPosition([0, 0]);

0

cửa sổ cuộn trên cùng
Cả window.pageY Offerset và document.documentEuity.scrollTop trả về cùng một kết quả trong tất cả các trường hợp. window.pageY Offerset không được hỗ trợ bên dưới IE 9.

app.component.ts

import { Component, HostListener, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  isShow: boolean;
  topPosToStartShowing = 100;

  @HostListener('window:scroll')
  checkScroll() {

    const scrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

    console.log('[scroll]', scrollPosition);

    if (scrollPosition >= this.topPosToStartShowing) {
      this.isShow = true;
    } else {
      this.isShow = false;
    }
  }

  gotoTop() {
    window.scroll({ 
      top: 0, 
      left: 10, 
      behavior: 'smooth' 
    });
  }
}

app.component.html

<style>
  p {
  font-family: Lato;
}

button {
  position: fixed;
  bottom: 5px;
  right: 5px;
  font-size: 20px;
  text-align: center;
  border-radius: 5px;
  outline: none;
}
  </style>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>

<button *ngIf="isShow" (click)="gotoTop()">👆</button>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.