Ответ 1
Вероятно, ваш document
не прокручивается, а div
внутри него. Событие прокрутки только пузырится до window
, если оно вызвано из document
. Также, если вы захватили событие из 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 = (): 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
, которая позволяет передавать объект как третий параметр.
С помощью этого объекта вы можете отметить прослушиватель событий как passive
. Это рекомендуемая вещь для события, которое устраивает много времени, что может помешать работе с пользовательским интерфейсом, например, с помощью события прокрутки. Чтобы реализовать это, вы должны сначала проверить, поддерживает ли текущий браузер эту функцию. На mozilla.org они отправили метод passiveSupported
, с помощью которого вы можете проверить поддержку браузера. Вы можете использовать это, хотя, когда вы уверены, что не собираетесь использовать event.preventDefault()
Прежде чем я покажу вам, как это сделать, есть еще одна функция производительности, о которой вы могли бы подумать. Чтобы предотвратить обнаружение изменений, (DoCheck
вызывается каждый раз, когда в зоне происходит что-то async. Как и при запуске события), вы должны запускать прослушиватель событий вне зоны и вводить его только тогда, когда это действительно необходимо. Су, позвольте объединить все это:
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();
});
}
};
}