在Angular 2中自动滚动

时间:2016-03-24 13:41:50

标签: javascript angular angular2-routing

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

但是,我不知道如何使用Angular 2完成此任务。有谁知道如何实现这一目标?

10 个答案:

答案 0 :(得分:17)

<强>更新

目前没有自动方式。

另见Angular 2 typescript error when using subscribe function on new router (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-beta.2中,您可以传递带有路由器链接和路由器导航的片段

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

它应滚动到它,但也会将#top添加到URL(尚未自我测试)

<强>更新

<强>原始

有一个涉及此https://github.com/angular/angular/issues/6595

的未解决问题

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

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

&gt; = RC.x

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

<强>β

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

答案 1 :(得分:13)

你能看到这里:https://angular.io/docs/ts/latest/api/router/index/Router-class.html#!#events-anchor,自Angular 2.0.0以来你必须使用“router.events.subscribe”

因此,自动scrool到所有页面顶部的一个很好的解决方案是拥有这样的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);
            }
        });
    }
}

答案 2 :(得分:9)

较新的RC(&gt; = RC.3)似乎没有公开changes Observable,它可能已经重命名为eventsrouterEvents

他们完全“精彩”的文档似乎没有提供任何有关正在做什么的信息,所以我猜你在那里有一个小俄罗斯轮盘。或翻硬币或其他东西。

this回答,似乎events Observable返回有关导航状态的事件:

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

答案 3 :(得分:4)

我有同样的问题。根据Gunter的回答,我发现Angular的2 RC.1新路由器不会直接暴露Observable。相反,它有一个changes属性用于此目的。 RC.1的解决方法是:

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

答案 4 :(得分:4)

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

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

答案 5 :(得分:3)

我测试的100%溶液:

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

答案 6 :(得分:2)

我在issue thread发布了此内容,但我会在此处再次发布。

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

服务代码:

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(/^#/, '');
  }
}

答案 7 :(得分:1)

对于那些发现window.scrollTo(0,0)没有工作的人(我猜是因为材料设计方面而非完全猜测),请使用此处找到的方法: Javascript / CSS window.scrollTo(0,0) not working

答案 8 :(得分:1)

我使用的是材料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;
    }
  }
}

答案 9 :(得分:1)

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

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

    onActivate(e) {
        window.scrollTo(0, 0);
    }