bindで追加されたイベントリスナーの削除


164

JavaScriptで、bind()を使用してイベントリスナーとして追加された関数を削除する最良の方法は何ですか?

(function(){

    // constructor
    MyClass = function() {
        this.myButton = document.getElementById("myButtonID");
        this.myButton.addEventListener("click", this.clickListener.bind(this));
    };

    MyClass.prototype.clickListener = function(event) {
        console.log(this); // must be MyClass
    };

    // public method
    MyClass.prototype.disableButton = function() {
        this.myButton.removeEventListener("click", ___________);
    };

})();

私が考えることができる唯一の方法は、bindで追加されたすべてのリスナーを追跡することです。

このメソッドを使用した上記の例:

(function(){

    // constructor
    MyClass = function() {
        this.myButton = document.getElementById("myButtonID");
        this.clickListenerBind = this.clickListener.bind(this);
        this.myButton.addEventListener("click", this.clickListenerBind);
    };

    MyClass.prototype.clickListener = function(event) {
        console.log(this); // must be MyClass
    };

    // public method
    MyClass.prototype.disableButton = function() {
        this.myButton.removeEventListener("click", this.clickListenerBind);
    };

})();

これを行うためのより良い方法はありますか?


2
あなたは何を除いてやっているthis.clickListener = this.clickListener.bind(this);this.myButton.addEventListener("click", this.clickListener);
Esailija

とてもいいです。これは別のトピックかもしれませんが、 "this"キーワードを使用する残りのメソッドに対してbind(this)を実行する必要があるかどうかを疑問に思いました。
takfuruya

後で削除するかどうかに関係なく、どこかに渡されるすべてのメソッドのコンストラクターで最初にこれを常に実行します。ただし、すべてのメソッドについてではなく、渡されるメソッドについてのみです。
エサイリヤ

あなたがしていることは理にかなっています。ただし、これがライブラリの一部である場合、たとえば、どのMyClassのメソッド(「パブリック」として文書化されている)が渡されるかを知ることはできません。
takfuruya '19 / 07/19

参考までに、Underscoreライブラリには、bindAllバインディングメソッドを簡略化する関数があります。オブジェクト初期化子の内部では、オブジェクト内の_.bindAll(this)すべてのメソッドをバインドされたバージョンに設定するだけです。あるいは、いくつかのメソッドのみをバインドしたい場合(偶発的なメモリリークを防ぐために、これをお勧めします)、それらを引数として提供できます_.bindAll(this, "foo", "bar") // this.baz won't be bound
machineghost 2012

回答:


274

@machineghostが言ったことは真実ですが、そのイベントは同じ方法で追加および削除されますが、方程式の欠けている部分は次のとおりです。

.bind()が呼び出された後に新しい関数参照が作成されます!

参照んバインド()関数の参照を変更?| 恒久的に設定するには?

したがって、追加または削除するには、変数への参照を割り当てます。

var x = this.myListener.bind(this);
Toolbox.addListener(window, 'scroll', x);
Toolbox.removeListener(window, 'scroll', x);

これは期待どおりに機能します。


4
すばらしい、これは受け入れられる答えになるはずです。古いトピックを更新してくれてありがとう。このトピックは検索エンジンで一番ヒットし、今これを投稿するまで適切な解決策がありませんでした。
Blargh、2014

これは、質問で述べた方法と何ら変わりはありません(それよりも優れているわけではありません)。
Peter Tseng

私は理解できません、クリックイベントでどのように機能させるのですか、ありがとう
AlbertoAcuña'25年

@AlbertoAcuña最近のブラウザーは.addEventListener(type, listener)、と.removeEventListener(type, listener)を使用して、要素のイベントを追加および削除します。どちらの場合も、ソリューションで説明されている関数参照をlistenerパラメーター"click"として、型として渡すことができます。developer.mozilla.org/en-US/docs/Web/API/EventTarget/...
ベン・

1
これは私に役立ちますが、この回答は4年前に投稿されました:)
user2609021 2018

46

ReactコンポーネントのリスナーをFluxストアに登録またはFluxストアから削除するときにこの問題が発生する場合は、コンポーネントのコンストラクターに以下の行を追加してください。

class App extends React.Component {
  constructor(props){
    super(props);
    // it's a trick! needed in order to overcome the remove event listener
    this.onChange = this.onChange.bind(this);  
  }
  // then as regular...
  componentDidMount (){
    AppStore.addChangeListener(this.onChange);
  }
  
  componentWillUnmount (){
    AppStore.removeChangeListener(this.onChange);
  }

  onChange () {
    let state = AppStore.getState();
    this.setState(state);
  }
  
  render() {
    // ...
  }
  
}


7
素敵なトリックですが、React / Fluxは何と関係があるのでしょうか。
Peter Tseng、

これは、さまざまなクラスまたはプロトタイプ関数からイベントリスナーを追加および削除する場合の正しいアプローチのようです。これは、Reactコンポーネント/クラスにも適用されると私は考えています。共通(ルートなど)のインスタンスレベルでバインドしています。
キースDC

1
this.onChange = this.onChange.bind(this)これは私が探していたものです。上に結合機能this永遠に:)
パヴェル

2

バインドされた関数を使用するかどうかは関係ありません。他のイベントハンドラーと同じ方法で削除します。バインドされたバージョンが独自の関数であることが問題である場合は、バインドされたバージョンを追跡するかremoveEventListener、特定のハンドラーをとらないシグネチャを使用できます(もちろん、同じタイプの他のイベントハンドラーは削除されます) )。

(補足として、addEventListenerすべてのブラウザで機能するわけではありません。ブラウザ間でイベントのフックアップを行うには、jQueryのようなライブラリを実際に使用する必要があります。また、jQueryには、名前空間付きのイベントという概念があります。 「click.foo」にバインドします。イベントを削除したい場合は、特定のハンドラーを知らなくても、他のハンドラーを削除しなくても、jQueryに「remove all foo events」を通知できます。


IEの問題を認識しています。キャンバスに大きく依存するアプリケーションを開発しているため、IE7-は公開されていません。IE8はキャンバスをサポートしますが、最低限です。IE9 +はaddEventListenerをサポートしています。jQueryの名前空間イベントは非常にきれいに見えます。私が心配しているのは効率だけです。
takfuruya '19 / 07/19

jQueryの人々は、ライブラリのパフォーマンスを維持するために非常に熱心に取り組んでいるので、それほど心配する必要はありません。ただし、厳しいブラウザー要件を前提として、代わりにZeptoをチェックアウトすることをお勧めします。これは、jQueryの縮小バージョンのようなものですが、高速ですが、古いブラウザーをサポートできません(他にもいくつか制限があります)。
machineghost 2012

jQuery名前空間付きイベントは広く使用されており、実質的にパフォーマンスの問題はありません。特にJQueryに対する不合理な恐怖と想像上のパフォーマンスの懸念からそうする場合、コードをより簡単に、そして(おそらくより重要なことに)理解しやすくするツールを使用しないように誰かに伝えることは、恐ろしいアドバイスになります。
machineghost

1
それはどの署名ですか? removeEventListenerのMDNページは、最初の2つの引数の両方が必要であることを示しています。
Coderer、2018

私の間違い。その答えを書いてから数年が経ちましたが、私はjQuery offunbindメソッドについて考えていたに違いありません。要素のすべてのリスナーを削除するには、追加されたリスナーを追跡する必要があります(jQueryまたは他のライブラリーでできることです)。
machineghost

1

jQueryソリューション:

let object = new ClassName();
let $elem = $('selector');

$elem.on('click', $.proxy(object.method, object));

$elem.off('click', $.proxy(object.method, object));

1

変更できないライブラリでこの問題が発生しました。Office Fabric UI。つまり、イベントハンドラーの追加方法を変更できませんでした。私たちがそれを解決した方法はaddEventListenerEventTargetプロトタイプでを上書きすることでした。

これにより、オブジェクトに新しい関数が追加されます element.removeAllEventListers("click")

(元の投稿:ファブリックダイアログオーバーレイからクリックハンドラーを削除

        <script>
            (function () {
                "use strict";

                var f = EventTarget.prototype.addEventListener;

                EventTarget.prototype.addEventListener = function (type, fn, capture) {
                    this.f = f;
                    this._eventHandlers = this._eventHandlers || {};
                    this._eventHandlers[type] = this._eventHandlers[type] || [];
                    this._eventHandlers[type].push([fn, capture]);
                    this.f(type, fn, capture);
                }

                EventTarget.prototype.removeAllEventListeners = function (type) {
                    this._eventHandlers = this._eventHandlers || {};
                    if (type in this._eventHandlers) {
                        var eventHandlers = this._eventHandlers[type];
                        for (var i = eventHandlers.length; i--;) {
                            var handler = eventHandlers[i];
                            this.removeEventListener(type, handler[0], handler[1]);
                        }
                    }
                }

                EventTarget.prototype.getAllEventListeners = function (type) {
                    this._eventHandlers = this._eventHandlers || {};
                    this._eventHandlers[type] = this._eventHandlers[type] || [];
                    return this._eventHandlers[type];
                }

            })();
        </script>

0

これが解決策です:

var o = {
  list: [1, 2, 3, 4],
  add: function () {
    var b = document.getElementsByTagName('body')[0];
    b.addEventListener('click', this._onClick());

  },
  remove: function () {
    var b = document.getElementsByTagName('body')[0];
    b.removeEventListener('click', this._onClick());
  },
  _onClick: function () {
    this.clickFn = this.clickFn || this._showLog.bind(this);
    return this.clickFn;
  },
  _showLog: function (e) {
    console.log('click', this.list, e);
  }
};


// Example to test the solution
o.add();

setTimeout(function () {
  console.log('setTimeout');
  o.remove();
}, 5000);

0

ES7について使用できます。

class App extends React.Component {
  constructor(props){
    super(props);
  }
  componentDidMount (){
    AppStore.addChangeListener(this.onChange);
  }

  componentWillUnmount (){
    AppStore.removeChangeListener(this.onChange);
  }

  onChange = () => {
    let state = AppStore.getState();
    this.setState(state);
  }

  render() {
    // ...
  }

}

-1

上記のように 'onclick'を使用する場合は、次のようにしてみてください。

(function(){
    var singleton = {};

    singleton = new function() {
        this.myButton = document.getElementById("myButtonID");

        this.myButton.onclick = function() {
            singleton.clickListener();
        };
    }

    singleton.clickListener = function() {
        console.log(this); // I also know who I am
    };

    // public function
    singleton.disableButton = function() {
        this.myButton.onclick = "";
    };
})();

お役に立てば幸いです。


-2

それはしばらくの間ですが、MDNはこれについて非常に説明しています。それはここにあるもの以上に私を助けました。

MDN :: EventTarget.addEventListener-ハンドラー内の「this」の値

これは、handleEvent関数の優れた代替手段を提供します。

これはバインドありとなしの例です。

var Something = function(element) {
  this.name = 'Something Good';
  this.onclick1 = function(event) {
    console.log(this.name); // undefined, as this is the element
  };
  this.onclick2 = function(event) {
    console.log(this.name); // 'Something Good', as this is the binded Something object
  };
  element.addEventListener('click', this.onclick1, false);
  element.addEventListener('click', this.onclick2.bind(this), false); // Trick
}

上記の例の問題は、バインドでリスナーを削除できないことです。別の解決策は、handleEventと呼ばれる特別な関数を使用してイベントをキャッチすることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.