[英]JavaScript addEventListener() not working as expected
我從未使用過addEventListener()
,但是由於生成內容的方式,我無法為每個要作為按鈕的<div>
編寫HTML等效代碼。 等效為:
<div onmousedown="jsItems[someId].toggleImage(someGallery, someIndex);"></div>
我一直在嘗試的是:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
}
}
}
在這里i
從0到1計數,而j
從0到2計數(在這種情況下,對於i
而言), i
代表someGallery
, j
代表someIndex
,我可以在上面的代碼中(或與self.id
someId
使用this.id
訪問self.id
在addEventListener
的函數中)。
問題是,盡管單擊這些“按鈕”( <div>
)之一確實會觸發:
JsTree.prototype.toggleImage = function(inGallery, inIndex) {
alert(this.id+", "+inGallery+", "+inIndex);
}
無論單擊哪個按鈕,它始終會發出“ 8、2、3”警報。 “ 8”是正確的,但我不知道為什么會警告“ 2”或“ 3”。 它們似乎僅比i
和j
計數多1(通過嘗試j < this.jsGalleries[i].buttons.length-1
來警告“ j < this.jsGalleries[i].buttons.length-1
”)。
編輯 : someId
, someGallery
和someIndex
不是真正的變量,它們是我試圖解釋問題的垃圾someIndex
。
這是經典的JS錯誤。 問題在於,在任何函數作用域中都不會捕獲i
和j
的值,並且事件處理程序是異步的。 這意味着當您的事件處理程序運行時,兩個for
循環都已完成,因此i == this.jsGalleries.length
和j === this.jsGalleries[this.jsGalleries.length - 1].buttons.length
。
嘗試以下方法之一:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
(function(self, innerI, innerJ){
var galleryEl = self.jsGalleries[innerI].buttons[innerJ];
galleryEl.addEventListener("mousedown", function() {
self.toggleImage(innerI, innerJ);
});
})(this, i, j);
}
}
}
或更清晰:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
var addHandler = function(self, i, j){
self.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
};
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
addHandler(this, i, j);
}
}
}
addEventListener沒問題。 這是一個常見的錯誤。 為了了解發生了什么,我必須解釋閉包是如何工作的。
當您有一個循環和其中的一個函數時:
var i = 5;
while(i--){
setTimeout(function(){
console.log(i);
}, 100);
}
每個函數都引用了變量i
。 這意味着它們在您定義它們時不會保留i
的值。 再次重申一下,每個函數都引用了相同的變量i
,而不是該函數聲明時的值。 在上面的示例中,所有setTimeout都是異步定義的。 匿名函數都會在100毫秒后觸發,每個匿名函數都會記錄該函數運行時i
中的值。 在我的示例中,所有函數的該值為-1。
有兩種解決方法。 首先,我將向您展示簡單的方法:
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
self.gallery = {i: i, j: j};
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(self.gallery.i, self.gallery.j);
});
}
}
在這里,您將值存儲在實際的DOM元素上。 這些值等於運行循環時的值,因此事件偵聽器將獲取正確的值。 注意,我將值嵌套在一個名為gallery的對象中。 我這樣做是為了給它命名空間。 將值存儲在DOM中的元素上不是一個好主意,以防萬一瀏覽器最終實現了具有相同名稱的屬性。 我覺得畫廊足夠安全。
解決此問題的另一種方法(可能是最佳實踐)是使用閉包來發揮自己的優勢。
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", (function closure(self, i, j){
return function actualListener(){
self.toggleImage(i, j);
}
})(self, i, j));
}
}
在這種情況下,我們創建一個自我執行函數(在我的示例中稱為closure),該函數在創建偵聽器時立即運行。 讓我再次聲明一下,此函數在添加偵聽器的那一刻開始運行,而不是在運行時運行。 這樣做的原因是,這樣我們就可以傳入想要保存以供以后使用的值,在這種情況下為self,i和j。 然后,當事件發生時,實際運行的函數是內部函數(稱為ActualListener)。 ActualListener具有運行閉包函數時存儲在其閉包中的所有值的副本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.