Javascript中的事件處理程序

事件是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。

事件流

事件流描述的是從頁面中接受事件的順序。

而IE和Netscape開發團隊提出了完全相反的兩個事件流的概念。IE的事件流是事件冒泡流;Netscape的事件流是事件捕獲流。

事件冒泡:即事件最開始由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然後逐級向上傳播至最不具體的那個節點(文檔)。

事件捕獲:不太具體的節點應該更早接收到事件,而最具體的節點最後接收到事件。

使用事件處理程序

◆ HTML事件處理程序

事件是直接加在html結構裏的html元素上的(不推薦,因爲違反了HTML與JavaScript分離的準則)


HTML事件的缺點:HTML和JS代碼緊密的偶合在一起,如果要更換已經寫好的事件處理程序,就需要手動改兩個地方(JS代碼和HTML都需要修改),很不方便。

◆ DOM0級事件處理程序

javascript指定處理程序比較傳統的一種方式,即把一個函數賦值給一個事件的處理程序屬性。是目前用的比較多的方法。所有瀏覽器都支持DOM0級事件處理程序,且使用該方式時,事件處理程序是在元素的作用域中運行,因此程序中的this都是指向元素。

DOM0級事件的優點:簡單、跨瀏覽器的優勢。可以給一個元素添加多個事件。

DOM0級事件的缺點:雖然可以給一個元素添加多個事件,但一個事件只能綁定一個響應函數,重複綁定會覆蓋之前的綁定。


如果要刪除某個事件處理程序,直接將指定元素的事件屬性設置爲null即可。

◆ DOM2級事件處理程序

比較推薦使用,可以給一個元素添加多個事件並綁定多個不同的函數,遺憾的是,它不兼容IE瀏覽器。

DOM2級事件定義了兩個方法,分別用於處理指定和刪除事件處理程序的操作:addEventListener()和removeEventListener()。

(1).addEventListener(event,function,useCapture)接收三個參數:要處理的事件名、作爲事件處理程序的函數、布爾值。

▶ event:字符串,事件名稱,如'click'等 不需要'on'前綴。(必須)

▶ function:事件處理的函數,實現EventListener接口。(必須)

(1).可以附加多個事件處理函數,執行順序按照綁定的先後

(2).關於處理函數刪除的問題,使用addEventListener()將事件處理函數加入到捕獲階段,則必須在removeEventListener()中指明是捕獲階段,才能正確地刪除這個事件處理函數。 

(3).使用傳統的方法(DOM0級事件處理程序)直接給事件處理函數屬性賦值,事件處理函數將被添加到事件的冒泡階段。

▶ useCapture:是否使用捕獲--- true表示該元素在事件的“捕獲階段”(由外往內傳遞時)響應事件,false表示該元素在事件的“冒泡階段”(由內向外傳遞時)響應事件。(捕獲過程要先於冒泡過程)(可選。默認爲false)

當鼠標點擊所看到的按鈕時,其實發生了一系列的時間傳遞,可以想象一下,button實際上是被body"包裹"起來的,body是被html"包裹"起來的,html是被document"包裹"起來的,document是被window“包裹”起來的。所以,在你的鼠標點下去的時候,最先獲得這個點擊的是最外面的window,然後經過一系列傳遞纔會傳到最後的目標button,當傳到button的時候,這個事件又會像水底的泡泡一樣慢慢往外層穿出,直到window結束。

綜上,一個事件的傳遞過程包含三個階段,分別稱爲:捕獲階段,目標階段,冒泡階段。目標指的就是包裹得最深的那個元素。

因爲在大多數情況下我們都是將事件處理程序添加到事件的冒泡階段,這樣可以最大限度的兼容各大瀏覽器,所以一般情況下都將useCapture的值設爲false。


(2).removeEventListener(event,function,useCapture)接收三個參數:要刪除的事件名、要移除的函數、布爾值(指定移除事件句柄的階段)。

通過addEVentListener添加的事件只能通過removeEventListener刪除。


◆ IE事件處理程序

IE有自己的一套事件處理程序,它提供了兩個方法,分別用於處理指定和刪除事件處理程序的操作:attachEvent(event,function)和detachEvent(event,function)。(IE11已不再支持該方法,只兼容DOM0級事件處理程序)

▶ event:字符串,事件名稱,如'onclick'等 需要加上'on'前綴。(必須)

▶ function:事件處理的函數。(必須)

這兩個方法接收相同的兩個參數:事件處理程序名稱和事件處理程序函數。由於IE8以及更早的瀏覽器版本只支持事件冒泡,所以通過attachEvent()添加的事件處理程序都會被添加到冒泡階段。在IE中使用attachEvent()與使用DOM0級方法的主要區別在於事件處理程序的作用域。在使用DOM0級的情況下,事件處理程序會在其所屬元素的作用域內運行;在使用attachEvent()方法的情況下,事件處理程序會在全局作用域中運行,因此this等於window;IE下,用attachEvent()方法指定多個事件處理程序,不是以添加它們的順序執行,而是以相反的順序被觸發。 

◆ 跨瀏覽器的事件處理程序

根據不同的瀏覽器支持的事件處理程序的不同,這裏將事件處理程序封裝在了一個單獨的js文件中(eventUtil.js),根據能力檢測法,分別判斷當瀏覽器支持DOM2級、IE事件處理程序、DOM0級時執行的事件處理程序。

eventUtil.js

//跨瀏覽器事件處理程序
var eventUtil={
	//添加句柄(element:元素,type:事件類型,handler:事件處理函數)
	addHandler:function(element,type,handler){
		if(element.addEventListener){
			element.addEventListener(type,handler,false);
		}else if(element.attachEvent){
			element.attachEvent("on"+type,handler);
		}else{   //DOM0級事件處理程序的判斷
			element['on'+type]=handler; 
		}
	},
	//刪除句柄(element:元素,type:事件類型,handler:事件處理函數)
	removeHandler:function(element,type,handler){
		if(element.removeEventListener){
			element.removeEventListener(type,handler,false);
		}else if(element.detachEvent){
			element.detachEvent("on"+type,handler);
		}else{   //DOM0級事件處理程序的判斷
			element['on'+type]=null; 
		}
	}
};

這裏說明一下,上面代碼中element['on'+type]=handler;由於我們在用DOM0級向元素添加事件時,都是用諸如element.onclick的方式,爲什麼這裏不能用element.'on'+type這種語法呢?記住,這個地方是不能用'.'去連接變量和字符串的,所以這種語法是錯誤的。在js中訪問一個屬性時,所有用'.'的地方都可以用'[]'。也就是說:element.onclick與element["onclick"]是完全等價的,所以這個地方可以用element['on'+type]的方式訪問元素屬性。

事件對象

什麼是事件對象?在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象中包含着所有與事件有關的信息。包括導致事件的元素、事件的類型,以及其它與特定事件相關的信息。例如,鼠標操作導致的事件對象中,會包含鼠標位置的信息,而鍵盤操作導致的事件對象中,會包含與按下的鍵有關的信息。所有瀏覽器都支持event對象,但支持方式不同。

◆ DOM事件對象

兼容DOM的瀏覽器會將一個event對象傳入到事件處理程序中。無論指定事件處理程序時使用DOM0級或是DOM2級,都會傳入event對象。在通過HTML特性指定事件處理程序時,變量event中保存着event對象。例:<input type="button" value="" οnclick="alert(event.type)" />。event對象包含與創建它的特定事件有關的屬性和方法。觸發的事件類型不一樣,可用的屬性和方法也不一樣。不過,所有事件都會有下表列出的成員。

屬性/方法
類型
讀/寫
說明
bubbles Boolean 只讀 表明事件是否冒泡
cancelable
Boolean
只讀
表明是否可以取消事件的默認行爲
currentTarget
Element
只讀
其事件處理程序當前正在處理事件的那個元素
detail
Integer
只讀
與事件相關的細節信息
eventPhase
Integer
只讀
調用事件處理程序的階段:1表示捕獲階段,
2表示“處於目標”,3表示冒泡階段
preventDefault() Function 只讀 取消事件的默認行爲。如果cancelable爲true,
則可以使用這個方法
stopPropagation()
Function
只讀
取消事件的進一步捕獲或冒泡。如果bubbles爲
true,則可以使用這個方法
target Element 只讀 事件的目標
type String 只讀 被觸發的事件的類型
view AbstractView 只讀 與事件關聯的抽象視圖。等同於發生事件的window對象

在事件處理程序內部,對象this始終等於currentTarget的值,而target則只包含時間段額實際目標。如果直接將事件處理程序指定給了目標元素,則this、currentTarget、和target包含相同的值。

事件對象event中比較重要的兩個屬性:

▶.type屬性:用於獲取事件類型

▶.target屬性:用於獲取事件目標

事件對象event中比較重要的兩個方法:

▶.stopPropagation()方法:用於阻止事件冒泡

<body>
	<div id="box">
		<input type="button" value="按鈕" id="btn"/>
	</div>
	<script src="eventUtil.js"></script>
	<script>
		function showMessage(){
			alert("點擊了btn");
		}
		function showBox(){
			alert("點擊了box");
		}
		var box=document.getElementById("box");
		var btn=document.getElementById("btn");
		eventUtil.addHandler(box,'click',showBox);
		eventUtil.addHandler(btn,'click',showMessage);
	</script>
</body>

運行上面代碼,點擊“按鈕”時會發現,瀏覽器彈出"點擊了btn"後,隨後接着彈出"點擊了box",事件冒泡會認爲我們點擊了按鈕的同時,也點擊了它的父元素div,一直向上觸發到整個document乃至window。這種情況有時候我們在開發中並不想看到,點擊按鈕時就是點擊按鈕,點擊div時就是點擊div,互不干擾纔行。所以這裏需要阻止事件冒泡,使每個事件處理程序都不被影響。(當然有些特殊情況下還是需要冒泡的)


這樣,再點擊按鈕的時候就不會冒泡到div了,點擊div也不會接着往上冒泡。

▶.preventDefault()方法:用於阻止事件的默認行爲

比如a標籤<ahref="#">超鏈接</a>,有一個默認的行爲就是跳轉頁面,那麼以a標籤爲例,取消它的默認跳轉行爲。


運行上面代碼,a標籤將不再跳轉。

◆IE中的事件對象

IE瀏覽器中的event和非IE瀏覽器的event也是有區別的,在非IE瀏覽器中可以直接用event,它就是簡單的傳入傳出的過程;但是在IE尤其是IE8以下的版本中,引用事件對象是是要通過window.event引用的。所以爲了更好地兼容,在使用事件對象event'時,最好寫成:

event=event || window.event;

IE的event對象同樣也包含着與創建它的事件相關的屬性和方法。這些屬性和方法也會因爲事件類型的不同而不同,但所有事件對象都會包含下表所列的屬性和方法。
屬性/方法
類型
讀/寫
說明
cancelBubble Boolean 讀/寫 默認爲false,但將其設置爲true就可以取消事件冒泡
returnValue
Boolean
讀/寫
默認爲true,但將其設置爲false就可以取消事件的默認行爲
srcElement
Element
只讀
事件的目標
type
String
只讀
被觸發的事件的類型

▶ type屬性:用於獲取事件類型

▶ srcElement屬性:用於獲取事件的目標

var ele=event.traget || event.srcElement; //獲取事件目標,兼容IE瀏覽器和非IE瀏覽器

▶ cancelBubble屬性:用於阻止事件冒泡

設置爲true表示阻止冒泡,設置爲false表示不阻止冒泡。

▶ returnValue屬性:用於阻止事件的默認行爲

設置爲false表示阻止事件的默認行爲

◆ 跨瀏覽器的事件對象

同上面跨瀏覽器的事件處理程序一樣,根據不同的瀏覽器支持的事件對象的不同,這裏將事件對象相關操作封裝在了一個單獨的js文件中(eventUtil.js),根據能力檢測法,分別判斷當瀏覽器支持DOM對象、IE事件對象時採用的事件對象。

完整的eventUtil.js
//跨瀏覽器事件處理程序
var eventUtil={
	//添加句柄(element:元素,type:事件類型,handler:事件處理函數)
	addHandler:function(element,type,handler){
		if(element.addEventListener){
			element.addEventListener(type,handler,false);
		}else if(element.attachEvent){
			element.attachEvent("on"+type,handler);
		}else{   //DOM0級事件處理程序的判斷
			element['on'+type]=handler; 
		}
	},
	//刪除句柄(element:元素,type:事件類型,handler:事件處理函數)
	removeHandler:function(element,type,handler){
		if(element.removeEventListener){
			element.removeEventListener(type,handler,false);
		}else if(element.detachEvent){
			element.detachEvent("on"+type,handler);
		}else{   //DOM0級事件處理程序的判斷
			element['on'+type]=null; 
		}
	}
	//獲得事件對象
	getEvent:function(){
		return event?event:window.event;
	},
	//獲得事件類型
	getType:function(){
		return event.type;
	},
	//獲得事件目標元素
	getElement:function(){
		return event.target || event.srcElement;
	}
	//阻止默認行爲
	preventDefault:function(event){
		if(event.preventDefault){
			event.preventDefault();
		}else{
			event.returnValue=false;
		}
	},
	//阻止事件冒泡
	stopPropagation:function(event){
		if(event.stopPropagation){  //以屬性的形式進行判斷
			event.stopPropagation();
		}else{
			event.cancelBubble=true;
		}
	}
};












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章