2012-07-19 127 views
99

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

})(); 

我能想到的唯一的辦法就是跟蹤與綁定添加每一位聆聽者的。

上面的例子用這種方法:

(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.myButton.addEventListener( 「點擊」,這.clickListener);' – Esailija 2012-07-19 16:46:48

+0

這非常好。這可能是一個不同的主題,但它使我想知道我是否應該做的我的方法,其使用「這個」關鍵詞其餘綁定(本),即使它會調用方法低效。 – takfuruya 2012-07-19 17:23:32

+0

我總是這樣做,因爲在構造函數中,第一件事就是所有方法是將要經過的地方,無論是否稍後我會刪除它們。但不是所有的方法,只是那些傳遞的方法。 – Esailija 2012-07-19 17:25:40

回答

178

雖然什麼@machineghost說的是真的,那事件,並移除同樣的方法,公式的缺失部分是這樣的:

創建一個新的函數引用.bind()後調用!

Does bind() change the function reference? | How to set permanently?

因此,添加或刪除它,引用賦值給一個變量:

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

可正常工作對我來說。

+2

非常好,這應該是被接受的答案。感謝您更新舊主題,這個主題在搜索引擎上成爲第一名,並且直到您現在發佈這個主題,它纔得到正確的解決方案。 – Blargh 2014-04-16 19:24:53

+0

感謝您按預期工作! – 2014-11-12 11:42:38

+0

這與問題中提到的方法沒有什麼不同。 – 2016-02-12 02:07:40

5

無論您是否使用綁定函數都無關緊要;您可以像刪除其他事件處理程序一樣刪除它。如果你的問題是綁定版本是它自己獨特的功能,你可以跟蹤綁定版本,或者使用不帶特定處理程序的簽名(儘管當然這會刪除相同的其他事件處理程序類型)。 (請注意,addEventListener不適用於所有的瀏覽器;您確實應該使用像jQuery這樣的庫以跨瀏覽器的方式爲您完成事件連接。另外,jQuery的概念是命名空間事件,它允許你綁定到「click.foo」;當你想刪除事件時,你可以告訴jQuery「刪除所有foo事件」,而不必知道具體的處理程序或刪除其他處理程序。)

+0

我知道IE的問題。我正在開發一個嚴重依賴畫布的應用程序,所以IE7-不在了。 IE8支持畫布,但至少。 IE9 +支持addEventListener。 jQuery的命名空間事件看起來非常整齊。我唯一擔心的是效率。 – takfuruya 2012-07-19 17:37:40

+0

jQuery人員*非常*難以保證他們的庫運行良好,所以我不會爲此擔心太多。但是,考慮到您嚴格的瀏覽器要求,您可能需要查看Zepto。這有點像jQuery的縮小版本,速度更快但不支持舊版本的瀏覽器(還有一些其他限制)。 – machineghost 2012-07-19 18:28:13

+0

「我不會擔心太多......」可怕的建議 – 1dayitwillmake 2013-09-01 02:18:56

-1

If你想使用'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 - The value of "this" within the handler

它給到的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的特殊函數來捕獲任何事件:

31

對於那些在向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() { 
 
    // ... 
 
    } 
 
    
 
}

+4

好戲,但React/Flux與什麼有什麼關係? – 2016-02-12 02:17:04

+0

Thx,這真的是一個很好的解決方案:) – Arcagully 2016-05-06 14:55:22

+0

這似乎是從不同類或原型函數添加和移除事件偵聽器時的正確方法,我相信這種連接也適用於React組件/類。你在一個普通的(例如,根)實例級別綁定它。 – 2018-01-21 02:31:42

-1

<咆哮>這是2016和DOM標準並沒有幫助解決一個很常見的問題多,我們打飄飛。 < /咆哮>

是刪除一個有界事件處理程序的唯一途徑是保持在有界函數的引用,並使用它作爲removeEventListener這裏其他的解決方案表示。

然而,當你有很多聽衆也變得混亂。沒有人提出簡單的函數來抽象出不得不引用有界函數的工作。我已經想出了兩個函數,我將它們添加到原型中,將其添加到所有HTMLElements中,並分別命名爲on()和off()(jQuery啓發式名稱)。 Code's here

所以使用,您可以添加事件偵聽器和像這樣刪除它們(在IE瀏覽器只適用11+,因爲它使用WeakMap):

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

實現細節和作出的決定有很多,所以我贏了「T解釋一下:)

如果你不喜歡添加功能,原生對象,那麼您可以通過編輯我的代碼位實現這一目標。 (但是,嚴格來說,DOM標準應該首先添加一些API來爲我們解決這個問題)。

+0

你好,看看這個很好的抽象保留參考Felix Kling的答案在這裏的事件:http://stackoverflow.com/questions/5660131/how-to-removeeventlistener-that-is-addeventlistener-with-anonymous-function – 2016-10-11 02:19:24

+0

他解決方案仍然是一團糟。我仍然需要在代碼中保留處理程序引用。我在一個文件中引用了6個這樣的處理程序,這個文件做了一些拖放操作(爲了在拖放完成後刪除事件監聽器)。而且我還有幾個有類似需求的文件。我唯一的要求是事件處理程序的'this'關鍵字應該是添加偵聽器的對象實例。在上面介紹的解決方案中,不需要保留處理程序引用。 – Munawwar 2016-10-11 20:18:46

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); 
相關問題