イベントハンドラの登録でアロー関数式を使いたいという状況が結構ある。
一番大きいのは、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 $