Удаление прослушивателя событий, который был добавлен с привязкой
В 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);
};
})();
Есть ли лучшие способы сделать это?
Ответы
Ответ 1
Хотя то, что @machineghost было правдой, что события добавляются и удаляются одинаково, недостающая часть уравнения такова:
Новая ссылка на функцию создается после вызова .bind()
!
См. Связано ли bind() с ссылкой на функцию? | Как установить навсегда?
Итак, чтобы добавить или удалить его, назначьте ссылку на переменную:
var x = this.myListener.bind(this);
Toolbox.addListener(window, 'scroll', x);
Toolbox.removeListener(window, 'scroll', x);
Это работает как ожидалось для меня.
Ответ 2
Для тех, у кого есть эта проблема при регистрации/удалении слушателя компонента React в/из хранилища Flux, добавьте строки ниже в конструктор вашего компонента:
class App extends React.Component {
constructor(props){
super(props);
// it 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() {
// ...
}
}
Ответ 3
Неважно, используете ли вы связанную функцию или нет; вы удаляете его так же, как любой другой обработчик событий. Если ваша проблема связана с тем, что связанная версия является ее собственной уникальной функцией, вы можете либо отслеживать связанные версии, либо использовать подпись removeEventListener
, которая не принимает конкретного обработчика (хотя, конечно, это приведет к удалению других обработчиков событий тот же тип).
(В качестве стороннего примечания addEventListener
не работает во всех браузерах, вам действительно нужно использовать библиотеку jQuery, чтобы сделать ваши подключения к событиям кросс-браузерным способом для вас. Кроме того, у jQuery есть концепция событий, связанных с именами, которые позволяют вам привязываться к "click.foo", когда вы хотите удалить событие, вы можете сказать jQuery "удалить все события foo", не зная конкретного обработчика или удаления других обработчиков.)
Ответ 4
Решение jQuery:
let object = new ClassName();
let $elem = $('selector');
$elem.on('click', $.proxy(object.method, object));
$elem.off('click', $.proxy(object.method, object));
Ответ 5
<rant> It 2016 и стандарт DOM не помогли в решении довольно распространенной проблемы, с которой мы сталкиваемся время от времени. </rant>
Да, единственный способ удалить ограничиваемый обработчик событий - сохранить ссылку на ограниченную функцию и использовать ее на removeEventListener, как указано в других решениях здесь.
Однако, когда у вас много слушателей, это становится беспорядочным. Ни один из них не придумал простые функции, которые абстрагируют работу по сохранению ссылок на ограниченные функции. Я придумал две функции с именем on() и off() (jQuery вдохновил имена), которые я добавил ко всем элементам HTMLElements, добавив их в прототип. Код здесь. (Работает только с IE 11+, поскольку использует WeakMap)
Таким образом, вы можете добавить прослушиватели событий и удалить их так:
this.myButton.on('click', this.clickListener, this);
this.myButton.off('click', this.clickListener, this); //yup, it removed
Детали реализации и принятые решения много, поэтому я не буду объяснять это:)
Если вам не нравится добавлять функции к родным объектам, вы можете добиться этого, немного изменив мой код. (Но серьезно, стандарт DOM должен был добавить API для решения этой проблемы в первую очередь).
Ответ 6
Вот решение:
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);
Ответ 7
У нас была эта проблема с библиотекой, которую мы не могли изменить. Пользовательский интерфейс Office Fabric, что означало, что мы не могли изменить способ добавления обработчиков событий. Мы решили это путем перезаписи 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>
Ответ 8
Если вы хотите использовать "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 = "";
};
})();
Надеюсь, это поможет.
Ответ 9
Было немного, но 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, чтобы поймать любые события: