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


164

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

})();

더 좋은 방법이 있습니까?


2
당신이하고있는 것을 제외 this.clickListener = this.clickListener.bind(this);하고this.myButton.addEventListener("click", this.clickListener);
Esailija

매우 좋군. 이것은 다른 주제 일 수 있지만 메소드 호출을 비효율적으로 만들더라도 "this"키워드를 사용하는 나머지 메소드에 대해 bind (this)를 수행 해야하는지 궁금합니다.
takfuruya

나는 나중에 제거할지 여부에 관계없이 항상 어딘가에 전달 될 모든 메소드에 대해 생성자에서 첫 번째 작업 으로이 작업을 수행합니다. 그러나 모든 방법이 아니라 통과 된 방법 만 있습니다.
Esailija

당신이하고있는 일이 의미가 있습니다. 그러나 이것이 예를 들어 라이브러리의 일부인 경우 어떤 MyClass '메소드 ( "공용"으로 문서화)가 전달 될지 알 수 없습니다.
takfuruya

참고로 Underscore 라이브러리에는 bindAll바인딩 방법을 단순화 하는 기능이 있습니다. 객체 이니셜 라이저 내에서 객체의 _.bindAll(this)모든 메소드를 바인딩 된 버전으로 설정하기 만하면 됩니다. 또는 실수로 메모리 누수를 방지하기 위해 일부 방법 만 바인딩하려는 경우 인수로 제공 할 수 있습니다 _.bindAll(this, "foo", "bar") // this.baz won't be bound.
machineghost

답변:


274

@machineghost가 말한 것은 사실이지만 이벤트는 같은 방식으로 추가 및 제거되지만 방정식의 누락 부분은 다음과 같습니다.

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

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

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

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

이것은 나에게 예상대로 작동합니다.


4
훌륭합니다.이 답변은 받아 들여야합니다. 오래된 주제를 업데이트 해 주셔서 감사합니다.이 주제는 검색 엔진에서 가장 인기있는 것으로 나타 났으며 지금 게시 할 때까지 적절한 해결책이 없었습니다.
Blargh

이것은 질문에 언급 된 방법과 다르지 않습니다.
Peter Tseng 2:12에

이해가 안 돼요, 클릭 이벤트에서 어떻게 작동합니까?
Alberto Acuña

@ AlbertoAcuña 현대의 브라우저를 사용 .addEventListener(type, listener)하고 .removeEventListener(type, listener)요소에 이벤트를 추가하고 제거 할 수 있습니다. 두 가지 모두 솔루션에 설명 된 함수 참조를 유형 listener과 함께 매개 변수 로 전달할 수 있습니다 "click". developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
Ben

1
이 답변은 4 년 전에 게시 된이 답변에도 도움이됩니다. :)
user2609021

46

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


7
좋은 속임수이지만 React / Flux는 무엇과 관련이 있습니까?
Peter Tseng 2:12에

이것은 다른 클래스 또는 프로토 타입 함수에서 이벤트 리스너를 추가하고 제거 할 때 올바른 접근 방식으로 보입니다.이 연결은 React 구성 요소 / 클래스에도 적용된다고 생각합니다. 공통 (예 : 루트) 인스턴스 수준에서 바인딩합니다.
Keith DC

1
this.onChange = this.onChange.bind(this)실제로 이것은 내가 찾던 것입니다. 이 기능 this은 영원히 계속됩니다 :)
Paweł

2

바운드 함수 사용 여부는 중요하지 않습니다. 다른 이벤트 핸들러와 동일한 방식으로 제거합니다. 바인딩 된 버전이 고유 한 기능이라는 문제가있는 경우 바인딩 된 버전을 추적하거나 removeEventListener특정 처리기를 사용하지 않는 서명을 사용할 수 있습니다 (물론 동일한 유형의 다른 이벤트 처리기를 제거하지만) ).

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


IE 문제를 알고 있습니다. IE7-이 종료되도록 캔버스에 크게 의존하는 응용 프로그램을 개발 중입니다. IE8은 최소 캔버스를 지원합니다. IE9 +는 addEventListener를 지원합니다. jQuery의 네임 스페이스 이벤트는 매우 깔끔해 보입니다. 내가 걱정하는 유일한 것은 효율성입니다.
takfuruya

jQuery 사람들 은 라이브러리의 성능을 유지하기 위해 매우 열심히 노력하므로 너무 걱정하지 않아도됩니다. 그러나 엄격한 브라우저 요구 사항이있는 경우 대신 Zepto를 확인하고 싶을 수 있습니다. 그것은 더 빠르지 만 이전 브라우저를 지원할 수 없으며 다른 제한이있는 jQuery의 축소 버전과 같습니다.
machineghost

JQuery 네임 스페이스 이벤트는 널리 사용되며 성능 문제가 거의 없습니다. 누군가가 코드를 더 쉽고 이해하기 쉽도록 만드는 도구를 사용하지 말라고 말하는 것은 특히 JQuery에 대한 비합리적인 두려움과 상상의 성능 문제에 대해 그렇게한다면 끔찍한 조언이 될 것입니다.
machineghost

1
어떤 서명이 될까요? removeEventListener의 MDN 페이지 는 처음 두 인수가 모두 필요하다는 것을 보여줍니다.
Coderer

내 실수. 그 대답을 쓴 지 몇 년이 지났지 만 jQuery off또는 unbind메소드를 생각하고 있었을 것 입니다. 요소에서 모든 리스너를 제거하려면 추가 될 때 리스너를 추적해야합니다 (jQuery 또는 다른 라이브러리가 할 수있는 일).
machineghost

1

jQuery 솔루션 :

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

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

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

1

변경할 수없는 라이브러리에서이 문제가 발생했습니다. 이벤트 처리기가 추가되는 방식을 변경할 수없는 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>

0

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

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

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

}

-1

위에서 제안한대로 '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 = "";
    };
})();

도움이 되길 바랍니다.


-2

한참이 지났지 만 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라는 특수 함수를 사용하여 이벤트를 포착하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.