js基礎(二):事件(事件冒泡、事件捕獲、事件對象、事件委託)

一、事件

javascript與HTML之間的交互是通過事件實現的。事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。

事件流

事件流描述的是從頁面中接收事件的順序。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕獲流。

事件冒泡

IE的事件流叫做事件冒泡(event bubbling),即事件開始由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然後逐級向上傳播到較爲不具體的節點(文檔)。

事件冒泡過程:
事件冒泡過程
如果你單擊了div元素,click事件首先在div元素上發生,然後click事件沿DOM樹向上傳播,在每一級節點都會發生,直至傳播到document對象。

事件捕獲

事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。事件捕獲的用意在於在事件到達預定目標之前捕獲它。

事件捕獲過程:
事件捕獲過程
事件捕獲過程中,document對象首先接收到click事件,然後事件沿DOM樹依次向下,一直傳播到事件的實際目標div。

DOM事件流

“DOM2級事件”規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。

DOM2事件流過程:
DOM2事件流過程
在DOM事件流中,實際目標div在捕獲階段不會接收到事件,這意味着在捕獲階段,事件從document到html再到body後就停止了。下一階段是“處於目標”階段,於是事件在div上發生,並在事件處理中被看成冒泡階段的一部分。然後冒泡發生,事件又傳播迴文檔。

事件對象

在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象包含着所有與事件有關的信息。包括導致事件的元素、事件的類型以及其他與特定事件相關的信息。

DOM中的事件對象:
兼容DOM的瀏覽器會將一個event對象傳入到事件處理程序中。
event對象包含與創建它的特定事件有關的屬性與方法。觸發的事件類型不一樣,可用的屬性和方法也不一樣。不過,所有事件都會有下表列出的成員。
event1
在這裏插入圖片描述
如何通過一個函數處理多個事件,使用type屬性。

var btn = document.getElementById("myBtn");
var handler = function(event){
	switch(event.type){
		case "click":
			alert("clicked);
			break;
		case "mouseover":
			event.target.style.backgroundColor = "red";
			break;
		case "mouseout":
			event.target.style.backgroundColor = ""
			break;
	}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

IE中的事件對象:

與訪問DOM中的event對象不同,要訪問IE中的event對象有幾種不同的方式,取決於指定事件處理程序的方法。

與DOM的event對象一樣,所有事件對象會包含下表所列的屬性和方法。
IE中的事件對象

二、內存與性能

在javascript中,添加到頁面上的事件處理程序數量將直接關係到頁面的整體運行性能。首先,每個函數都是對象,都會佔用內存;內存中的對象越多,性能就越差。其次,必須事先指定所有事件處理程序而導致DOM訪問次數,會延遲整個頁面的交互就緒時間。

從如何利用好事件處理程序的角度出發,來尋找一些提升性能的方法。

事件委託

對“事件處理程序過多”問題的解決方案就是事件委託。事件委託利用了冒泡,只指定一個事件處理程序,就可以管理某一類型所有事件。

以下面代碼爲例:

<ul id="myLinks">
	<li id="goSomewhere">Go somewhere</li>
	<li id="doSomething">Do something</li>
	<li id="sayHi">Say hi</li>
</ul>

其中包含3個被單擊後會執行操作的列表項。按照傳統做法,需要爲它們分別添加3個事件處理程序。
使用事件委託,只需要在DOM樹中儘量最高的層次上添加一個事件處理程序。如:

var list = document.getElementById("myLinks");
EventUtil.addHandler(list,"click",function(event){
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);
	
	switch(target.id){
		case "goSomewhere":
			document.title = "I changed the document‘s title";
			break;
		case "goSomewhere":
			location.href = "http://www.wrox.com";
			break;
		case "sayHi":
			alert("hi);
			break;
	}
});

在這段代碼裏,我們使用事件委託只爲ul元素添加了一個onclick事件處理程序。由於所有列表項都是這個元素的子節點,而且它們的事件會冒泡。所以單擊事件最終會被這個函數處理。事件目標是被單擊的列表項,故而可以通過檢測id屬性來決定採取適當操作。
如果可以的話,也可以考慮爲document對象添加一個事件處理程序,用以處理頁面上發生的某種特定類型的事件。
最適合採用事件委託技術的事件包括:click、mousedown、mouseup、keydown、keyup和keypress

移除事件處理程序

內存中留有那些過時不用的“空事件處理程序”(dangling event handler),也是造成Web應用程序內存與性能問題的主要原因。

先來看一段代碼:

<div id="myDiv">
	<input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
	var btn = document.getElementById("myBtn");
	btn.onclick = function(){
		//先執行某些操作
		console.log("我要被替換了!");

		document.getElementById("myDiv").innerHTML = "Processing...";
</script>

這裏,有一個按鈕被包含在div元素中。爲避免雙擊,單擊這個按鈕時就將按鈕移除並替換成一條消息;這是網站設計中非常流行的一種做法。但問題在於,當按鈕被從頁面中移除時,它還帶着一個事件處理程序呢。在div元素上設置innerHTML可以把按鈕移走,但事件處理程序仍然與按鈕保持着引用關係。如果你知道某個元素即將被移除,那麼最好手工移除事件處理程序。如下:

<div id="myDiv">
	<input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
	var btn = document.getElementById("myBtn");
	btn.onclick = function(){
		//先執行某些操作
		console.log("請先移除myBtn的引用關係!");
		//移除事件處理程序
		btn.onclick = null;
		//移除頁面按鈕,替換爲文本
		document.getElementById("myDiv").innerHTML = "Processing...";
	}
</script>

在此,我們設置div的innerHTML屬性之前,先移除了按鈕的事件處理程序。這樣就確保了內存可以被再次利用,而從DOM中移除按鈕也做到了乾淨利索。
注意:在事件處理程序中刪除按鈕也能阻止事件冒泡。目標元素在文檔中是事件冒泡的前提。

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