js循環綁定事件javascript
在js中,用循環來爲一個元素隊列的元素綁定事件,是一個常見的問題。java
一般進入誤區的新人,都會理想固然地這麼寫代碼:瀏覽器
(假設元素隊列爲o,默認使用jQuery)閉包
//error method
var o =$('.blockHead'); for(var i=0; i<o.length; i++){ o[i].onclick = function(){ alert(i); } }
固然這種狀況下,你會發現每一個元素點擊運行時,顯示的 i值 都是 o.length-1;異步
由於js的函數是調用時觸發,綁定事件的時候i值並無被傳入執行函數裏。函數
做爲異步監聽的事件,點擊事件發生的時候,循環已經結束,此時的 i值 爲[o.length-1], 觸發事件傳遞的參數天然是同一個最大值,而不是預期不一樣元素傳不一樣值。性能
解決辦法思路:將綁定事件過程當中獲得的i值,在執行函數域裏保存起來;調用時天然就獲得相應的結果。this
方法一: 使用閉包函數存儲i值spa
var o = $('.blockHead'); for(var i=0; i<o.length; i++){ o[i].onclick = (function closure(ii){ //var ii; //we save ii in this scope return function(){ alert(ii); } })(i); }
如上,colsure是一個閉包函數(closure函數名可省去,做匿名閉包函數),聲明後當即執行,傳遞了i值,因而在它的函數域裏保存了i值(arguments參數列表裏);代理
函數的返回結果是事件處理函數,其參數ii就是colsure裏保存的ii值,也就是循環時就傳遞的i值。
ps:方法一是很是常見的處理該問題的解決方法;在尋找解決方案的過程裏,我發現了另外一個寫法,以下。
方法二: 將事件綁定的代碼寫在一個外圍函數裏
var o = $('.blockHead'); for(var i=0; i<o.length; i++){ attach(i,o[i]); function attach(ii,o){ o.onclick = function(){ alert(ii); }; } } //或者 直接在聲明函數後馬上執行 var o = $('.blockHead'); for(var i=0; i<o.length; i++){ (function attach(ii,o){ o.onclick = function(){ alert(ii); }; })(i,o[i]); }
//在函數域範圍內可訪問外層函數的變量,用一個生存與當即函數的變量來存貯i,因而能夠寫成醬紫 var o = $('.blockHead'); for(var i=0; i<o.length; i++){ (function (){ var p = i; o[i].onclick = function(){ alert(p); }; })(); }
一樣的道理,在attach的函數域裏,onclick事件獲得的ii值已是執行循環便獲取的的i,而外部的i執行完循環後的結果是什麼已經跟內部沒有關係了。
稍許比方法一麻煩的是,參數須要多傳遞一個元素的引用。
最後一個方法顯然是最簡便明朗的。
js的事件代理機制
讓咱們追溯到問題更深刻的部分,即什麼場景會出現[事件循環綁定]的要求?一個父級元素的子級元素隊列元素綁定事件,除了一個一個子級元素地去綁定有更好的解決方案麼?
真巧,js有這麼一種機制,叫 事件代理/委託(delegate),基於瀏覽器事件處理的冒泡機制,用一個事件處理程序能夠管理某一類型的全部事件。
當須要多個元素添加事件的時候,能夠經過將事件添加到它們的父節點而將事件委託給父節點來觸發處理函數。
使用代理的好處:減小事件處理程序的數量,可以很好提升js性能;
延遲指定對DOM元素的處理程序,使得交互時間縮短。