RxJSでいろいろなパターンのクリックイベントを取得する

RxJSのストリームの扱いに慣れるためにいろいろ試してみたメモ。
サンプルコードではインポートの記述を省略しているので、実際の動きはstackblitzで確認するといいかもです。

基本編

fromEventを使って、プリミティブなクリックイベントを取得してみます。

次のようなHTMLを用意します。

index.html
<button id="button">押してね</button>

fromEventの第一引数に対象のHTMLElementを第二引数にイベント名を入力します。

index.ts
const button: HTMLElement = document.getElementById('button');

// click
const click$: Observable<Event> = fromEvent(button, 'click');
// mousedown
const mousedown$: Observable<Event> = fromEvent(button, 'mousedown');
// mouseup
const mouseup$: Observable<Event> = fromEvent(button, 'mouseup');

それぞれのイベントストリームは、pipe()してゴニョゴニョするなりsubscribeするなり好きに扱う事ができるようになります。

https://stackblitz.com/edit/rxjs-click-events?file=index.ts

ダブルクリック

まず、基本編同様にfromEventdblclickイベントをハンドリングする方法があります。

index.ts
const button: HTMLElement = document.getElementById('button');

// dblclick
const dblclick$: Observable<Event> = fromEvent(button, 'dblclick');

https://stackblitz.com/edit/rxjs-dblclick-event?file=index.ts

別の方法として、次のようにハンドリングする用法も考えられます。
(ダブルクリックの間隔をアプリケーション側で調整したい場合など?)

index.ts
const button: HTMLElement = document.getElementById('button');

const click$: Observable<Event> = fromEvent(button, 'click');

const dblclick$ = click$.pipe(
  bufferTime(1000),
  map((event: Event[]) => event.length),
  filter((eventLength: number) => eventLength === 2),
);

bufferTimeで一定期間のイベントストリームをまとめて流し、そのイベント数をフィルターして条件にあったクリックイベントのみを取得しています。

https://stackblitz.com/edit/rxjs-dblclick-event-2?file=index.ts

n秒間にx回クリック

これは、ダブルクリックの2つめとほぼ同じですが、次のように実現できますね。

index.ts
const button: HTMLElement = document.getElementById('button');

const click$: Observable<Event> = fromEvent(button, 'click');

// 5 times in 2 seconds
const dblclick$ = click$.pipe(
  bufferTime(2000),
  map((event: Event[]) => event.length),
  filter((eventLength: number) => eventLength === 5),
);

https://stackblitz.com/edit/rxjs-x-times-in-n-seconds-event?file=index.ts

長押し

index.ts
const button: HTMLElement = document.getElementById('button');

const mouseDown$: Observable<MouseEvent> = fromEvent(button, 'mousedown');
const mouseUp$: Observable<MouseEvent> = fromEvent(button, 'mouseup');

// long press 2 seconds
const longpress$ = mouseDown$.pipe(
  mergeMap((e) => {
    return of(e).pipe(
      delay(2000),
      takeUntil(mouseUp$),
    );
  }),
);

ポイントは、subscribeをキャンセルする条件(takeUntil)をmergeMapで返す内側のObservableに対して設定することです。
これを、外側(全体)のObservableに対して設定してしまうと、このイベントが一回発火した以降、再度発火しなくなってしまいます。

https://stackblitz.com/edit/rxjs-longpress-event?file=index.ts

 終わりに

RxJS楽しいです(小並感)。
これ何に使うの感は若干ありますが、上記で紹介したストリームをいい感じに組み合わせて複雑なパターンを作り、「隠しページへのリンクを配置する」などといった使い方ができるのではないでしょうか。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.