讓咱們回到尚未DOM2級事件方法的年代,那時候,咱們想給某元素綁定一個事件方法時,是這樣作的:javascript
javascriptvar ele=document.getElementById("main"); ele.onclick=function(){ console.log("作一件事"); }
這就是DOM0級事件方法,咱們須要用XXX.onYYY=ZZZ這種形式來綁定事件,當點擊元素時,會輸出「作一件事」,恩,很理想。java
接下來,當咱們但願在點擊該元素的時候再作另外一件事怎麼辦呢?下面這樣:web
javascriptele.onclick=function(){ console.log("作第二件事"); }
這樣確定不行,由於咱們是但願點擊時既輸出「作一件事」,又要輸出「作第二件事」。而這樣的話後面的方法將前面的覆蓋掉了,由於XXX.onYYY=ZZZ只能給元素的某個事件類型(如例子中的click事件)綁定一個方法ZZZ;這個問題難不到咱們,既然這樣,那咱們就把要作的事情全都放在ZZZ裏不就好了麼;數組
javascriptfunction fn1(){ console.log("作一件事"); } function fn2(){ console.log("作第二件事"); } ele.onclick=function(){ fn1(); fn2(); }
接下來,挑戰來了,那若是咱們是但願在點擊元素時作100件不一樣的事情呢?難道也是這樣用XXX.onYYY=ZZZ形式,將100個方法都在ZZZ裏依次執行嗎?顯然這是很笨的方法;動一下腦筋,很快想到另外一個解決方案:
明確如下需求,咱們是但願在點擊事件發生時,執行多個方法,而具體有多少個,一開始時不明確的,那麼咱們能夠用一個數組來保存全部的回調方法;當點擊發生時,執行一個方法,在這個方法中依次執行數組中的回調方法。函數
javascriptvar arr=[]; ele.onclick=function(){ for(var i=0;i<arr.length;i++){ if(typeof arr[i] === "function"){ arr[i](); } } } arr.push(fn1); arr.push(fn2);
這樣的話,當咱們須要給元素點擊事件添加更多的方法時,直接往數組arr
中push更多的回調方法就好了,甚至當咱們但願去掉某個方法,也能夠經過刪除數組中的某項來完成。
可是這樣並不能實際使用,由於用來保存事件的數組當前是做爲一個全局變量。爲了持久化保存這個數組,咱們能夠這樣,將這個數組定義爲元素節點對象的某一個屬性,就像下面這樣:this
javascriptele.eventList=[]; ele.onclick=function(){ for(var i=0;i<btn.eventList.length;i++){ btn.eventList[i](); } } ele.eventList.push(fn1); ele.eventList.push(fn2);
以上代碼只是針對click事件,在實際運用時,有各類不一樣的事件類型;在某個類型事件觸發時,須要執行全部綁定到該類型事件的方法,所以,針對每一個事件類型,都要採用一個數組來保存相應回調方法。
另外,爲了在某類型事件觸發時,找到針對與該事件類型的全部方法並執行,咱們能夠將這個數組直接以事件類型名稱命名;
而後考慮將以上方法用一個函數封裝起來:以下code
javascriptfunction bind(ele,ev,callback){ if(!ele.ev){ ele.ev=[];//直接將保存某個事件類型全部回調方法的數組命名爲事件類型 ele["on"+ev]=function(){ for(var i=0;i<ele.ev.length;i++){ ele.ev[i].call(ele);//這裏別忘了用call改變this關鍵字 } } } ele.ev.push(callback); }
爲了防止重複綁定,在將回調函數push進事件隊列數組時,首先須要判斷被綁定的回調是否已經存在於事件隊列中;對象
javascriptfunction bind(ele,ev,callback){ if(!ele.ev){ ele.ev=[]; ele["on"+ev]=function(){ //只須要綁定一次 for(var i=0;i<ele.ev.length;i++){ ele.ev[i].call(ele);//這裏別忘了用call改變this關鍵字 } } } //因爲IE9如下不支持數組的indexOf方法,因此須要遍歷查找 var flag=true; for(var i=0;i<ele.ev.length;i++){ if(ele.ev[i]==callback){ flag=false; break; } } if(flag){ ele.ev.push(callback); } }
而相應unbind方法,只須要將須要解綁的回調函數從相應的函數隊列中刪除便可;
另外還有如下一個問題:上述這種狀況下,綁定了幾個類型的事件,就會給ele多添加幾個屬性,命名空間污染極其嚴重;因此採用的方案是在元素上只添加一個屬性(如_event
),而後全部的事件隊列全都擴展在這個屬性上。blog
所有代碼以下:隊列
javascriptfunction bind(ele,ev,callback){ if(!ele._event){ ele._event={}; } if(!ele._event[ev]){ ele._event[ev]=[]; ele["on"+ev]=function(e){ e=e||window.event; var eList=ele._event[ev]; for(var i=0;i<eList.length;i++){ eList[i].call(ele,e); } } } var flag=true; for(var i=0;i<ele._event[ev].length;i++){ if(ele._event[ev][i] == callback){ flag=false; break; } } if(flag){ ele._event[ev].push(callback); } } function unbind(ele,ev,callback){ try{ var eList=ele._event[ev]; for(var i=0;i<eList.length;i++){ if(eList[i]==callback){ eList.splice(i,1); return 1; } } }catch(e){ //這裏主要防止在未調用bind初始化就調用unbind時出錯。 console.log("請先使用bind綁定事件。"); } }
在使用時傳入三個參數,元素,事件類型,回調方法,便可。解綁時傳入一樣的三個參數;這個簡單的方案解決了如下問題:
以上,咱們就完成了一個粗糙的事件系統,它的思想很相似於Dean Edward在其發佈的addEvent.js中的實現方式;