删除绑定中添加的事件监听器

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


})();

还有什么更好的办法吗?

73411 次浏览

是否使用有界函数并不重要;您可以像删除任何其他事件处理程序一样删除它。如果你的问题是绑定版本是它自己的唯一函数,你可以跟踪绑定版本,或者使用不接受特定处理程序的removeEventListener签名(当然,这将删除相同类型的其他事件处理程序)。

(顺便说一句,addEventListener并不适用于所有浏览器;你真的应该使用像jQuery这样的库来跨浏览器地为你做你的事件连接。此外,jQuery还有命名空间事件的概念,它允许你绑定到“click.foo”;当你想删除事件时,你可以告诉jQuery“删除所有foo事件”,而不需要知道特定的处理程序或删除其他处理程序。)

如果你想使用'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 = "";
};
})();

我希望这能有所帮助。

虽然@machineghost说的是真的,事件以相同的方式添加和删除,但等式中缺失的部分是:

.bind()被调用之后,一个新的函数引用被创建。

看到bind()会改变函数引用吗?|如何永久设置?

因此,要添加或删除它,将引用赋值给一个变量:

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

这对我来说就像预期的那样。

已经有一段时间了,但MDN对此有一个超级解释。那比这里的东西更有用。

MDN:: EventTarget。addEventListener -处理程序中"this"的值

它为handleEvent函数提供了一个很好的替代方案。

这是一个带和不带bind的例子:

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
}

上面示例中的一个问题是不能使用bind删除侦听器。另一个解决方案是使用一个叫做handleEvent的特殊函数来捕捉任何事件:

对于那些在从Flux store中注册/删除React组件的监听器时遇到这个问题的人,请将以下代码添加到组件的构造函数中:

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

}

下面是解决方案:

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

jQuery的解决方案:

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


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


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

我们有一个无法更改的库的问题。Office Fabric UI,这意味着我们不能更改添加事件处理程序的方式。我们解决它的方法是在EventTarget原型上覆盖addEventListener

这将在对象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>

可以使用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() {
// ...
}


}

正如其他人所说,bind创建了一个新的函数实例,因此事件监听器不能被删除,除非以某种方式记录它。

为了获得更漂亮的代码风格,你可以将方法函数设置为惰性getter,以便在第一次访问时自动替换为绑定版本:

class MyClass {
activate() {
window.addEventListener('click', this.onClick);
}


deactivate() {
window.removeEventListener('click', this.onClick);
}


get onClick() {
const func = (event) => {
console.log('click', event, this);
};
Object.defineProperty(this, 'onClick', {value: func});
return func;
}
}

如果ES6箭头函数不受支持,请使用const func = (function(event){...}).bind(this)代替const func = (event) => {...}

Raichman Sergey的方法也很好,尤其是在课堂上。这种方法的优点是它更加自我完整,没有其他地方的分离代码。它也适用于没有构造函数或启动器的对象。