題目簡述
編寫十個button,內容分別是1到10,點擊其中的一個button則輸出當前的序號,即點擊1輸出1。
解題思路
這裏的十個button樣式都是一樣的,只是內容不同,顯然使用js動態添加較爲方便,很多人可能會這樣寫:
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.innerHTML = i;
doc.onclick = function(){
console.log(i);
};
document.body.appendChild(doc);
}
</script>
輸出的效果是十個並排的按鈕,內容分別是1到10,看似沒有問題,但是點擊之後,無論點擊哪一個按鈕,輸出的總是11,這裏就發生了閉包。
由於click不是立即執行的事件,當我們點擊按鈕的時候,上述代碼的循環早已結束,我們輸出的是最後的i值,那爲什麼是11呢?
根據for循環的邏輯,初始值是1,之後每次執行過後i+1,在執行到i=10的循環體之後,i再次+1,這時候變成了11,但是不能通過i<=10的判斷,所以不再繼續執行循環體,i最終爲11。
爲了解決這個問題,這裏提供幾個解題方法:
方法一,綁定id
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.id = doc.innerHTML = i;
doc.onclick = function(){
console.log(this.id);
};
document.body.appendChild(doc);
}
</script>
在賦值的同時把i同時給button動態添加一個id,id的值爲i,雖然這樣能實現我們的需求,但是很顯然這不是最好的方式,這樣的id是無意義的,並且很容易重複,所以如果使用綁定屬性的方法,還是推薦大家使用下面的這個方法,使用dataset。
方法二,使用dataset新特性
dataset是HTML5裏面的新特性,它叫自定義屬性對象。使用方式也很簡單:
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.dataset.value = doc.innerHTML = i;
doc.onclick = function(){
console.log(this.dataset.value);
};
document.body.appendChild(doc);
}
</script>
添加dataset屬性時只需要doc.dataset.value即可,這裏的value是自定義屬性名,其在html裏面生成的代碼樣式爲:
<button data-value="1">1</button>
獲取它的屬性值也是直接doc.dataset.value即可獲取。
方法三,使用bind
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.innerHTML = i;
doc.onclick = (function(){
console.log(this.toString());
}).bind(i);//綁定i值,同時this指向i
document.body.appendChild(doc);
}
</script>
我們知道,上述閉包的發生是因爲i值的變化,那我們可以像個辦法來組織它的變化。
使用bind函數可以使當前對象或函數綁定對應的值或對象並且將this指向它,這樣的話每個button的點擊事件就會被綁定i,這個i是當前的一次循環,但是我們使用bind把它和當前的button綁定住了,雖然大體的i在變化,但是每個button綁定的i不會在變化,所以這裏我們可以輸出當前的button序號。
很顯然,這是最簡單也是最好的辦法。