記事を書いたのにremoveEventListenerでまた躓いた。
今回はオブジェクト指向プログラミング版。
失敗例
class X {
click(e) {
// thisがオブジェクトXを指すようにしたい
console.log('click', this, e.currentTarget);
}
add() {
document.addEventListener('click', (e) => this.click(e));
}
remove() {
document.removeEventListener('click', (e) => this.click(e));
}
}
var x = new X();
x.add();
x.remove();
(()=>{}) === (()=>{})
や(function(){}) === (function(){})
がfalseを返すのでremoveEventListenerに無名関数を使用できない。
addEventListenerで指定した関数と同じ関数をremoveEventListenerで指定できていないのでイベントリスナーを削除できない。
イベントリスナー関数にthis.click
を指定するとremoveEventListenerで削除できるが、thisがdocumentを指し、e.currentTargetもdocumentを指すので重複してしまう。
オブジェクト指向プログラミングでイベントリスナー関数は書けない?
ここで小一時間悩んだ。
成功例1 (bind)
class X {
constructor() {
this.event_listeners = {};
}
click(e) {
// thisがオブジェクトXを指すようにしたい
console.log('click', this, e.currentTarget);
}
add() {
this.event_listeners.click = this.click.bind(this);
document.addEventListener('click', this.event_listeners.click);
}
remove() {
document.removeEventListener('click', this.event_listeners.click);
}
}
var x = new X();
x.add();
x.remove();
関数にthisをbind1して、その関数を保存して書けばいいと思う。
成功例2 (handleEvent)
あらゆるイベントを捕捉する handleEvent() という特別な関数が存在した。2
handleEventを使うとどうなるか。
class X {
click(e) {
// thisがオブジェクトXを指すようにしたい
console.log('click', this, e.currentTarget);
}
add() {
document.addEventListener('click', this);
}
remove() {
document.removeEventListener('click', this);
}
handleEvent(e) {
if (e.type === 'click' && e.currentTarget === document) {
this.click(e);
}
}
}
var x = new X();
x.add();
x.remove();
この方法でもイベントリスナーを削除できる
オブジェクトx
のすべてのイベントが1つの関数handleEvent
に集約されるので、オブジェクトのイベントが多くなったときデバッグしやすくなるかもしれない。
-
this.event_listeners.click = (e) => this.click(e)
と書いてもいい。addEventListenerがthisを書き変えるのでthis.event_listeners.click = function (e) { this.click(e) }
とは書けない。 ↩