前言:
面試的時候有點蒙,結束之后想想自己好像根本就誤解了面試官的問題,因為我理解的這個問題本身就沒有意義。但是當時已經有一些思路,但是在一個點上被卡住。
結束之后腦子瞬間靈光,想出了當時沒有邁出的那一小步。所以不想計較這個問題本身的意義,單純的想要把這個我理解錯了的問題解決,就當是滿足自己一個小小的願望吧。
問題:
用addEventListener()和attachEvent()給一個DOM元素綁定事件處理程序時,如果傳入一個匿名函數,那么用相應的removeEventListener()和detachEvent()是無法將這個匿名的處理程序解除綁定的。所以我們用的時候應該傳入一個函數表達式。
那么,如果我就是想使用匿名函數進行綁定和解綁,怎么解決?
思路:
既然這兩個函數都高冷的說明了不接受相同的匿名函數進行解綁,那么就只能另尋出路,不能靠它來管理事件了。
所以需要一個自定義的對象來管理事件。
事件處理程序的本質就是,當一個事件在一個對象上發生時,執行監聽這個事件的函數。
翻譯一下:
一個DOM元素可能被綁定多個事件類型的處理程序。比如click的時候顏色改變,mouseover的時候變大。
一個事件類型可能綁定多個事件處理程序。比如mouseover的時候又變色又變大。
所以,這個事件對象應該有一個屬性用來存儲這個DOM元素上綁定的所有事件處理程序,還應該有兩個方法,一個用來添加,一個用來刪除。
{ handlers:{ type1:[handler1,handler2], type2:[handler1,handler2], ...//其他事件類型和對應的事件處理函數 }, on:function(){}, off:function(){} }
當一個事件發生時,就調用這個對象里面對應的事件類型的數組里面的所有函數。
所以綁定事件就是往對應的數組里面添加函數,解除綁定事件就是把這個函數從這個數組里面刪掉。
那么怎么保證操作的是那個正確的DOM元素呢?
顯然,每個DOM元素都應該需要一個這樣的對象,用於管理自己的事件處理程序。
每個對象都有的東西,那不就是他的屬性嘛。(而我當時就被卡在了這里)。
實現:
每個DOM元素都需要這樣一個對象,而且每個對象中的on()和off()方法都是相同的,所以需要一個構造函數,把這兩個方法放到他的原型對象中去。
function EventManage(){ this.handlers={} } EventManage.prototype={ on:function(type,handler){ if(!this.handlers[type]){ this.handlers[type]=[handler]; return true; //避免添加多個事件 }else{ this.handlers[type].push(handler); } }, off:function(type,handler){ for(var i=0,len=this.handlers[type].length;i<len;i++){ if(this.handlers[type][i].toString()==handler.toString()){ this.handlers[type].splice(i,1); } } } }
每個對象有了這兩個方法,就可以自行添加和移除事件處理程序了,但是,監聽事件,還是要靠JavaScript提供的方法,所以借用addEventListner()和attachEvent()來監聽事件:
var EventUtil={}; EventUtil.on=function(ele,type,handler){ if (!ele.event) { ele.event=new EventManage(); } var isNewType=ele.event.on(type,handler); var fire=function(){ for(var i=0,len=ele.event.handlers[type].length;i<len;i++){ ele.event.handlers[type][i](); } }; if (isNewType) { if (ele.addEventListener) { ele.addEventListener(type,fire,false); }else{ ele.attachEvent("on"+type,fire); } } } EventUtil.off=function(ele,type,handler){ ele.event.off(type,handler); }
這里要注意一個問題,每次使用EventUtil.on()時都會重新定義一個fire函數,addEventListener()就會給相同的事件類型添加多個相同的事件處理程序,所以需要判斷一下這個事件類型是不是新增的,如果是的話再用addEventListener()來監聽這個事件類型。
使用示例:
var btn=document.getElementById("btn"); EventUtil.on(btn,"click",function(){ console.log("11"); }); EventUtil.on(btn,"click",function(){ console.log("22"); }); EventUtil.off(btn,"click",function(){ console.log("11"); });
當點擊btn時,只打印了"22",說明匿名函數成功解綁。