ईवेंट श्रोता को हटाना जो बाइंड के साथ जोड़ा गया था


164

जावास्क्रिप्ट में, बाइंड () का उपयोग करके एक श्रोता के रूप में जोड़े गए फ़ंक्शन को हटाने का सबसे अच्छा तरीका क्या है?

उदाहरण

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

})();

एक ही रास्ता है जिसके बारे में मैं सोच सकता हूँ कि हर श्रोता को बाँध के साथ जोड़ा जाए।

इस विधि के साथ उपरोक्त उदाहरण:

(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);
एसेलिजा

यह बहुत अच्छा है। यह एक अलग विषय हो सकता है, लेकिन इसने मुझे आश्चर्यचकित कर दिया कि क्या मुझे अपने बाकी तरीकों के लिए बाइंड (यह) करना चाहिए जो "इस" कीवर्ड का उपयोग करते हैं, भले ही यह विधि कॉल को अक्षम बना दे।
टेकफुरुया

मैं हमेशा निर्माणकर्ता में उन सभी तरीकों के लिए एक पहली चीज के रूप में करता हूं जो कहीं से पारित होने जा रहे हैं, भले ही मैं बाद में हटाने जा रहा हूं। लेकिन सभी तरीकों के लिए नहीं, बस उन लोगों के लिए जो पास हो जाते हैं।
एस्लेइजा

तुम क्या कर रहे हो समझ में आता है। लेकिन अगर यह एक पुस्तकालय का हिस्सा था, उदाहरण के लिए, आप कभी नहीं जान सकते कि मायक्लास के तरीके ("सार्वजनिक" होने के रूप में प्रलेखित) को पास किया जाएगा।
टेकफुरुया

सिर्फ FYI करें, अंडरस्कोर लाइब्रेरी में एक bindAllफ़ंक्शन है जो बाध्यकारी तरीकों को सरल करता है। अपने ऑब्जेक्ट इनिलाइज़र के अंदर आप अपनी ऑब्जेक्ट की _.bindAll(this)प्रत्येक विधि को एक सीमित संस्करण में सेट करने के लिए करते हैं । वैकल्पिक रूप से, यदि आप केवल कुछ तरीकों को बांधना चाहते हैं (जो कि मैं सुझाऊंगा, आकस्मिक मेमोरी लीक को रोकने के लिए), तो आप उन्हें तर्क के रूप में प्रदान कर सकते हैं _.bindAll(this, "foo", "bar") // this.baz won't be bound:।
मशीनगॉस्ट

जवाबों:


274

हालाँकि @machineghost ने जो कहा, वह सच था, कि घटनाओं को जोड़ा जाता है और उसी तरह से हटा दिया जाता है, समीकरण का गायब हिस्सा यह था:

.bind()कहा जाता है के बाद एक नया समारोह संदर्भ बनाया जाता है!

देखें क्या बाइंड () फ़ंक्शन संदर्भ को बदलता है? | स्थायी रूप से कैसे सेट करें?

इसलिए, इसे जोड़ने या हटाने के लिए, किसी चर का संदर्भ असाइन करें:

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

यह मेरे लिए उम्मीद के मुताबिक काम करता है।


4
बहुत बढ़िया, यह स्वीकृत उत्तर होना चाहिए। एक पुराने विषय को अपडेट करने के लिए धन्यवाद, यह विषय सर्च इंजन पर नंबर एक हिट के रूप में आया और आपके पास अब तक इसे पोस्ट करने का उचित समाधान नहीं था।
ब्लरग

यह प्रश्न में उल्लिखित विधि से अलग नहीं है (और इससे बेहतर नहीं)।
पीटर Tseng

मुझे समझ में नहीं आता, आप इसे क्लिक इवेंट के साथ कैसे काम करते हैं, धन्यवाद
अल्बर्टो एक्यूना

@ AlbertoAcuña आधुनिक ब्राउज़रों का उपयोग .addEventListener(type, listener)और .removeEventListener(type, listener)एक तत्व पर जोड़ सकते हैं और घटनाओं को हटाने के लिए। दोनों के लिए, आप समाधान में वर्णित फ़ंक्शन संदर्भ को listenerपैरामीटर के "click"रूप में, प्रकार के साथ पास कर सकते हैं । developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
बेन

1
इससे मुझे भी मदद मिलती है-हालांकि यह जवाब 4 साल पहले पोस्ट किया गया था :)
user2609021

46

उन लोगों के लिए जिनके पास फ्लक्स स्टोर से / के लिए रिएक्ट घटक के श्रोता को पंजीकृत / हटाने के दौरान यह समस्या है, अपने घटक के निर्माता के नीचे की पंक्तियों को जोड़ें:

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
अच्छी चाल, लेकिन रिएक्ट / फ्लक्स का किसी भी चीज़ से क्या लेना-देना है?
पीटर Tseng

विभिन्न वर्गों या प्रोटोटाइप फ़ंक्शंस से इवेंट श्रोताओं को जोड़ने और हटाने पर यह सही दृष्टिकोण प्रतीत होता है, जो मुझे विश्वास है कि इसके साथ कनेक्शन भी रिएक्ट घटकों / कक्षाओं पर लागू होता है। आप इसे एक सामान्य (जैसे, रूट) उदाहरण स्तर पर बाँध रहे हैं।
कीथ डीसी

1
this.onChange = this.onChange.bind(this)वास्तव में यह वही है जिसकी मुझे तलाश थी। समारोह thisहमेशा के लिए बाउंड :)
पावेल

2

इससे कोई फर्क नहीं पड़ता कि आप एक बाउंड फ़ंक्शन का उपयोग करते हैं या नहीं; आप इसे किसी अन्य ईवेंट हैंडलर की तरह ही हटा दें। यदि आपका मुद्दा यह है कि बाध्य संस्करण का अपना विशिष्ट कार्य है, तो आप या तो बाध्य संस्करणों का ट्रैक रख सकते हैं, या removeEventListenerहस्ताक्षर का उपयोग कर सकते हैं जो एक विशिष्ट हैंडलर नहीं लेता है (हालांकि निश्चित रूप से उसी प्रकार के अन्य ईवेंट हैंडलर को हटा देगा )।

(एक साइड नोट के रूप addEventListenerमें, सभी ब्राउज़रों में काम नहीं करता है; आपको वास्तव में आपके लिए क्रॉस-ब्राउज़र तरीके से अपने ईवेंट हुक-अप को करने के लिए jQuery जैसी लाइब्रेरी का उपयोग करना चाहिए। इसके अलावा, jQuery में नामांकित घटनाओं की अवधारणा है, जो अनुमति देते हैं। आप "click.foo" से बंधते हैं; जब आप घटना को हटाना चाहते हैं तो आप विशिष्ट हैंडलर या अन्य हैंडलर को हटाए बिना jQuery को "सभी फू घटनाओं को हटा सकते हैं" बता सकते हैं।)


मैं IE मुद्दे के बारे में पता कर रहा हूँ। मैं एक ऐसा एप्लिकेशन विकसित कर रहा हूं जो कैनवास पर बहुत निर्भर करता है इसलिए IE7- बाहर हैं। IE8 कैनवास का समर्थन करता है लेकिन न्यूनतम पर। IE9 + addEventListener का समर्थन करता है। jQuery के नाम स्थान की घटना बहुत साफ दिखती है। केवल एक चीज जिसकी मुझे चिंता है, वह है दक्षता।
टेकफुरुया

JQuery के लोग अपनी लाइब्रेरी को अच्छा प्रदर्शन करने के लिए बहुत मेहनत करते हैं, इसलिए मैं इसके बारे में ज्यादा चिंता नहीं करता। हालाँकि, अपनी सख्त ब्राउज़र आवश्यकताओं को देखते हुए आप इसके बजाय Zepto को देखना चाहते हैं। यह jQuery के एक स्केल-डाउन संस्करण की तरह है जो तेज़ है लेकिन पुराने ब्राउज़रों का समर्थन नहीं कर सकता (और कुछ अन्य सीमाएं हैं)।
मशीनगॉस्ट

JQuery नाम स्थान की घटनाओं का व्यापक रूप से उपयोग किया जाता है और लगभग कोई प्रदर्शन चिंता नहीं है। किसी को ऐसे उपकरण का उपयोग न करना, जो उनके कोड को आसान बना देगा और (यकीनन अधिक महत्वपूर्ण) समझने में आसान होगा, भयानक सलाह होगी, खासकर अगर ऐसा JQuery और काल्पनिक प्रदर्शन चिंताओं के एक अतार्किक डर से किया जाए।
मशीनगॉस्ट सिप

1
वह कौन सा हस्ताक्षर होगा? निष्कासन सूची पर MDN पृष्ठ दिखाता है कि पहले दो तर्कों की आवश्यकता है।
कोडर

मेरी गलती। मुझे यह जवाब लिखे हुए कई साल हो गए हैं, लेकिन मैं jQuery offया unbindविधि के बारे में सोच रहा होगा । एक तत्व पर सभी श्रोताओं को निकालने के लिए आपको उन्हें ट्रैक रखना होगा क्योंकि वे जुड़ जाते हैं (जो कि कुछ jQuery या अन्य पुस्तकालय आपके लिए कर सकते हैं)।
मशीनगूस्ट


1

हमें एक पुस्तकालय के साथ यह समस्या थी जिसे हम बदल नहीं सकते थे। ऑफिस फैब्रिक UI, जिसका अर्थ है कि हम इवेंट हैंडलर को जोड़ने के तरीके को बदल नहीं सकते हैं। जिस तरह से हमने इसे हल किया वह प्रोटोटाइप 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>

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

यदि आप 'ओनलीक' का उपयोग करना चाहते हैं, जैसा कि ऊपर बताया गया है, तो आप यह कोशिश कर सकते हैं:

(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 :: EventTarget.addEventListener - हैंडलर के भीतर "यह" का मूल्य

यह 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
}

ऊपर दिए गए उदाहरण में एक समस्या यह है कि आप श्रोता को बाइंड के साथ नहीं हटा सकते। एक अन्य समाधान एक विशेष फ़ंक्शन का उपयोग कर रहा है, जिसे किसी भी घटना को पकड़ने के लिए हैंडल कहा जाता है:

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.