Usuwanie detektora zdarzeń, który został dodany za pomocą bind

W JavaScript, jaki jest najlepszy sposób na usunięcie funkcji dodanej jako detektor zdarzeń za pomocą bind ()?

Przykład

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

})();

Jedyny sposób, jaki mogę wymyślić, to śledzenie każdego słuchacza dodanego za pomocą bind.

Powyższy przykład z tą metodą:

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

})();
Czy są na to jakieś lepsze sposoby?
Author: takfuruya, 2012-07-19

8 answers

Chociaż to, co powiedział @machineghost, było prawdą, że zdarzenia są dodawane i usuwane w ten sam sposób, brakującą częścią równania było to:

Po wywołaniu .bind() tworzy się nowe odniesienie do funkcji!

Zobacz czy bind() zmienia odniesienie do funkcji? / Jak ustawić na stałe?

Więc, aby dodać lub usunąć, Przypisz odniesienie do zmiennej:

var x = this.myListener.bind(this);
Toolbox.addListener(window, 'scroll', x);
Toolbox.removeListener(window, 'scroll', x);
To działa zgodnie z oczekiwaniami.
 208
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:41

Dla tych, którzy mają ten problem podczas rejestracji / usuwania detektora komponentu Reactowego do / ze sklepu Flux, dodaj poniższe linie do konstruktora komponentu:

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() {
    // ...
  }
  
}
 37
Author: Raichman Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-15 21:10:52

Nie ma znaczenia, czy używasz funkcji bound, czy nie; usuwasz ją w taki sam sposób, jak każdy inny mechanizm obsługi zdarzenia. Jeśli problem polega na tym, że powiązana wersja jest własną, unikalną funkcją, możesz albo śledzić powiązane wersje, albo użyć podpisu removeEventListener, który nie wymaga określonej procedury obsługi (chociaż oczywiście spowoduje to usunięcie innych procedur obsługi zdarzeń tego samego typu).

(na marginesie, addEventListener nie działa we wszystkich przeglądarkach; naprawdę powinieneś użyć biblioteki takiej jak jQuery, aby zrobić swoje łączenie zdarzeń w sposób między przeglądarkowy dla Ciebie. Ponadto, jQuery ma pojęcie zdarzeń przestrzeni nazw, które pozwalają powiązać " kliknij.foo"; gdy chcesz usunąć zdarzenie, możesz powiedzieć jQuery "usuń wszystkie zdarzenia foo" bez konieczności znajomości konkretnego programu obsługi lub usuwania innych programów obsługi.)

 4
Author: machineghost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-20 14:35:29

Oto rozwiązanie:

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
Author: Nazar Vynnytskyi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-14 09:32:27

JQuery solution:

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

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

$elem.off('click', $.proxy(object.method, object));
 0
Author: Ed Kolosovsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 10:23:04

Jeśli chcesz użyć 'onclick', jak sugerowano powyżej, możesz spróbować tego:

(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 = "";
    };
})();
Mam nadzieję, że to pomoże.
 -1
Author: Diogo Schneider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 17:28:16

Jest rok 2016, a standard DOM nie pomógł wiele w rozwiązaniu dość częstego problemu, z którym mamy do czynienia od czasu do czasu.

Tak jedynym sposobem na usunięcie ograniczonego mechanizmu obsługi zdarzeń jest zachowanie odniesienia do ograniczonej funkcji i użycie go w removeEventListener, jak opisano w innych rozwiązaniach tutaj.

Jednak gdy masz wielu słuchaczy robi się bałagan. Żaden z nich nie wymyślił prostych funkcji, które abstrahują od konieczności utrzymywania odniesień do funkcji ograniczonych. Wymyśliłem dwie funkcje o nazwie on () i off () (nazwy inspirowane jQuery), które dodałem do wszystkich HTMLElements, dodając je do prototypu. kod jest tutaj . (Działa tylko na IE 11 + ponieważ używa WeakMap)

Więc używając tego, możesz dodawać słuchacze zdarzeń i usuwać je w ten sposób:

this.myButton.on('click', this.clickListener, this);
this.myButton.off('click', this.clickListener, this); //yup, it's removed

Szczegółów realizacji i podjętych decyzji jest wiele, więc nie będę tego wyjaśniał:)

Jeśli nie podoba Ci się dodawanie funkcji do obiektów natywnych, możesz to osiągnąć edytując mój kod trochę. (Ale poważnie, standard DOM powinien był dodać jakieś API, aby rozwiązać to dla nas w pierwszej kolejności).

 -1
Author: Munawwar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-05 17:51:04

Minęło trochę czasu, ale MDN ma super Wyjaśnienie na ten temat. To pomogło mi bardziej niż te rzeczy tutaj.

MDN:: EventTarget.addEventListener-wartość "this" wewnątrz obsługi

Daje świetną alternatywę dla funkcji handleEvent.

To jest przykład z i bez 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
}

Problem w powyższym przykładzie polega na tym, że nie można usunąć słuchacza za pomocą bind. Innym rozwiązaniem jest użycie specjalnej funkcji o nazwie handleEvent do łapania dowolne zdarzenia:

 -2
Author: Noitidart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 06:30:08