web-dev-qa-db-ja.com

Angular 4のウィンドウスクロールイベントの処理方法

ウィンドウスクロールイベントをキャプチャできないようです。いくつかのサイトで、次のようなコードを見つけました。

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

スニペットは多くの場合バージョン2からのものです。これはAngular 4.2.2では機能していないようです(もう?)。たとえば、「window:scroll」を「window:touchmove」に置き換えた場合、touchmoveイベントは正常に処理されます。

誰かが私が欠けていることを知っていますか?どうもありがとうございました!

26
Robert

おそらくあなたのdocumentはスクロールしていませんが、その中のdivはスクロールしています。スクロールイベントは、windowから呼び出された場合にのみdocumentまでバブルします。また、documentからイベントをキャプチャし、stopPropagationのようなものを呼び出した場合、windowでイベントを受信しません。

アプリケーション内のすべてのスクロールイベントをキャプチャする場合は、これも小さなスクロール可能なコンテナからのものであり、addEventListeneruseCaptureに設定したデフォルトの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としてマークできます。これは、スクロールイベントのように、UIのパフォーマンスを妨げる可能性のある、長時間起動するイベントで行うことをお勧めします。これを実装するには、まず現在のブラウザがこの機能をサポートしているかどうかを確認する必要があります。 mozilla.orgにメソッドpassiveSupportedを投稿しました。このメソッドを使用して、ブラウザのサポートを確認できます。ただし、event.preventDefault()を使用しないことが確実な場合にのみ、これを使用できます。

その方法を説明する前に、考えられる別のパフォーマンス機能があります。変更検出の実行を防ぐには(ゾーン内で非同期が発生するたびにDoCheckが呼び出されます。イベントの発生のように)、ゾーン外でイベントリスナーを実行し、本当に必要な場合にのみ入力する必要があります。スー、これらすべてを組み合わせましょう:

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();
           });
        }
    };   
}
65
PierreDuc

たまたまAngular Materialを使用

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

Tsで:

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

12
ttugates

まだコメントできません。 @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;
  }
6
Jason

@PierreDuc、答えてくれてありがとう。

パッシブリスナーによるスクロールパフォーマンスの向上 MDNのセクションで説明されているとおり:

基本的なscrollイベントのpassiveの値について心配する必要はありません。キャンセルできないため、イベントリスナーはページのレンダリングをブロックできません。

したがって、私に関する限り、window.addEventListener関数の3番目のパラメーターとしてtrueを渡すだけで、答えを単純化できます。

0
vgrigory