如下html代碼,如何給一個列表中所有子元素添加點擊事件,輸出對應內容?
<ul id="container">
<li id="li1">1</li>
<li id="li2">2</li>
<li id="li3">3</li>
<li id="li4">4</li>
<li id="li5">5</li>
</ul>
首先,循環列表,依次添加點擊事件:
for(var i = 1; i < 5; i++){
document.getElementById('li' + i).addEventListener('click', function() {
alert(i);
})
}
似乎很容易理解這段代碼,但是實際上,不管點擊哪個<li>
標簽,都會彈出數字5。還不清楚原因的同學,可以先來了解一下html事件處理程序和js作用域的相關知識。
有個很直接的方法,每次輸出li標簽對應的innerText即可。如果我一定要輸出這個i呢?可以使用閉包的方式:
for(var i = 1; i < 5; i++){
(function(x){
document.getElementById('li' + x).addEventListener('click', function(){
alert(x)
})
})(i)
}
這里形如(function(){})()
的代碼塊,叫做“立即被調用的函數表達式”(Immediately Invoked Function Expression)。可以思考,這里閉包如何產生?
另外,采用ES6新增的變量類型let,也可以快速的解決該問題。
for(let i = 1; i < 5; i++){
document.getElementById('li' + i).addEventListener('click', function() {
alert(i);
})
}
用let代替var來聲明變量,就可以把變量的作用域限制在當前代碼塊中。變量i只存在於for循環中,一旦循環結束,在其他位置均無法訪問該變量。
上面方法都是通過循環,通過addEventListener
方法分別給每個<li>
標簽添加點擊事件。這樣做完全OK,不會導致什么問題。
但是在javaScript中,添加到頁面上的事件處理程序的數量,將直接關系到頁面的整體運行性能。首先,每個函數都是對象,都會占用內存;內存中的對象越多,性能就越差。其次,必須事先制定所有事件處理程序而導致的DOM訪問次數,會延遲整個頁面的交互就緒時間。
想象以上示例中,<li>
標簽的數量很大時,循環為每個子元素添加事件,絕非好方法。下面給出一種優雅的方法,采用事件委托。
document.getElementById('container').addEventListener('click', function(event) {
var target = event.target;
if(target.tagName == 'LI'){
alert(target.innerText);
}
}, false);
這段代碼里,使用事件委托只為<ul>
元素添加一個onclick
事件處理程序。因為有事件冒泡機制,單擊每個<li>
標簽時,都會被這個函數處理。關於事件流,主要為事件捕獲階段與事件冒泡階段,可以另行了解。
參閱學習文檔: