removeEventListener が効かない・消えないときの原因&対処法

JavaScript を使ってるとイベント関係で次のトラブルに悩まさること多いです。

  1. addEventListenrでリスナー登録
  2. removeEventListenerで解除する
  3. アレ?なぜか削除が効いてない...?

ここでは個人的にハマってしまった、
JSでの removeEventListenerが効かない・消えないときの対処法 を紹介!

特に jQuery とかが使えない環境だと、イベントリスナーには要注意です。

なぜ? removeEventListener が効かない問題に遭遇

ごくまれに jQuery とかライブラリが一切使えない場合・・・

そのときはイベントリスナー登録に次の2つを使わないといけなくなります。

  • addEventListener
    イベントリスナーを追加する。上書きではなく新規追加
  • removeEventListener
    イベントリスナーを削除する。追加したリスナー関数を渡す必要あり

問題なのは removeEventListenr  を使ってイベントを削除する場合です。

 

そもそもこの関数、次みたいなものだと説明されています。

▼ MDNでの removeEventListener の解説

EventTarget.removeEventListener() メソッドは、 EventTarget から、以前に EventTarget.addEventListener() で登録されたイベントリスナーを削除します。削除されるイベントリスナーはイベントの型、イベントリスナー関数そのもの、マッチングプロセスに影響を与えるさまざまな任意のオプションを使用して識別します。

引用元 : EventTarget.removeEventListener - JavaScript | MDN

上の説明の EventTarget  とはすなわちクリックとかされる要素のこと

例えばこの関数の簡単な使い方は次みたいな感じです。

▼ すっごく単純化したコード

このコード自体は何の問題もなく動きます。でも複雑なコードだったり、 addEventListener  の設定次第では期待通りに動かないことがあるのが厄介ですね。

特に僕個人がハマってしまったのは次の2パターン

原因1.登録時と削除時の useCapture の値が違うから

まず1つめの原因はコレ

リスナー登録時と削除時の useCapture の値が違うから

この useCapture  はイベントの伝播のされ方を変えるフラグのことです。

  • useCaputure : trueの場合
    イベント発生元の一番上位の要素 => イベント元要素 の順にイベントが伝わる
  • useCapture : falseの場合
    イベント発生元 => イベント発生元の上位要素 の順にイベントが伝わる

デフォルト値は false  なので、この順番を気にしてる人は少ないかもしれません。

でも removeEventListener  を使う時は、この useCapture の値が問題になります。

 

問題になるのは 登録時の useCapture の値と、削除時の値が違うときです。

▼ 例えば分かりやすいコードで示すなら以下の通り

ハイライトした部分に注目

こういう風に useCapture の値が一致してないとイベント削除が全く効かなくなるみたいです。

 

なので useCapture の値を一致させればOK

▼ こういうコードに直してみた

※ 2021/05/08 : コメントのご指摘があり、間違いを修正。

これでちゃんとイベント削除が働くようになりました。

他人のコードを読んだり書いたりするときは、useCapture の値に注意ですね。

原因2.アロー関数(無名関数)を渡してしまってるから

もう1つ原因があって、

それがアロー関数を直接渡してしまってるから

そもそも removeEventListener  はイベントハンドラーを指定することで削除ができます。なので無名関数を渡しても削除はできないという訳です。

 

たとえば removeEventListener  が効かないのはこんな場合です。

▼ クリックリスナーが消えないBADコード例

こういうコードもNG

アロー関数の時点で addEventListener  に渡したものと全く別のインスタンスができるので、いくら removeEventListenr  を呼び出しても無駄になります。

 

対処法としてはアロー関数を変数として保存すればOK

▼ 改善したコード例

2~4行目の部分がアロー関数を保存しているコード

とにかく素の関数とかアロー関数を渡さないことが大事です。

ここまでのまとめ

最後に removeEventListener  が効かない原因(対処法)まとめ

  1. 削除時の useCapture の値が違うから
    これはイベント削除が動かない原因でありがち。もし追加時に true  を設定しているなら削除時も true を渡す必要あり
  2. アロー関数を渡してしまってるから
    削除するコールバック関数は追加時と削除時で一致させないとダメ。なので関数オブジェクトをそのまま渡すのはNG

以上、 removeEventListener  で消えない・効かないときの対処法でした。

Shareこの記事をシェアしよう!

Commentsこの記事についたコメント

4件のコメント
  • さかい

    > 原因1.登録時と削除時の useCapture の値が違うから

    上記の原因1のremoveEventListenerの箇所で
    「こういうコードに直してみた」の方も
    useCaptureの値がfalseになっているように見えるのですが、
    これは合っているのでしょうか?

    これだと、addEventListenerのuseCaptureの値(true)と一致してないように見えまして。

    btnAlert.removeEventListener(‘click’, showAlert, false);

    5月 7, 2021 10:02 pm
    • ぴー助

      まさしくご指摘の通りです。
      次のように一致させる必要があります。

      btnAlert.removeEventListener(‘click’, showAlert, true);

      記事の方も修正しておこうと思います。
      コメントありがとうございました。

      5月 8, 2021 7:16 am
  • さかい

    > 原因1.登録時と削除時の useCapture の値が違うから

    上記の原因1のremoveEventListenerの箇所で
    「こういうコードに直してみた」の方も
    useCaptureの値がfalseになっているように見えるのですが、
    これは合っているのでしょうか?

    これだと、addEventListenerのuseCaptureの値(true)と一致してないように見えまして。

    ・02行目:btnAlert.addEventListener(‘click’, showAlert, true);
    ・12行目:btnAlert.removeEventListener(‘click’, showAlert, false);

    5月 7, 2021 10:28 pm
  • さかい

    ご確認いただきありがとうございます。

    5月 8, 2021 5:06 pm

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください