web-dev-qa-db-ja.com

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

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", ___________);
    };

})();

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

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

(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);
    };

})();

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

141
takfuruya

@machineghostが言ったことは真実でしたが、そのイベントは同じ方法で追加および削除されましたが、方程式の欠落した部分はこれでした:

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

bind()は関数参照を変更しますか?|永続的に設定する方法? を参照してください。

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

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

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

244
Ben

Reactコンポーネントのリスナーを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() {
    // ...
  }
  
}
40
Raichman Sergey

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

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

3
machineghost

jQueryソリューション:

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

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

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

これが解決策です:

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

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

これにより、オブジェクトに新しい関数が追加されます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
Peter

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() {
    // ...
  }

}
0
chiic