在Angular 2中自动滚动

Autoscroll in Angular 2

本文关键字:滚动 Angular      更新时间:2023-09-26

我在Angular 2中遇到一个问题,从一条路线更改到另一条路线不会自动滚动到新视图的顶部。我意识到Angular 1允许将autoscroll属性添加到HTML元素中,而其他人则想出了一些简单的javascript(如window.scroll(0, 0))来强制视图在加载时滚动到顶部。

然而,我不确定如何使用Angular 2来实现这一点。有人知道如何做到这一点吗?

更新

目前还没有自动的方法。

另请参阅Angular 2在新路由器上使用订阅功能时的打字错误(rc 1)

另请参阅https://github.com/angular/angular/issues/6595#issuecomment-244232725

class MyAppComponent {
  constructor(router: Router) {
    router.events.subscribe(s => {
      if (s instanceof NavigationEnd) {
        const tree = router.parseUrl(router.url);
        if (tree.fragment) {
          // you can use DomAdapter
          const element = document.querySelector("#" + tree.fragment);
          if (element) { element.scrollIntoView(element); }
        }
      }
    });
  }
}

更新

在新的路由器V3测试版2中,您可以传递带有路由器链接和路由器导航的片段

<a [routerLink]="..." fragment="top">

它应该滚动到它,但也将#top添加到URL(我自己还没有测试)

更新

原始

这是一个悬而未决的问题https://github.com/angular/angular/issues/6595

解决方法(在https://github.com/angular/angular/issues/6946)

注入路由器,订阅路由更改并调用滚动到顶部:

>=RC.x

router.changes.subscribe() => {
  window.scrollTo(0, 0);
});

β

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

您可以在这里看到吗:https://angular.io/docs/ts/latest/api/router/index/Router-class.html##事件锚点,您必须使用"router.events.subscribe",因为Angular 2.0.0

因此,自动搜索到所有页面顶部的一个好的解决方案是拥有这样的AppComponent:

import {Component} from '@angular/core';
import {Router, NavigationEnd} from "@angular/router";
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
    constructor(private router: Router) {
        router.events.subscribe((val) => {
            if (val instanceof NavigationEnd){
                window.scrollTo(0,0);
            }
        });
    }
}

较新的RC(>=RC.3)似乎没有公开changes Observable,它可能已经被重命名为events或routerEvents。

他们完全"神奇"的医生似乎没有提供任何关于正在做什么的信息,所以我想你会在那里玩一点俄罗斯轮盘赌。或者扔硬币什么的。

从这个答案来看,events Observable似乎返回了有关导航状态的事件:

router.events.subscribe(event:Event => {
    if(event is NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized   
  }

我遇到了同样的问题。根据Gunter的回答,我发现Angular的2 RC.1新路由器并没有直接暴露Observable。相反,它具有用于该目的的changes属性。RC.1的变通方法是:

this._router.changes.subscribe(() => {
    window.scrollTo(0, 0);
}); 

我在每个页面的ngOnInit中使用了if(this.router.navigationd)来确定是否使用window.sollTo(0,0)。这将涵盖大多数路由到页面的情况,同时如果单击浏览器的"后退"按钮,则将滚动位置保留在该位置。

if(this.router.navigated) {
  window.scrollTo(0, 0);
}

我测试的100%溶液:

constructor(router:Router){
    this.router.events.subscribe(() => {
        window.scrollTo(0, 0);
    });
}

我在问题线程中发布了这篇文章,但我会在这里再次发布。

我的团队一直在angular.io上使用angular团队在这个repo上使用的东西。只需像往常一样提供服务并注入即可。然后,在每个页面上的ngAfterViewInit上,只要调用此行为即可。[scroll service variable name]scrollToTop()。最后,您需要将其添加到index.html:<div id="top-of-page"></div><body>的顶部

服务代码:

import { Injectable, Inject } from '@angular/core';
import { PlatformLocation } from '@angular/common';
import { DOCUMENT } from '@angular/platform-browser';
import {fromEvent} from 'rxjs/observable/fromEvent';
export const topMargin = 16;
/**
 * A service that scrolls document elements into view
 */
@Injectable()
export class ScrollService {
  private _topOffset: number | null;
  private _topOfPageElement: Element;
  // Offset from the top of the document to bottom of any static elements
  // at the top (e.g. toolbar) + some margin
  get topOffset() {
    if (!this._topOffset) {
      const toolbar = this.document.querySelector('md-toolbar.app-toolbar');
      this._topOffset = (toolbar && toolbar.clientHeight || 0) + topMargin;
    }
    return this._topOffset;
  }
  get topOfPageElement() {
    if (!this._topOfPageElement) {
      this._topOfPageElement = this.document.getElementById('top-of-page') || this.document.body;
    }
    return this._topOfPageElement;
  }
  constructor(
      @Inject(DOCUMENT) private document: any,
      private location: PlatformLocation) {
    // On resize, the toolbar might change height, so "invalidate" the top offset.
    fromEvent(window, 'resize').subscribe(() => this._topOffset = null);
  }
  /**
   * Scroll to the element with id extracted from the current location hash fragment.
   * Scroll to top if no hash.
   * Don't scroll if hash not found.
   */
  scroll() {
    const hash = this.getCurrentHash();
    const element: HTMLElement = hash
        ? this.document.getElementById(hash)
        : this.topOfPageElement;
    this.scrollToElement(element);
  }
  /**
   * Scroll to the element.
   * Don't scroll if no element.
   */
  scrollToElement(element: Element) {
    if (element) {
      element.scrollIntoView();
      if (window && window.scrollBy) {
        // Scroll as much as necessary to align the top of `element` at `topOffset`.
        // (Usually, `.top` will be 0, except for cases where the element cannot be scrolled all the
        //  way to the top, because the viewport is larger than the height of the content after the
        //  element.)
        window.scrollBy(0, element.getBoundingClientRect().top - this.topOffset);
        // If we are very close to the top (<20px), then scroll all the way up.
        // (This can happen if `element` is at the top of the page, but has a small top-margin.)
        if (window.pageYOffset < 20) {
          window.scrollBy(0, -window.pageYOffset);
        }
      }
    }
  }
  /** Scroll to the top of the document. */
  scrollToTop() {
    this.scrollToElement(this.topOfPageElement);
  }
  /**
   * Return the hash fragment from the `PlatformLocation`, minus the leading `#`.
   */
  private getCurrentHash() {
    return this.location.hash.replace(/^#/, '');
  }
}

对于那些发现window.scrollTo(0,0)不起作用的人(我猜是因为材料设计侧面导航,但完全是猜测),请使用这里的方法:Javascript/CSS窗口.scrollTo(0,0)不工作

我正在使用material sidenav,但我无法获得任何建议的答案。以下是我的工作解决方案:

import { Router, NavigationEnd } from '@angular/router';
...
constructor(
  private router: Router,
) {
  router.events.subscribe(event => {
    if (event instanceof NavigationEnd) {
      document.querySelector('.mat-sidenav-content').scrollTop = 0;
    }
  }
}

我没有在每个组件中编写代码,而是在一个地方添加了以下代码-

<router-outlet (activate)="onActivate($event)"></router-outlet>
    onActivate(e) {
        window.scrollTo(0, 0);
    }