Как обрабатывать событие прокрутки окна в Angular 4?

Кажется, я не могу захватить событие прокрутки окна. На нескольких сайтах я нашел код, похожий на этот:

@HostListener("window:scroll", [])
onWindowScroll() {
  console.log("Scrolling!");
}

Отрывки часто бывают из версии 2. Это, похоже, не работает (больше?) в Angular 4.2.2. Если я заменил "window: scroll" на "window: touchmove", например, тогда событие touchmove обрабатывается отлично.

Кто-нибудь знает, что мне не хватает? Большое вам спасибо!

Ответ 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();
           });
        }
    };   
}

Ответ 2

Мне пока не разрешено комментировать. @PierreDuc ваш ответ точен, за исключением того, что @Robert сказал, что документ не прокручивается. Я немного изменил ваш ответ, чтобы использовать событие, отправленное слушателем, а затем отслеживать исходный элемент.

 ngOnInit() {
    window.addEventListener('scroll', this.scrollEvent, true);
  }

  ngOnDestroy() {
    window.removeEventListener('scroll', this.scrollEvent, true);
  }

  scrollEvent = (event: any): void => {
    const number = event.srcElement.scrollTop;
  }

Ответ 3

Если вы используете Angular Material, вы можете сделать это:

import { ScrollDispatchModule } from '@angular/cdk/scrolling';

В ц:

import { ScrollDispatcher } from '@angular/cdk/scrolling';

  constructor(private scrollDispatcher: ScrollDispatcher) {    
    this.scrollDispatcher.scrolled().subscribe(x => console.log('I am scrolling'));
  }

И в шаблоне:

<div cdkScrollable>
  <div *ngFor="let one of manyToScrollThru">
    {{one}}
  </div>
</div>

Ссылка: https://material.angular.io/cdk/scrolling/overview

Ответ 4

сначала спасибо всем за все ваши ответы, теперь не могли бы вы рассказать мне, почему так важно установить useCapture в true. Я пробовал без установки useCapture в true, я не получил событие прокрутки.

благодарю вас.