自动滚动 Angular 2
Autoscroll in Angular 2
我在使用 Angular 2 时遇到了一个问题,即从一条路线更改为另一条路线不会自动滚动到新视图的顶部。我意识到 Angular 1 允许将 autoscroll
属性 添加到 HTML 元素,其他人想出了一些简单的 javascript (例如window.scroll(0, 0)
) 强制视图在加载时滚动到顶部。
但是,我不确定如何使用 Angular 2. 有谁知道如何实现吗?
更新
目前没有自动方式。
另见
另见 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 中提到)
注入路由器,订阅路由更改并调用滚动到顶部:
>=RC.x
router.changes.subscribe() => {
window.scrollTo(0, 0);
});
测试版
router.events
.filter(e => e instanceof NavigationEnd)
.subscribe(() => {
window.scrollTo(0, 0);
});
我遇到了同样的问题。根据 Gunter 的回答,我发现 Angular 的 2 RC.1 新路由器没有直接公开 Observable。相反,它有一个 changes
属性 用于此目的。 RC.1 的解决方法是:
this._router.changes.subscribe(() => {
window.scrollTo(0, 0);
});
较新的 RC (>= RC.3) 似乎没有公开 changes
Observable,它可能已经重命名为 events or routerEvents。
他们完全 "fantastic" 的文档似乎没有提供任何关于正在做什么的信息,所以我猜你正在玩俄罗斯轮盘赌。或者抛硬币什么的。
根据 的回答,events
Observable returns 事件似乎与导航状态有关:
router.events.subscribe(event:Event => {
if(event is NavigationStart) {
}
// NavigationEnd
// NavigationCancel
// NavigationError
// RoutesRecognized
}
对于那些发现 window.scrollTo(0,0)
不起作用的人(我猜是因为 material design sidenav 但完全是猜测)使用这里找到的方法:
Javascript / CSS window.scrollTo(0,0) not working
我已经在每个页面的ngOnInit中使用了if(this.router.navigated)来判断是否使用window.scrollTo(0, 0)。这将涵盖大多数路由到页面的情况,同时将滚动位置保留在单击浏览器后退按钮时应有的位置。
if(this.router.navigated) {
window.scrollTo(0, 0);
}
你能在这里看到吗:https://angular.io/docs/ts/latest/api/router/index/Router-class.html#!#events-anchor,你必须使用"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);
}
});
}
}
我测试的 100% 解决方案:
constructor(router:Router){
this.router.events.subscribe(() => {
window.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);
}
我 post 在 issue thread 编辑了这个,但我会 post 在这里再写一遍。
我的团队一直在 angular.io 上使用 what the angular team uses on this repo。只需创建一个服务并像往常一样注入它。然后,在您想要此行为的每个页面上的 ngAfterViewInit 上,只需调用 this.[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(/^#/, '');
}
}
我在使用 Angular 2 时遇到了一个问题,即从一条路线更改为另一条路线不会自动滚动到新视图的顶部。我意识到 Angular 1 允许将 autoscroll
属性 添加到 HTML 元素,其他人想出了一些简单的 javascript (例如window.scroll(0, 0)
) 强制视图在加载时滚动到顶部。
但是,我不确定如何使用 Angular 2. 有谁知道如何实现吗?
更新
目前没有自动方式。
另见
另见 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 中提到)
注入路由器,订阅路由更改并调用滚动到顶部:
>=RC.x
router.changes.subscribe() => {
window.scrollTo(0, 0);
});
测试版
router.events
.filter(e => e instanceof NavigationEnd)
.subscribe(() => {
window.scrollTo(0, 0);
});
我遇到了同样的问题。根据 Gunter 的回答,我发现 Angular 的 2 RC.1 新路由器没有直接公开 Observable。相反,它有一个 changes
属性 用于此目的。 RC.1 的解决方法是:
this._router.changes.subscribe(() => {
window.scrollTo(0, 0);
});
较新的 RC (>= RC.3) 似乎没有公开 changes
Observable,它可能已经重命名为 events or routerEvents。
他们完全 "fantastic" 的文档似乎没有提供任何关于正在做什么的信息,所以我猜你正在玩俄罗斯轮盘赌。或者抛硬币什么的。
根据 events
Observable returns 事件似乎与导航状态有关:
router.events.subscribe(event:Event => { if(event is NavigationStart) { } // NavigationEnd // NavigationCancel // NavigationError // RoutesRecognized }
对于那些发现 window.scrollTo(0,0)
不起作用的人(我猜是因为 material design sidenav 但完全是猜测)使用这里找到的方法:
Javascript / CSS window.scrollTo(0,0) not working
我已经在每个页面的ngOnInit中使用了if(this.router.navigated)来判断是否使用window.scrollTo(0, 0)。这将涵盖大多数路由到页面的情况,同时将滚动位置保留在单击浏览器后退按钮时应有的位置。
if(this.router.navigated) {
window.scrollTo(0, 0);
}
你能在这里看到吗:https://angular.io/docs/ts/latest/api/router/index/Router-class.html#!#events-anchor,你必须使用"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);
}
});
}
}
我测试的 100% 解决方案:
constructor(router:Router){
this.router.events.subscribe(() => {
window.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);
}
我 post 在 issue thread 编辑了这个,但我会 post 在这里再写一遍。
我的团队一直在 angular.io 上使用 what the angular team uses on this repo。只需创建一个服务并像往常一样注入它。然后,在您想要此行为的每个页面上的 ngAfterViewInit 上,只需调用 this.[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(/^#/, '');
}
}