你這裏的問題是,可變i
不會有你認爲它會具有的價值。這是由於關閉如何工作。
試試這個簡單的例子:
for(var i = 0; i < 5; i++) {
console.log("Before timeout: " + i);
setTimeout(function() {
console.log("After timeout: " + i);
}, 1000);
}
當您運行此,你會期待什麼?可能在一秒鐘後,你在控制檯中看到了這一點?
Before timeout: 0
Before timeout: 1
Before timeout: 2
Before timeout: 3
Before timeout: 4
After timeout: 0
After timeout: 1
After timeout: 2
After timeout: 3
After timeout: 4
但你猜怎麼着,你會看到這一點:
Before timeout: 0
Before timeout: 1
Before timeout: 2
Before timeout: 3
Before timeout: 4
After timeout: 5
After timeout: 5
After timeout: 5
After timeout: 5
After timeout: 5
爲什麼5? for
聲明基本上將i
設置爲0,然後在每次迭代中對其進行計數並每次檢查它是否仍低於5,如果不是,則打破循環。因此,在循環後,i
有值爲5,這樣的:
...給:
After loop: 5
好,但爲什麼使用值i
在循環之後有?這是因爲您傳遞給setTimeout
的函數回調直接訪問外部範圍中的i
變量!而且因爲函數只會在一秒後運行(在循環完成之後),此時i
的值將爲5,如上所示!
因此,解決辦法是創建這個變量,它是本地的每次迭代的本地副本。現在,這是不是因爲它似乎是因爲在JavaScript中,像for
控制塊不會創建範圍(除非你使用ES6和let
)微不足道。你必須使用另一個匿名函數圍繞它來創建一個本地副本,每次它是那麼的不同:
for(var i = 0; i < 5; i++) {
// This is a so-called "immediately executed function expression"
// It's an anonymous function which is then immediately called due
// to the `()` at the end.
(function() {
var j = i; // Now we have a local `j`
console.log("Before timeout: " + j);
setTimeout(function() {
console.log("After timeout: " + j);
}, 1000);
})();
}
另一種方式由於這一點,這是一個有點短,是這一招:
for(var i = 0; i < 5; i++) {
// See how this anonymous function now takes a parameter `i`?
// And we pass this parameter when calling this function below,
// which basically creates an inner copy which we can now also call
// `i`.
(function(i) {
console.log("Before timeout: " + i);
setTimeout(function() {
console.log("After timeout: " + i);
}, 1000);
})(i);
}
這就是爲什麼我建議在迭代數組時使用類似forEach
的東西,因爲它們將函數回調作爲參數,所以您自動在代碼中有一個本地作用域。
現在你的情況,你可以使用上面的一個匿名函數解決方案,或者類似forEach
,但有一個問題:element
變量不是普通的JavaScript數組,它是一個DOM元素列表,它沒有forEach
。但是,你仍然可以使它工作使用Array.prototype.forEach.call
招:
function recordData(){
var element = document.getElementsByClassName("bubble");
Array.prototype.forEach.call(element, function(i) {
element[i].addEventListener("click", function(){
var id = element[i].attributes.id.value;
var x_cordinate = element[i].children[2].attributes.x.value;
var y_cordinate = element[i].children[2].attributes.y.value;
var keyword = element[i].children[0].textContent;
clicked_elements.push({
id: id,
x_cordinate: x_cordinate,
y_cordinate: y_cordinate,
keyword: keyword
})
}, false);
});
}
之前,因爲你有效利用element[element.length].attributes
(因爲i
已經是一個超出你的數組上限的),沒有工作,所以element[i]
是未定義。
你走了!
但是,更好的方法是使用this
,因爲在事件偵聽器中,this
引用事件的目標。因此,而不是element[i]
,您可以在聽衆中隨處使用this
!
您可以使用'this'來引用事件偵聽器函數中的clicked元素。你會陷入麻煩,因爲在你的for循環中'i'正在改變。 – user3297291
你可以通過使用IIFE來解決這個問題(var i = 0; i
jcubic
參考這項工作!謝謝 ! –