イベントハンドラの登録でアロー関数式を使いたいという状況が結構ある。
一番大きいのは、functionを使ってその場で関数を定義した場合はイベントターゲットとなったオブジェクトがthisに設定されるが、アロー関数式を使った場合は現在のスコープのthisがそのままアロー関数内でthisとして使われる点である。
わざわざ別にthisを指定しなくてよいのでクラスを書くとアロー関数だらけになるのだが、その場でアロー関数を定義しているとremoveEventListenerでハンドラを削除するときにちょっと困ったことになる。
一番普通なのは登録するときにメンバ変数にぶち込んで名前をつける方法だと思う。
document.getElementById("hoge").addEventListener("click", this.foo = ev => { ... });
...
document.getElementById("hoge").removeEventListener("click", this.foo);
この方法はそもそもgetElementByIdと一緒に使われることが多くて元々長いものがさらに長くなる上に、要素・ハンドラごとに別々の名前を用意してあげる必要があって面倒くさい。
基本的にremoveEventListenerを呼び出すときはaddEventListenerと同じ情報が必要で、この情報を保持しておくのが面倒である。
ぐーぐる先生に聞いてみると、ハンドラ登録関数を作ってその中で適当なキーを割り当て、そこにアロー関数と共に登録に使った情報を保持しておくという方法もあった。
ハンドラ登録関数が返したキーをハンドラ削除関数に渡すと、パラメータ一式を引き出してきて削除してくれるようになっている。
だいぶ楽にはなるが、キーは覚えておかなければならない。
だが、よく考えてみるとハンドラ登録直後はすべての情報が揃っている。
つまり、この時点でremoveEventListenerを呼び出すのはさほど面倒くさくない。
だったら、removeEventHandlerの呼び出しそのものを変数に保持しておけばパラメータを保持しておく必要がないことに気づいた。
つまり、こういうことである。
let fn;
document.getElementById("hoge").addEventListener("click", fn = ev => { ... });
this.remover = () => document.getElementById("hoge").removeEventListener("click", fn);
...
this.remover();
これは関数にまとめることができる。
function addHandler(elem, ev, fn) {
elem.addEventListener(ev, fn);
return () => elem.removeEventListener(ev, fn);
}
this.remover = addHandler(document.getElementById("hoge"), "click", ev => { ... });
...
this.remover();
残余引数構文を使うと、面倒くさいオプションまでまとめて面倒を見られる。09 Aug 2024
function addHandler(elem, ...args) {
elem.addEventListener(...args);
return () => elem.removeEventListener(...args);
}
this.remover = addHandler(document.getElementById("hoge"), "click", ev => { ... });
...
this.remover();
イベントというのはいくつかまとめて登録することも多い。
たとえばpointerdown・pointermove・pointerup・pointercancelあたりはまとめて登録削除することが多いだろう。
addHandlerは関数なので、配列の要素として直接書くことができ、
let hoge = document.getElementById("hoge");
this.removers = [
addHandler(hoge, "pointerdown", ev => { ... }),
addHandler(hoge, "pointermove", ev => { ... }),
addHandler(hoge, "pointerup", ev => { ... }),
addHandler(hoge, "pointercancel", ev => { ... }),
];
こうするとひとつ変数を用意するだけで、ハンドラの削除は、
for (let r of this.removers) r();で済む。
まぁきっと世の中誰かが既に発見している気がするが。
23 Apr 2022: 初稿
Copyright (C) 2022 akamoz.jp
$Id: js-remove-listener.htm,v 1.4 2022/11/03 03:07:16 you Exp $