【Angular活用講座】Rxjsでwindow:resizeイベントから要素のClientWidthを上手く捌くやり方


2022/04/25

久々に
Rxjsのネタをやってみます。

今回はタイトルのように
「window:resizeイベントの際の特定HTML要素のClientWidthをRxjs的に監視する」ということをやってみます。


Rxjsでのwindow:resizeイベントハンドラの始め方

これはブラウザの画面サイズを変化させたときに色々な操作を行いたい場合、window:resizeイベントが使えます。

Rxjsでのイベントを制御するObservableを生成したい場合、
fromEventメソッドを利用することになります。

例として、とりあえず
HogeComponentというコンポーネントの要素を画面サイズが変わったときに、特定の要素(ここでは#hoge_contentとしてElementRefで引き出したdiv要素)を監視したいとすると、

            
            import { Component, OnInit, OnDestroy, ViewChild, ElementRef} from '@angular/core';
import {
    Subscription, BehaviorSubject, fromEvent,
    throttleTime, distinctUntilChanged, map
} from 'rxjs';

@Component({
    selector: 'app-hoge-compo',
    template: `
    <div class="hoge-wrapper">
        <div #hoge_content>
            <span>Hello, Hoge!</span>
        </div>
    </div>
    `,
    styleUrls: ['./hoge.component.scss']
})
export class HogeComponent implements OnInit, OnDestroy {

    @ViewChild('hoge_content', { read: ElementRef, static: true }) hogeContent: ElementRef;
    hogeContent$: Subscription;
    windowResize$: Subscription;
    hogeContent$$: BehaviorSubject<number>;

    ngOnInit() {
        //👇BehaviorSubjectでターゲット要素のclientWitdhプロパティを監視
        this.hogeContent$$ = new BehaviorSubject<number>(this.hogeContent.nativeElement.clientWidth);

        //👇rxjsがwindow:resizeのイベントからObservableを購読する
        this.windowResize$ = fromEvent(window, 'resize').pipe(
            throttleTime(500),
            map(_ => this.hogeContent.nativeElement.clientWidth),
            distinctUntilChanged((prev: number, curr: number) => {
                return prev == curr;
            }),
        ).subscribe((res: number) => this.hogeContent$$.next(res));

        //👇画面サイズが変更される度に、BehaviorSubjectからClientWidthが取得できる
        this.hogeContent$ = this.hogeContent$$$.subscribe((cw: number) => {
            //☆ClientWidthを取得!
            console.log(cw);
        });
    }

    ngOnDestroy() {
        //👇コンポーネント終了の際の購読を終了することも忘れずに
        if (this.hogeContent$) { this.hogeContent$.unsubscribe(); }
        if (this.windowResize$) { this.windowResize$.unsubscribe(); }
    }

}
        
のようなコーディングになると思います。


window:resizeイベントを捌くポイント〜throttleTimeとdistinctUntilChanged

もしもthrottleTimedistinctUntilChangedの絞りがないと、画面サイズが変わると無駄にイベントが下流に大放出されてしまいます。

合同会社タコスキングダム|蛸壺の技術ブログ

今回のコードの中で、イベントを綺麗に捌くための大きなポイントとしては、

            
            //...中略

this.windowResize$ = fromEvent(window, 'resize').pipe(
    throttleTime(500), //👈一つ値を通したら、そこから0.5秒以内に来た他の値は捨てる
    map(_ => this.hogeContent.nativeElement.clientWidth), //👈ストリームの値を要素のclientWidthとして変換
    //👇clientWidthが変わらなければストリームは捨てる
    distinctUntilChanged((prev: number, curr: number) => {
        return prev == curr;
    }),
).subscribe((res: number) => this.hogeContent$$.next(res));

//...中略
        
の箇所です。

これがあることで、効率的なイベントハンドリングが実現できるようになります。

合同会社タコスキングダム|蛸壺の技術ブログ

なおこのテクニックでは、window:resizeだけでなく、
window.onscrollなどにも応用が効きますので覚えておかれると役に立つことでしょう。

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。