EventManager / createDoubleTapDetector

AbortController を土台にした、ライフサイクルに閉じたイベントの登録簿です。あわせて、ポインターの種類を問わない、タッチのジェスチャー向けの小さなダブルタップの検出器も入っています。

これが解くのは、リスナーを 外すほう の問題です。removeEventListener が効くのは、登録したときと まったく同じ 関数の参照とオプションを渡したときだけです。渡す途中でハンドラーをアロー関数で包んでしまえば、もう二度と外せません。マウントとアンマウントを繰り返すコンポーネントは、そのたびにリスナーをひとつずつ漏らしていきます。AbortController は、そのすべてを abort() ひと呼びに変えます。

使い方

Web Component の中で

import { EventManager } from 'ranuts/utils';

class MyElement extends HTMLElement {
  private _events = new EventManager();

  connectedCallback() {
    this._events.on(this._input, 'input', this.handleInput).on(this, 'click', this.handleClick, { capture: true });
  }

  disconnectedCallback() {
    this._events.abort(); // すべてのリスナーを外し、次の接続に備えて仕切り直します
  }
}

ふつうのページのコードで

function initSection(container: HTMLElement) {
  const scope = new EventManager();

  scope.on(input, 'input', handleSearch).delegate(container, '[data-action]', 'click', (ev, target) => {
    handleAction(target.getAttribute('data-action'));
  });

  return () => scope.abort(); // その区画を片づけるときに呼びます
}

API

on

このマネージャーに閉じたリスナーを登録します。つないで書けます。

パラメーター

パラメーター 説明 既定値
target イベントの相手 EventTarget 必須
type イベントの名前 string 必須
handler ハンドラーの関数 EventListener 必須
options addEventListener のオプションから signal を除いたもの Omit<AddEventListenerOptions,'signal'> -

戻り値

引数 説明
this このマネージャー。つないで書くためのものです EventManager

delegate

イベントの委譲です。parentひとつだけ リスナーを付け、selector に当てはまる子孫からイベントが起きたときにかぎり handler を呼びます。つないで書けます。

ハンドラーは、もとのイベントと、当てはまった要素を受け取ります。

scope.delegate(list, '.item', 'click', (ev, item) => {
  console.log(item.getAttribute('data-id'));
});

パラメーター

パラメーター 説明 既定値
parent ただひとつのリスナーを結び付ける要素 HTMLElement 必須
selector 子孫が当てはまるべきセレクター string 必須
type イベントの名前 string 必須
handler (event, matchedElement) => void Function 必須
options addEventListener のオプションから signal を除いたもの Omit<AddEventListenerOptions,'signal'> -

戻り値

引数 説明
this このマネージャー。つないで書くためのものです EventManager

abort

登録したリスナーをすべて外し、内側の AbortController を作り直します。何度呼んでも大丈夫です。そのあとの on()delegate() は、まっさらなところから始まります。

戻り値

戻り値はありません(void

signal

土台の AbortSignal です。自分で addEventListener に渡したいときのために公開しています。

引数 説明
signal このマネージャーの中断シグナル AbortSignal

createDoubleTapDetector

生の (x, y, 時刻) の標本からダブルタップを見分けます。ポインターの種類を問わないので、Pointer、Touch、Mouse のどのイベントから渡しても同じように働きます。タッチのジェスチャー(ダブルタップで頭出し、拡大、いいね)のために作られました。この手の場面では、時刻と距離のしきい値の判定を呼び出しのたびに書き直すと、気づきにくい形で間違えやすいのです。片方の軸しか比べていない、あるいは当たったあとに仕切り直すのを忘れて、素早い 3 回のタップが重なり合ったダブルタップ 2 回として数えられてしまう、といった具合に。

import { createDoubleTapDetector } from 'ranuts/utils';

const detector = createDoubleTapDetector();
el.addEventListener('pointerup', (e) => {
  if (detector.check(e.clientX, e.clientY)) seek();
});

createDoubleTapDetector(options?)

パラメーター(DoubleTapDetectorOptions

オプション 説明 既定値
windowMs 2 回のタップのあいだの、許される最大の間隔(ミリ秒) number 300
maxDistancePx 2 回のタップのあいだの、平面上で許される最大の距離(px) number 60

DoubleTapDetector

メンバー 説明
check (x, y) でのタップを記録し、それが直前のタップとダブルタップを成すかどうかを報せます。ダブルタップと見なした時点で追跡を仕切り直すので、素早い 3 回目のタップは同じダブルタップの一部として数えられず、新しい組の始まりになります (x: number, y: number, now?: number) => boolean
reset 最後に記録したタップを忘れます。タップ以外のジェスチャー(ドラッグなど)が始まったときに呼んでください () => void