Удаление прослушивателя событий, добавленного с помощью bind

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

})();

есть ли лучшие способы сделать это?

8 ответов


хотя то, что сказал @machineghost, было правдой, что события добавляются и удаляются одинаково, недостающая часть уравнения была такой:

новая ссылка на функцию создается после .bind() называется!

посмотреть изменяет ли bind () ссылку на функцию? | Как установить постоянно?

Итак, чтобы добавить или удалить его, назначьте ссылку на переменную:

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

это работает, как ожидалось для меня.


для тех, у кого есть эта проблема при регистрации / удалении прослушивателя компонента React в / из Flux store, добавьте следующие строки в конструктор вашего компонента:

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 подпись, которая не принимает определенного обработчика (хотя, конечно, это удалит другие обработчики событий того же типа).

(в качестве примечания,addEventListener не работает во всех браузерах; вы действительно должны использовать библиотеку как jQuery, чтобы сделать ваши подключения событий в кросс-браузере для вас. Кроме того, jQuery имеет концепцию событий пространства имен, которые позволяют привязывать к "щелчку".foo"; когда вы хотите удалить событие, вы можете сказать jQuery "удалить все события foo" без необходимости знать конкретный обработчик или удалять другие обработчики.)


вот решение:

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

Если вы хотите использовать "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 не очень помог в решении довольно распространенной проблемы, с которой мы сталкиваемся время от времени.

да единственный способ удалить обработчик ограниченных событий-сохранить ссылку на ограниченную функцию и использовать ее в removeEventListener, как указано в других решениях здесь.

однако, когда у вас есть много слушателей, это становится грязным. Никто не придумал простых функций, которые абстрагируют работу по сохранению ссылок на ограниченные функции. Я придумал две функции с именами on () и off () (имена, вдохновленные jQuery), которые я добавил ко всем HTMLElements, добавив его в прототип. код. (Работает только на IE 11+, так как он использует WeakMap)

таким образом, используя это, вы можете добавить прослушиватели событий и удалить их так:

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.

Это пример с и без привязки:

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, чтобы поймать любые события: