JS基礎——事件綁定

     上一篇博客JS事件對象中,老師問JS事件處理和VB中的事件處理有什麼聯繫?先來解決一下這個問題。舉個VB.net中事件處理的樣例(JS敲久了,VB習慣的都不熟悉了,看來得經常回想了):javascript

一、事件處理VB VS JS

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MsgBox("helo!")
        MsgBox(sender.width) '彈出觸發這個事件對象的寬度
        MsgBox(sender.name) '彈出觸發這個事件對象的名字
        MsgBox(TypeOf e Is Object) 'TRUE
        MsgBox(TypeOf sender Is Object) 'true
End Sub</span>

解析:看上面的事件處理代碼:可以看出來組成和JS中的如出一轍,相同包含觸發事件的對象button1,事件處理函數Click和對應要運行的函數(這裏理解爲事件更好)Button1.Click(即要運行的行爲),可見僅僅是他們的表示方式有些不同而已。java

   那麼在這個事件處理中有兩個參數sender和e,它們的做用又是幹嗎的呢?數組

   一、sender:觸發事件的對象,這裏指button,在代碼中經過彈出sender.name來獲取觸發該事件對象的名字,sender.width來獲取觸發該事件對象的寬度等,可見經過sender可以獲取觸發該事件對象的相關信息。類型爲object瀏覽器

   二、e:這裏看到e有沒有認爲跟JS中事件綁定中傳入的參數e有點兒同樣呢?它在VB中就至關於JS中瀏覽器默認傳遞給事件處理的一個事件對象。經過e可以獲取相應事件的相關信息,相同在上篇博客中經過JS中事件處理中的事件對象來獲取一些事件的相關信息在VB中也是可以作到的,好比改動鍵的樣例,在VB中經過e也是可以來進行推斷用戶按下的鍵值的,僅僅是語法上不一樣罷了。相同它的類型爲object函數

   綜上,JS中的事件處理和VB中的沒啥差異(我的大膽的推測中。。。),那麼瞭解了事件對象後,對後面理解事件綁定會有很是大的幫助。學習

二JS事件綁定

     先理解一下事件綁定:事實上,這裏說到事件綁定,仍是很是熟悉,C#中用到的託付就是這麼個思想,VB中也用到了事件綁定AddHandler,removeHandler等都是事件綁定思想的一個應用。核心思想都是講一個方法或事件掛載到還有一個事件或方法上去運行,調用時就好像一個代理,直接調用同一個事件或方法就可以運行規定好的同一種類型(或理解爲遵循同一種規則)的所有方法或事件(純屬我的編造的句子)。this

   這裏同一種規則在JS的事件綁定中可以理解爲:比方相同都有一個觸發事件的對象,後面緊跟着一個事件處理函數(onClick),在後面就是要運行的方法的詳細過程等。spa

   對於傳統的事件綁定,JS中常見的有兩種形式,內聯和腳本樣式,這樣的一般可以用來處理簡單的事件函數,對於略微複雜的,會有很是大弊端:.net

   一、不能同一時候綁定多個函數。代理

   二、統一元素上多個一樣的函數,不能夠本身主動屏蔽一樣的函數

   三、函數體內的this指向window,而不是當前的DOM對象

   四、函數運行的順序不能依照綁定順序運行

   五、需要在函數體內進行標準化event對象

 針對以上問題,JS中採取現代事件綁定,但W3C中直接採取現代事件綁定addEventListener可解決以上問題,IE中(attachevent)僅僅能解決第一條和第5條。爲了解決IE中this傳遞和標準化event問題,可以採用call傳值的方式來解決。

	function addEvent(obj,type,fn){
		if ( typeof obj.addEventListener!='undefined'){ //W3C
			obj.addEventListener(type,fn,false);
		}else if(typeof obj.attachEvent!='undefined'){//IE
			obj.attachEvent('on'+type,function(){
				fn.call(obj,window.event);//利用對象冒充來解決this傳遞問題,window.event參數的傳遞用來解決標準化event的問題。
			});
		}
	}
	

經過以上現代事件綁定後,出現一個問題,由於採用call來進行對象冒充,致使後面封裝時,刪除不需要的函數時出現困難,主要緣由是沒法識別到底是要刪除哪個函數,假設爲每一個函數加上一個ID,刪除時,僅僅要指明要刪除的ID就可以了。針對這個問題,咱們在基於JS一切皆對象的原理上,可以爲每一個事件加入一個屬性ID,來進行識別。同一時候,針對於上述IE中出現的其他問題,採取了傳統事件綁定來模擬現代事件綁定的方式來解決一樣函數屏蔽和刪除指定函數的問題。

一、模擬現代事件綁定

//爲每個事件分配一個計數器
addEvent.ID=1; //爲了防止全局變量的災難,因此將事件ID變爲addEvent的屬性

function addEvent(obj,type,fn){
	if ( typeof obj.addEventListener!='undefined'){ //W3C
		obj.addEventListener(type,fn,false);
	}else {
		//建立一個存放事件的哈希表
		if(!obj.events)obj.events={};
		//第一次運行時運行
		if(!obj.event[type]){
			//建立一個存放事件處理函數的數組
			obj.events[type]=[];
			//把第一次的事件處理函數先存儲到第一個位置
			if(obj['on'+type]) obj.events[type][0]=fn;
		}else {
		//同一個註冊函數進行屏蔽,不加入到計數器中
			if(addEvent.equal(obj.events[type],fn)) return false; //假設以後的函數與第一個相等,本身主動屏蔽
		}
		//從第二次開始,用事件計數器來存儲
		obj.events[type][addEvent.ID++]=fn;
		//運行事件處理函數
		obj['on'+type]=addEvent.exec(); //這裏講事件處理運行函數進一步封裝
		}
		

}

二、封裝事件處理的運行函數

	addEvent.exec=function(event){  //這裏需要經過傳遞的事件來獲取事件類型
		var e=event||addEvent.fixEvent(window.event); //經過addEvent事件中傳遞的event對象來獲取type類型
		var es=this.events[e.type] //將obj用this來取代獲取obj對象
		for (var i in  es){ //這裏要用this.events[e.type],不能直接用obj.events
			es[i].call(this,e);  //IE需要傳遞this來彈出this.value值,解決IE中的this傳遞問題
		}
	}

三、同一個註冊函數進行屏蔽

	addEvent.equal=function(es,fn){
		for(var i in es){
			if(es[i]==fn) return true;
		}
		return false;
	}


四、刪除事件

	function removeEvent(obj,type,fn){
		if(typeof obj.removeEventListener!='undefined'){
			obj.removeEventListener(type,fn,false);
		}else {
			for (var i in obj.events[type]){
				if(obj.events[type][i]==fn){
					delete obj.events[type][i];
				}
			}
		}
	}


     可以看出,IE在模擬現代事件過程當中,事件對象e起了很是大做用,經過它可以獲取加入事件的類型及ID,而在刪除時,正好藉助於事件對象獲取的屬性來進行刪除,很是方便。同一時候,理解了跨瀏覽器事件綁定的封裝,對後面JS封裝庫理解也就簡單了很是多,封裝時,很是多方法也都是在這個基礎上來進行的,最後若想實現連綴,需要返回封裝庫的一個核心對象,這也是後面學習JQuery的一個核心。

    以上純屬我的理解,不表明百度或CSDN的意見,若參考,請認真斟酌!




相關文章
相關標籤/搜索