[javascript] 바인드로 추가 된 이벤트 리스너 제거

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가 말한 것은 사실이지만 이벤트는 같은 방식으로 추가 및 제거되지만 방정식의 누락 부분은 다음과 같습니다.

.bind()호출 된 후 새로운 함수 참조가 생성됩니다 !

참조 합니까 바인드 () 함수 참조를 변경? | 영구적으로 설정하는 방법?

따라서 변수를 추가하거나 제거하려면 변수에 참조를 지정하십시오.

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특정 처리기를 사용하지 않는 서명을 사용할 수 있습니다 (물론 동일한 유형의 다른 이벤트 처리기를 제거하지만) ).

(부수적 addEventListener으로 모든 브라우저에서 작동하지는 않습니다. jQuery와 같은 라이브러리를 사용하여 크로스 브라우저 방식으로 이벤트 훅업을 수행해야합니다. 또한 jQuery에는 네임 스페이스 이벤트 개념이 있습니다. “click.foo”에 바인딩해야합니다. 이벤트를 제거하려면 특정 처리기를 알거나 다른 처리기를 제거하지 않고도 jQuery에 “모든 foo 이벤트 제거”를 지시 할 수 있습니다.)


답변

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. 우리가 해결 한 방법 addEventListenerEventTarget 프로토 타입 .

이것은 객체에 새로운 기능을 추가합니다 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>


답변

해결책은 다음과 같습니다.

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


답변

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

}