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 |