可能您的document 没有滚动,但其中的div 可以。如果从document 调用滚动事件,它只会冒泡到window。此外,如果您从document 捕获事件并调用stopPropagation 之类的东西,您将不会在window 中收到该事件。
如果您想捕获应用程序内的所有滚动事件,这些事件也来自微小的可滚动容器,您必须使用默认的addEventListener 方法,并将useCapture 设置为true。
这将在事件进入DOM 时触发该事件,而不是气泡阶段。不幸的是,坦率地说,一个很大的失误,angular 不提供传递事件侦听器选项的选项,所以你必须使用addEventListener:
export class WindowScrollDirective {
ngOnInit() {
window.addEventListener('scroll', this.scroll, true); //third parameter
}
ngOnDestroy() {
window.removeEventListener('scroll', this.scroll, true);
}
scroll = (event): void => {
//handle your scroll here
//notice the 'odd' function assignment to a class field
//this is used to be able to remove the event listener
};
}
现在这还不是全部,因为所有主要浏览器(显然除了 IE 和 Edge)都实现了新的 addEventListener 规范,这使得将对象作为 third parameter 传递成为可能。
使用此对象,您可以将事件侦听器标记为passive。建议对触发大量时间的事件执行此操作,这可能会干扰 UI 性能,例如滚动事件。要实现这一点,您应该首先检查当前浏览器是否支持此功能。在 mozilla.org 上,他们发布了一个方法 passiveSupported,您可以使用该方法检查浏览器支持。你只能使用这个,当你确定你不会使用event.preventDefault()
在我向您展示如何做到这一点之前,您可以想到另一个性能功能。为了防止更改检测运行(每次在区域内发生异步事件时都会调用DoCheck。就像事件触发一样),您应该在区域外运行事件侦听器,并且仅在真正需要时才输入它。 Soo,让我们把所有这些东西结合起来:
export class WindowScrollDirective {
private eventOptions: boolean|{capture?: boolean, passive?: boolean};
constructor(private ngZone: NgZone) {}
ngOnInit() {
if (passiveSupported()) { //use the implementation on mozilla
this.eventOptions = {
capture: true,
passive: true
};
} else {
this.eventOptions = true;
}
this.ngZone.runOutsideAngular(() => {
window.addEventListener('scroll', this.scroll, <any>this.eventOptions);
});
}
ngOnDestroy() {
window.removeEventListener('scroll', this.scroll, <any>this.eventOptions);
//unfortunately the compiler doesn't know yet about this object, so cast to any
}
scroll = (): void => {
if (somethingMajorHasHappenedTimeToTellAngular) {
this.ngZone.run(() => {
this.tellAngular();
});
}
};
}