除去使用绑定添加的事件侦听器
在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); }; })();
有没有更好的方法来做到这一点?
虽然@machineghost说的是真实的,事件也是以同样的方式join和删除的,但方程式的缺失部分是这样的:
调用
.bind()
后创build一个新的函数引用!
请参阅bind()是否更改函数引用? | 如何永久设置?
因此,要添加或删除它,请将引用分配给一个variables:
var x = this.myListener.bind(this); Toolbox.addListener(window, 'scroll', x); Toolbox.removeListener(window, 'scroll', x);
这符合我的预期。
对于那些在向Flux存储库注册/删除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() { // ... } }
是否使用绑定函数并不重要; 您可以像删除其他事件处理程序一样删除它。 如果你的问题是绑定版本是它自己的唯一函数,你可以跟踪绑定的版本,或者使用不带特定处理程序的removeEventListener
签名(尽pipe当然会删除相同的事件处理程序types)。
(注意, addEventListener
在所有的浏览器中都不起作用;你应该使用像jQuery这样的库来以跨浏览器的方式为你做事件连接,另外,jQuery有名称空间事件的概念,允许你绑定到“click.foo”;当你想删除事件,你可以告诉jQuery“删除所有foo事件”,而不必知道具体的处理程序或删除其他处理程序。
这是解决scheme:
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);
如果你想使用'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 = ""; }; })();
我希望它有帮助。
现在是2016年,DOM标准在解决我们偶尔碰到的一个很常见的问题方面没有多大帮助。</ rant>
是的,删除有界事件处理程序的唯一方法是保留对有界函数的引用,并在removeEventListener上使用它,如其他解决scheme中所述。
然而,当你有很多听众,它会变得混乱。 没有人提出简单的函数来抽象出不得不引用有界函数的工作。 我想出了两个名为on()和off()(jQuery启发的名字)的函数,我将它们添加到原型中,添加到所有HTMLElements中。 代码在这里 。 (因为它使用WeakMap,所以只能在IE 11+上运行)
所以使用它,你可以添加事件监听器,并像这样删除它们:
this.myButton.on('click', this.clickListener, this); this.myButton.off('click', this.clickListener, this); //yup, it's removed
实施的细节和决定很多,所以我不会解释它:)
如果您不喜欢将函数添加到本机对象,那么您可以通过编辑我的代码来实现这一点。 (但是,严格地说,DOM标准应该为我们添加一些API来解决这个问题。
已经有一段时间了,但MDN对此有一个超级解释。 这帮助我比这里的东西更多。
MDN :: EventTarget.addEventListener – 处理程序中的“this”的值
它为handleEvent函数提供了一个很好的select。
这是一个有和没有绑定的例子:
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 }
上面的例子中的一个问题是,你不能通过绑定来移除监听器。 另一个解决scheme是使用一个名为handleEvent的特殊函数来捕获任何事件: