js筆記--事件

事件

JavaScript與HTML之間的交互是通過事件實現的。時間就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。可以使用偵聽器來預訂事件,以便事件發生時執行相應的代碼。即觀察員模式。支持頁面的行爲(js代碼)與頁面外觀(HTML和CSS代碼)之間的鬆散耦合。

1.事件流

問題:頁面的哪一部分會擁有某個特定的事件?如果單擊了某個按鈕,單擊事件不僅發生在按鈕上,同時還發生在按鈕的容器元素,甚至整個頁面。
事件流描述的是從頁面中接收事件的順序。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕獲流。(事件觸發先後順序不同)

1.事件冒泡

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

如下圖:當我們點擊div的時候,click事件沿DOM樹向上傳播。在每級節點上都會發生,直至傳播到document對象。


IE9,Firefox,Chrome,Safari將事件一直冒泡到window對象。

2.事件捕獲

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

document對象首先接收到click事件,然後事件沿DOM樹一次向下,一直傳播到事件的實際目標,即<DIV>元素


優先使用事件冒泡,在有特殊需要時再使用事件捕獲。

3.DOM事件流

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

事件捕獲階段:爲截獲事件提供了機會。

實際目標接收到事件

冒泡階段:可以在這個階段對事件做出響應。


2.事件處理程序

事件就是用戶或瀏覽器自身執行的某種動作。如:click,load和mouseover,都是事件的名字。而響應某個事件的函數就叫做事件處理程序(事件偵聽器)。事件處理程序的名字以"on"開頭,因此click事件的事件處理程序就是onclick,load就是onload。

1.HTML事件處理程序

<html>
	<head>
		<title>事件</title>
	</head>
	<body>
		<input type="button" οnclick="buttonClick()" value="點我">
	</body>
<script>
	function buttonClick(){
		alert("hello world!");
	}
事件處理程序中的代碼在執行過程中,有權訪問全局作用域中的任何代碼。
<input type="button" οnclick="alert(event.type)" value="點我">
通過event變量可以直接訪問事件對象
<input type="button" οnclick="alert(this.value)" value="點我">
在函數內部,this值等於事件的目標元素

可直接訪問value屬性,因爲函數內部使用with擴展了作用域
<input type="button" οnclick="alert(value)" value="點我">
擴展如下:
	function(){
		with(document){
			with(this){
				//直接訪問元素屬性
				console.log(value);
			}
		}
	}
如果是表單元素,擴展如下:可以直接訪問表單元素
	function(){
		with(document){
			with(this.form){
				with(this){
					//直接訪問元素屬性
					console.log(value);
				}
			}
		}
	}
事件處理程序直接訪問表單字段
		<form method="post">
			<input type="text" name="username" value="zhangsan">
			<input type="button" οnclick="alert(username.value)" value="點我">
		</form>
缺點一:如果用戶在解析前面的buttonClick()函數之前就點擊了按鈕,就會報錯(此時函數並不存在)。
缺點二:這樣擴展作用域鏈在不同的瀏覽器會導致不同的結果。
通過HTML指定事件處理程序的最後一個缺點就是HTML與JavaScript緊密耦合

2.DOM0級事件處理程序

每個元素(包括window和document)都有自己的事件處理程序屬性,這些屬性通常全部小寫,如:onclick

通過給這種屬性值設置函數來指定事件處理程序,該函數被認爲是元素的方法。因此,這時候的事件處理程序是在元素的作用域中運行。也就是說程序中的this引用當前元素。

	var btn = document.getElementById("heheda");
	btn.onclick = function(){//作爲元素的方法
		alert(this.id);//this指向的是點擊的button元素
	}

這種方式添加的事件處理程序會在事件流的冒泡階段被處理。

刪除事件:  btn.onclick  = null

3.DOM2級事件處理程序

addEventListener():給元素添加事件
removeEventListener():給元素刪除事件
都接收3個參數:  1.要處理的事件名  2.事件處理函數  3,布爾值,true:捕獲階段調用;false:冒泡階段調用
	var div = document.getElementById("mydiv");
	var btn = document.getElementById("mybtn");
	div.addEventListener("click",function(){
		alert("div");
	},false);
	btn.addEventListener("click",function(){
		alert("btn");
	},false);
	btn.addEventListener("click",function(){
		alert("btn2");
	},false);
	btn.removeEventListener("click",function(){
		//沒有用,需要與添加時傳入的函數參數一致才行。(添加時使用的匿名函數,無法刪除)
	},false);
添加和刪除中使用相同的函數參數


	var div = document.getElementById("mydiv");
	var btn = document.getElementById("mybtn");
	btn.addEventListener("click", handler,false);
	btn.removeEventListener("click",handler,false);
	function handler(){
		alert(this.id);
	}
注意:大多數情況下,都是將事件處理程序添加到事件流的冒泡階段,這樣可以最大限度地兼容各種瀏覽器。最好只在需要在事件到達目標之前截獲它的時候將事件處理程序添加到捕獲階段。如果不是特別需要,我們不建議在事件捕獲階段註冊事件處理程序。

4.跨瀏覽器事件處理程序

EventUtil:
	var btn = document.getElementById("mybtn");
	var EventUtil = {
		addHandler:function(element,type,handler){
			//能力檢測
			if(element.addEventListener){
				element.addEventListener(type,handler,false);
			}else if(element.attachEvent){
				element.attachEvent("on"+type,handler);//事件處理程序在全局作用域運行,this等於window
			}else{
				element["on"+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{
				element["on"+type] = null;
			}
		}
	}
	EventUtil.addHandler(btn,"click",function(){
		alert(this.id);
	});
默認採用DOM0級方法。
DOM0級對每個事件只支持一個事件處理程序。

3.事件對象

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

1.DOM中的事件對象

無論指定事件處理程序時使用什麼方法(DOM0級或DOM2級),都會傳入event對象。
	var div = document.getElementById("mydiv");
	var btn = document.getElementById("mybtn");
	//DOM0
	btn.onclick = function(event){
		alert(event.type);
		console.log(event.currentTarget === this); //true
		console.log(event.target === this);     //true
	}
	//DOM2
	 div.addEventListener("click",function(event){
		alert(event.type);
		console.log(event.currentTarget === this);  //true   當前執行這個事件的元素
		console.log(event.target === this);  // fasle 
		console.log(event.target === btn);   //true   點擊的目標元素
	},false);
單擊按鈕時,
target  :事件的目標(實際點擊的元素btn)
currentTarget:事件處理程序當前正在處理事件的那個元素(btn沒有點擊處理事件,冒泡到div)
在事件處理程序內部,對象this始終指向currentTarget

一個函數處理多個事件:通過event.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;

preventDefault():阻止特定事件默認行爲
	var a  = document.getElementById("aLink");
	a.onclick = function(event){
		event.preventDefault(); //阻止鏈接的默認事件
	}
cancelable屬性爲true的事件才能用上述方法取消默認事件。

stopPropagation():立即停止事件在DOM層次中的傳播,取消進一步的事件捕獲或冒泡。
	var div = document.getElementById("mydiv");
	var btn = document.getElementById("mybtn");
	var a  = document.getElementById("aLink");
	btn.onclick = function(event){
		alert("hehe");
		event.stopPropagation();//阻止事件冒泡
	}
	div.onclick = function(){
		alert("haha");
	}

eventPhase:調用事件處理程序的階段:1.捕獲階段,2.表示"處於目標",3.冒泡階段
		var div = document.getElementById("div");
		var btn = document.getElementById("btn");
		btn.onclick = function(event){//儘管"處於目標“發生在冒泡階段
			console.log(event.eventPhase);//2 
		}
		div.addEventListener("click",function(event){
			console.log(event.eventPhase);// 1
		},true);//事件捕獲階段
		div.addEventListener("click",function(event){
			console.log(event.eventPhase); //3
		},false);//事件冒泡階段
注意:只有在事件處理程序執行期間,event對象纔會存在,一旦事件處理程序執行完成,event對象就會被銷燬

2.IE中的事件對象

與訪問DOM中的event對象不同,要訪問IE中的event對象有幾種不同的方式,取決於指定事件處理程序的方法。
DOM0級方法添加事件處理程序:event對象作爲window對象的一個屬性存在
........

3.跨瀏覽器的事件對象

IE中event對象的全部信息和方法DOM對象中都有,只不過實現方式不一樣。
		var EventUtil = {
			addHandler:function(element,type,handler){},
			removeHandler:function(element,type,handler){},
			getEvent:function(event){
				return event?event:window.event;
			},
			getTarget:function(event){
				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;
				}
			}
		}

4.事件類型

Web瀏覽器中可能發生的事件有很多類型。DOM3級事件規定如下:
  • UI(User Interface,用戶界面)事件,用戶與頁面元素交互時觸發;
  • 焦點事件,元素獲得或失去焦點時觸發;
  • 鼠標事件,用戶通過鼠標在頁面上執行操作時觸發;
  • 滾輪事件,使用鼠標滾輪時觸發;
  • 文本事件,在文檔中輸入文本時觸發;
  • 鍵盤事件,通過鍵盤操作頁面時觸發
  • 合成事件,IME(Input  Method  Editor,輸入法編輯器)輸入字符時觸發
  • 變動事件(mutation),底層DOM結構發生變化時觸發
  • 變動名稱事件,元素貨屬性名變動時觸發(已廢棄)

1.UI事件

load:頁面完全加載後(圖像,js,css)在window上面觸發。

方式一:js代碼執行
		window.addEventListener("load",function(event){
			console.log(event);
		},false);
方式二:給元素添加onload特性
	<body οnlοad="alert(event.target)">
		<div id="div">
			<input type="button" id="btn" value="btn">
		</div>
	</body>
一般來說,window上面發生的任何事件都可以在<body/>元素中通過特性來指定,因爲HTML中無法訪問window元素。
”DOM2級事件“規範,應該在document而非window上面觸發load事件。但是所有瀏覽器都在window上面實現了該事件,以確保向後兼容。
建議使用js方式
		EventUtil.addHandler(window,"load",function(){
			var image = document.createElement("img");
			
			EventUtil.addHandler(image,"load",function(event){
				alert(event.target.src);
			});
			document.body.appendChild(image);
			image.src  = "mark.png";
		});
首先爲window指定onload事件處理程序,因爲要向DOM中添加新元素,必須確定頁面已經加載完畢--否則操作document.body時會報錯。然後創建img元素,設置src屬性。img元素不一定要從添加到文檔後纔開始下載,只要設置了src屬性就會開始下載。
		//使用DOM0級的Image對象實現圖片的加載
		EventUtil.addHandler(window,"load",function(){
			var image = new Image();
			EventUtil.addHandler(image,"load",function(event){
				console.log(event);
			});
			image.src = "mark.png";//加載圖片
		});
該image對象無法添加到DOM樹中(有的瀏覽器將Image對象實現爲<img>元素,就可以添加)

<script>元素也會觸發load事件,以便開發人員確定動態加載的JavaScript文件是否加載完畢。只有在設置了src屬性並將元素添加到文檔後,纔會開始下載JavaScript文件。
		//設置src屬性並添加到document文檔中後纔開始加載
		EventUtil.addHandler(window,"load",function(){
			var script = document.createElement("script");
			script.src = "test.js";
			document.body.appendChild(script);
			EventUtil.addHandler(script,"load",function(){
				console.log("script loaded");
			});
		});

		//判斷樣式表是否加載
		EventUtil.addHandler(window,"load",function(event){
			var link = document.createElement("link");
			link.type = "text/css";
			link.rel = "stylesheet";
			EventUtil.addHandler(link,"load",function(){
				console.log("css loaded");
			});
			//指定href屬性並添加到document文檔中後纔開始加載
			link.href = "ceshi.css";
			document.getElementsByTagName("head")[0].appendChild(link);
		});
unload事件
在文檔被完全卸載後觸發。只要用戶從一個頁面切換到另一個頁面,就會發生unload事件。利用這個事件最多的情況是清楚引用,以避免內存泄漏。
方式一:js代碼中調用,生成的event對象在兼容DOM的瀏覽器中只包含target屬性(值爲document)
		EventUtil.addHandler(window,"unload",function(){
			console.log("unloaded");
		});
方式二:<body>元素添加特性 onunload
<body οnunlοad="console.log('hei')">
unload事件是在一切都被卸載後才觸發的,那麼在頁面加載後存在的那些對象,此時就不一定存在了。此時操作DOM節點或者元素的樣式就會報錯。
resize事件
瀏覽器窗口被調整到新的高度或寬度時,觸發該事件
		EventUtil.addHandler(window,"resize",function(){
			console.log("resize");
		});
注意:瀏覽器變化1像素就會觸發resize事件,然後隨着變化不斷重複觸發。不要在這個事件處理程序中加入大量計算的代碼(可能被頻繁執行)。
scroll事件
在window對象上發生,實際表示頁面中相應元素的變化。在混雜模式下,可以通過<body>元素的scrollLeft和scrollTop來監控這一變化。
		EventUtil.addHandler(window,"scroll",function(event){
			// console.log(document.documentElement.scrollTop);
			console.log(document.body.scrollTop);
		});

2.焦點事件

會在頁面獲得或失去焦點時觸發。與document.hasFocus()方法及document.activeElement屬性配合,可以獲取頁面上的行蹤。
  • blur:元素失去焦點時觸發。事件不會冒泡;所有瀏覽器都支持它。
  • DOMFocusIn:元素獲得焦點時觸發。與HTML事件focus等價,冒泡。DOM3級事件廢除。
  • DOMFocusOut:元素失去焦點觸發。HTML事件blur的通用版本。DOM3廢棄。
  • focus:元素獲得焦點時觸發。不冒泡;所有瀏覽器都支持
  • focusin:元素獲得焦點觸發。與focus等價。但是冒泡
  • focusout:元素失去焦點時觸發。blur通用版本。
焦點從頁面中的一個元素移動到另一個元素,依次觸發下列事件:
  1. focusout失去焦點的元素上觸發
  2. focusin獲得焦點的元素上觸發
  3. blur失去焦點的元素上觸發
  4. DOMFocusOut
  5. focus
  6. DOMFocusIn
檢測瀏覽器是否支持這些事件
var isSupported = document.implementation.hasFeature("FocusEvent","3.0");
即使focus和blur不冒泡,也可以在捕獲階段偵聽到。
		a.addEventListener("focus",function(event){
			console.log(event.eventPhase);
			console.log("a");
		},true);
		div.addEventListener("focus",function(event){//div先捕獲
			console.log(event.eventPhase);
			console.log("div");
		},true);

3.鼠標滾輪事件

  • click:用戶單擊鼠標按鈕或按下回車鍵時觸發。onclick事件處理程序可以通過鍵盤也可以通過鼠標執行。
  • dblclick:用戶雙擊鼠標時觸發
  • mousedown:按下任意鼠標按鈕時觸發。不能通過鍵盤觸發這個事件。
  • mouseenter:鼠標光標從元素外部首次移動到元素範圍之內時觸發。事件不冒泡,光標移動到後代元素上不會觸發
  • mouseleave:位於元素上方的鼠標光標移動到元素範圍之外時觸發。事件不冒泡
  • mousemove:鼠標指針在元素內部移動時重複觸發。不能通過鍵盤觸發這個事件。
  • mouseout:鼠標指針位於一個元素上方,指針移入到另一個元素時觸發。移入的另一個元素可能位於前一個元素的外部,也可能是這個元素的子元素
  • mouseover:鼠標指針位於元素外部,用戶將其首次移入另一個元素邊界之內時觸發。不能通過鍵盤觸發這個事件。
  • mouseup:用戶釋放鼠標按鈕時觸發。不能通過鍵盤觸發這個事件。
只有在同一個元素上相繼觸發mousedown和mouseup事件,纔會觸發click事件,觸發兩次click事件纔會觸發一次dbclick事件.
檢測瀏覽器是否支持鼠標事件
		//DOM2級事件,除dbclick,mouseenter和mouseleave外
		var isSupported  = document.implementation.hasFeature("MouseEvents","2.0");
		//DOM3級事件
		var isSupported3 = document.implementation.hasFeature("MouseEvent","3.0");
		console.log(isSupported);
		console.log(isSupported3);

1.客戶區座標位置

鼠標事件都是在瀏覽器視圖中的特定位置上發生的。位置信息保存在事件對象clientX和clientY屬性中。所有瀏覽器都支持這兩個屬性。
		var div = document.getElementById("div");
		EventUtil.addHandler(div,"click",function(event){
			console.log("x:"+event.clientX);
			console.log("y:"+event.clientY);
		});
座標值中不包括頁面滾動的距離

2.頁面座標位置

pageX,pageY:可知道事件是在頁面中的什麼位置發生的。表示鼠標光標在頁面中的位置(座標是從頁面本身而非視圖的左邊和頂邊計算)

3.屏幕座標位置

screenX,screenY:相對於整個電腦屏幕的位置。
		var div = document.getElementById("div");
		EventUtil.addHandler(div,"click",function(event){
			console.log("x:"+event.clientX);
			console.log("y:"+event.clientY);
			console.log("pageX:"+event.pageX);
			console.log("pageY:"+event.pageY);
			console.log("screenX:"+event.screenX);
			console.log("screenY:"+event.screenY);
		});

4.修改鍵

按下鼠標時同時也按下鍵盤的指定的按鍵(如:shift,ctrl,alt,meta)

			var keys = [];
			if(event.shiftKey){
				keys.push("shift");
			}
			if(event.ctrlKey){
				keys.push("ctrl");
			}
			if(event.altKey){
				keys.push("alt");
			}
			if(event.metaKey){
				keys.push("meta");
			}
			console.log(keys.join(","));

5.相關元素

mouseover和mouseout事件發生時會涉及多個元素。

mouseover:事件的主目標是獲得光標的元素,相關元素是失去光標的元素。

通過event的relatedTarget屬性來獲取相關元素,該屬性只有mouseover和mouseout事件才包含。

		//誇瀏覽器獲取relatedTarget屬性
		function getRelatedTarget(){
			if(event.relatedTarget){
				return event.relatedTarget;
			}else if(event.toElement){
				return event.toElement;
			}else if(event.fromElement){
				return event.fromElement;
			}else{
				return null;
			}
		}

6.鼠標按鈕

mousedown,mouseup事件,在其處理程序event對象中存在一個button屬性,表示按下或釋放的按鈕。

DOM的button屬性有下面3個值:0鼠標左鍵,1中間滾輪,2鼠標右鍵

		btn.addEventListener("mousedown",function(event){
			console.log(event.button);
		},false);
		btn.addEventListener("mouseup",function(event){
			console.log("MOUSEUP:"+event.button);
		},false);

7.鼠標滾輪事件mousewheel

當用戶通過鼠標滾輪與頁面交互,在垂直方向上滾動頁面時(無論向上還是向下),就會觸發mousewheel事件。這個事件可以在任何元素上面觸發,最終會冒泡到document或window對象。事件對應的event對象包含一個特殊的wheelDelta屬性。向前滾動時,wheelDelta是120的倍數;向後滾動是-120的倍數。

	//opera9.5之前值是相反的
	EventUtil.addHandler(document,"mousewheel",function(event){
		//兼容早期Opera版本
		var delta = (client.engine.opera && client.engine.opera<9.5 ? -event.wheelDelta:event.wheelDelta);
		console.log(delta);
	});

 4.鍵盤與文本事件

主要遵循DOM0級。

keydown:按下鍵盤上的任意鍵時觸發,按住不放會重複觸發此事件

keypress:按下鍵盤上的字符鍵時觸發,按住不放會重複觸發

keyup:釋放鍵盤上的鍵時觸發。

一個文本事件:textInput。在文本插入文本框之前觸發

1.鍵碼

keydown和keyup事件觸發時,event對象的keyCode屬性中會包含一個代碼,與鍵盤上特定的鍵對應。
	EventUtil.addHandler(text,"keydown",function(event){
		console.log(event.keyCode);
	});

2.字符編碼

	EventUtil.addHandler(text,"keypress",function(event){
		 console.log(event.charCode);
		 if(typeof event.charCode=='number'){
		 	console.log(String.fromCharCode(event.charCode));
		 	return event.charCode;
		 }else{
		 	console.log(event.keyCode);
		 	return event.keyCode;
		 }
	});

3.textInput事件

在可編輯區域中輸入字符時,就會觸發這個事件。用來替代keypress事件

與keypress區別:

1.任何可以獲得焦點的元素都可以觸發keypress事件,只有可編輯區域才能觸發textInput事件

2.textInput事件只會在用戶按下能夠輸入實際字符的鍵時纔會被觸發,keypress在按下能夠影響文本顯示的 鍵時就會觸發(如退格鍵)

event.data:按鍵的值

	EventUtil.addHandler(text,"textInput",function(event){
		console.log(event.data);
	});

5.變動事件

DOM中的某一部分發生變化時觸發
  • DOMSubtreeModified:DOM結構中發生任何變化時觸發
  • DOMNodeInserted:節點作爲子節點被插入到另一個節點中時觸發
  • DOMNodeInsertedIntoDocument:節點被直接插入文檔或通過子樹間接插入文檔後觸發。在DOMNodeInserted後觸發
  • DOMNodeRemoved:節點從父節點中被移除時觸發
  • DOMNodeRemovedFromDocument:節點被直接移除或通過子樹間接移除之前觸發。DOMNodeRemoved之後觸發
  • DOMAttrModified:元素特性被修改後觸發
  • DOMCharacterDataModified:文本節點的值發生變化時觸發。

1.刪除節點

使用removeChild()或replaceChild()從DOM中刪除節點時,首先觸發DOMNodeRemoved事件。event.target是被刪除的節點。event.relatedNode屬性中包含着對目標節點父節點的引用。事件觸發時節點還沒從父節點中刪除,parentNode屬性仍然指向父節點(與event.relatedNode相同)。事件會冒泡,可以在DOM的任何層次上處理。


如果被移除的節點包含子節點,那麼在其所有子節點以及這個被移除的節點上會相繼觸發DOMNodeRemovedFromDocument事件。但這個事件不會冒泡,只有直接給其中一個子節點的事件處理程序纔會被調用。

	var ul = document.getElementById("myList");
	//DOM發生變化就 觸發
	EventUtil.addHandler(document,"DOMSubtreeModified",function(event){ //4
		console.log(event.target);
	});
	//事件冒泡到document處理
	EventUtil.addHandler(document,"DOMNodeRemoved",function(event){//1
		console.log(event.type);
		console.log(event.type);
		console.log(event.target);
		console.log(event.relatedNode == document.body);//true
	});
	EventUtil.addHandler(ul,"DOMNodeRemovedFromDocument",function(){//2
		console.log("ul:DOMNodeRemovedFromDocument");
	});
	EventUtil.addHandler(ul.firstChild,"DOMNodeRemovedFromDocument",function(event){//3
		console.log(event.type);
		console.log(event.target);
		console.log("li:DOMNodeRemovedFromDocument");
	});
	ul.parentNode.removeChild(ul);

2.插入節點

使用appendChild(),replaceChild()或insertBefore()向DOM中插入節點時 

1.觸發DOMNodeInserted事件。事件的目標是被插入的節點,event.relatedNode包含對父節點的引用。在觸發這個事件時,節點已經被插入到了新的父節點中。事件冒泡,可在DOM的各個層次上處理。


2.在新插入的節點上觸發DOMNodeInsertedIntoDocument事件。事件不冒泡,必須在插入節點前添加事件處理程序。


3.觸發DOMSubtreeModified

	var ul = document.getElementById("myList");
	var li = document.createElement("li");
	li.appendChild(document.createTextNode("Item 4"));
	EventUtil.addHandler(ul,"DOMNodeInserted",function(event){
		console.log(1);
		console.log(event.type);
		console.log(event.target);
	});
	EventUtil.addHandler(document,"DOMNodeInserted",function(event){//冒泡
		console.log(2);
		console.log(event.type);
		console.log(event.target);
	});
	EventUtil.addHandler(li,"DOMNodeInsertedIntoDocument",function(event){
		console.log(3);
		console.log(event.type);
		console.log(event.target);
	});
	EventUtil.addHandler(document,"DOMSubtreeModified",function(event){
		console.log(4);
		console.log(event.type);
		console.log(event.target); //UL
	});
	ul.appendChild(li);

6.HTML5事件

1.contextmenu事件

用於表示何時應該顯示上下文菜單。

contextmenu事件冒泡,可以給document指定一個事件處理程序,用來處理頁面中所有這類事件。

取消該事件:event.preventDefalut()

簡單右鍵菜單樣式:

	var div = document.getElementById("myDiv");
	var ul = document.getElementById("myList");
	EventUtil.addHandler(div,"contextmenu",function(event){
		event.preventDefault();//阻止默認的右鍵事件
		ul.style.left = event.clientX+"px";
		ul.style.top = event.clientY+"px";
		ul.style.visibility = "visible";
	});
	EventUtil.addHandler(document,"click",function(event){
		ul.style.visibility = "hidden";
	});

2.beforeunload事件

在頁面卸載前阻止該操作。該事件會在瀏覽器卸載頁面前觸發,可通過它取消卸載並繼續使用原有頁面。

爲顯示彈出對話框,必須將event.returnValue的值設置爲要顯示給用戶的字符串,同時作爲函數的值返回。
	EventUtil.addHandler(window,"beforeunload",function(event){
		var msg = "確定退出?";
		event.returnValue = msg;
		return msg;
	});

3.DOMContentLoaded事件

window的load事件是在頁面中的一切都加載完後觸發,但這個過程可能會因爲要加載的外部資源過多而頗費周折。
DOMContentLoaded事件是在DOM樹加載完後就觸發,忽略圖像,js文件,css文件的加載。該事件支持在頁面下載的早期添加事件處理程序,用戶能夠儘早地與頁面交互。
給document或window添加相應的事件處理程序(這個事件會冒泡到window,但它的目標實際上是document)
	EventUtil.addHandler(window,"load",function(event){
		console.log("window  load");
	});
	EventUtil.addHandler(document,"DOMContentLoaded",function(event){
		console.log(event.target); //document
		console.log("DOM loaded"); 
	});
對不支持DOMContentLoaded的瀏覽器,可以在頁面加載期間設置一個超時調用,但是無法保證會早於load事件
setTimeout(function(){
      //添加事件處理
},0);

4.readystatechange事件

提供與文檔或元素的加載狀態有關的信息。
readyState屬性:
  • uninitialized(未初始化):對象存在但尚未初始化
  • loading(正在加載):對象正在加載數據
  • loaded(加載完畢):對象加載數據完成
  • interactive(交互):可以操作對象的,但還沒有完全加載
  • complete(完成):對象已經加載完畢
對於document而言,值爲"interactive"的readyState會在與DOMContentLoaded大致相同的時刻觸發readystatechange事件。此時DOM樹已經加載完畢,可以安全操作它了,因此就會進入(interactive)交互階段。但與此同時,圖像及其他外部文件不一定可用。
	EventUtil.addHandler(document,"readystatechange",function(event){
		console.log(document.readyState);
		console.log(event);
	});
	EventUtil.addHandler(window,"load",function(){
		console.log("load");
	});
與load事件一起使用時,無法預測兩個事件觸發的先後順序。外部資源多時,交互階段更有可能早於完成階段;頁面外部資源少時,完成階段早於交互階段的可能性更大。
同時檢測交互和完成階段。
	EventUtil.addHandler(document,"readystatechange",function(event){
		if(document.readyState =='interactive' || document.readyState == 'complete'){
			EventUtil.removeHandler(document,"readystatechange",arguments.callee);
			console.log("loaded");
		}
	});
如果進入交互階段或完成階段,就移除相應的事件處理程序以免在其他階段再執行。

<script>和<link>元素也可以觸發readystatechange事件,可以用來確定外部的JavaScript和CSS文件是否已經加載完成。必須將元素添加到頁面後纔會開始下載外部資源。
	EventUtil.addHandler(window,"load",function(event){
		var script = document.createElement("script");
		var link = document.createElement("link");
		link.type = "text/css";
		link.rel = "stylesheet";

		EventUtil.addHandler(script,"readystatechange",function(event){//<span style="color:#ff0000;">事件沒觸發?</span>
			console.log(event);
			var target = event.target;
			console.log(target == this);
			if(target.readyState == "loaded"||target.readyState=="complete"){
				//取消匿名的事件處理程序
				EventUtil.removeHandler(target,"readystatechange",arguments.callee);
			}
		});
		EventUtil.addHandler(link,"readystatechange",function(event){
			console.log("css");
		});
		script.src = "/test.js";
		document.body.appendChild(script);
		link.href="test.css";
		document.getElementsByTagName("head")[0].appendChild(link);
	});
無法觸發readystatechange事件?

5.pageshow和pagehide事件

頁面緩存在瀏覽器中後,當請求的頁面是從緩存中取的,就不會觸發load事件。
pageshow會在頁面顯示時觸發(load觸發後觸發),無論來自緩存還是重新加載。事件的目標是document,但必須將事件處理程序添加到window。
 	(function(){
 		var showCount = 0;
		EventUtil.addHandler(window,"load",function(){
			console.log("load");
		});
		EventUtil.addHandler(window,"pageshow",function(){
			showCount++;
			console.log(event.persisted);//頁面是否來自緩存
			console.log("pageshow"+showCount);
		});
 	})();

pagehide,在瀏覽器卸載頁面時觸發,在unload事件之前觸發。在document上觸發,但事件處理程序必須添加到window對象。

指定了onunload事件處理程序的頁面會自動排除在緩存外。即使事件處理程序是空的。因爲onunload常用於撤銷在onload中所執行的操作,而跳過onload後再顯示頁面很可能會導致頁面不正常(頁面被卸載,再次顯示時不去load,可能導致一些初始化數據的操作不執行)。

6.hashchange事件

URL參數列表(URL中#後面的所有字符串)發生變化時觸發。事件處理程序必須添加給window對象。event包含 oldURL和newURL兩個額外屬性。
	EventUtil.addHandler(window,"hashchange",function(event){
		console.log(event.oldURL+"   "+event.newURL);
		console.log(location.hash);	
	});

5.內存和性能

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

1.事件委託

解決事件處理程序過多的問題。利用事件冒泡,指定一個事件處理程序就可以管理某一類型的所有事件。
使用事件委託,只需要在DOM樹中儘量最高的層次上添加一個事件處理程序。
		<ul id="myList">
			<li id="item1">Item 1</li>
			<li id="item2">Item 2</li>
			<li id="item3">Item 3</li>
		</ul>
	var myList = document.getElementById("myList");
	EventUtil.addHandler(myList,"click",function(event){
		var target = event.target;
		if(target.id=='item1'){
			console.log("item1 clicked");
		}else if(target.id=='item2'){
			console.log("item2 clicked");
		}else if(target.id == 'item3'){
			console.log("item3 clicked");
		}
	});
如果可行,可以考慮給document對象添加事件處理程序,用以處理頁面上發生的某種特定類型的事件,有點如下:

  • document對象很快就可以訪問,而且可以在頁面生命週期的任何時點上爲它添加事件處理程序(無需等待DOMContentLoaded或load事件)
  • 在頁面中設置事件處理程序所需的時間更少。只添加一個事件處理程序所需的DOM引用少,花的時間也少
  • 整個頁面佔用的內存空間更少,能夠提升整體性能。
適合事件委託的事件包括:click,mousedown,mouseup,keydown,keyup和keypress

2.移除事件處理程序

每當將事件處理程序指定給元素時,運行中的瀏覽器代碼與支持頁面交互的JavaScript代碼之間就會建立一個連接。這種連接越多,頁面執行起來就越慢。內存中留存的過時不用的"空事件處理程序",也是造成web應用程序內存與性能問題的主要原因。


情形一:從文檔中刪除帶有事件處理程序的元素時。原來添加到元素的事件處理程序可能無法被當作垃圾回收。

	var div  = document.getElementById("myDiv");
	var btn  = document.getElementById("btn");
	btn.onclick = function(){
		btn.onclick = null; //移除事件處理程序
		div.innerHTML = "hehe";
	}
注意:在事件處理程序中刪除目標元素也能阻止事件冒泡。目標元素在文檔中是事件冒泡的前提。

情形二:頁面卸載(頁面卸載前沒有清理乾淨事件處理程序)。在頁面卸載前,通過onunload事件處理程序移除所有事件處理程序。

只要是通過onload事件處理程序添加的東西,最後就要通過onunload事件處理程序移除。

6.模擬事件

1.DOM中的事件模擬

可以在document對象上使用createEvent()方法創建event對象。方法接收一個參數,表示要創建的事件類型的字符串。

  • UIEvents:一般化的UI事件,鼠標和鍵盤事件都繼承自UI事件
  • MouseEvents:一 般化的鼠標事件。
  • MutationEvents:一般化的DOM變動事件
  • HTMLEvents:一般化的HTML事件
觸發事件。dispatchEvent()所有支持事件的DOM節點都支持這個方法。方法接收一個參數,表示要觸發事件的event對象。

1.模擬鼠標事件

initMouseEvent():初始化鼠標事件信息。15個參數
  • type(字符串):要觸發的事件類型,如“click”。
  • bubbles(布爾值):是否冒泡,true
  • cancelable(布爾值):是否可取消,true
  • view:與事件關聯的視圖。幾乎總是設置爲document.defaultView
  • detail(整數):與事件有關的詳細信息。一般只有事件處理程序使用,通常設置爲0
  • screenX:相對於屏幕的座標
  • screenY:
  • clientX:相對於瀏覽器窗口的座標
  • clientY:
  • ctrlKey(布爾值):是否按下了Ctrl鍵。默認false
  • altKey(布爾值):是否按下的Alt鍵。默認false
  • shiftKey(布爾值):是否按下了Shift鍵。默認false
  • metaKey(布爾值):是否按下了Meta鍵。默認false
  • button(整數):表示按下了哪個鼠標鍵。默認0
  • relatedTarget(對象):與事件相關的對象。只在模擬mouseover或mouseout時使用
	var div  = document.getElementById("myDiv");
	var btn  = document.getElementById("btn");
	btn.onclick = function(){
		btn.onclick = null; //移除事件處理程序
		div.innerHTML = "hehe";
	}
	//創建事件對象
	var event = document.createEvent("MouseEvents");
	//初始化事件對象
	event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
	//觸發事件
	btn.dispatchEvent(event);

2.模擬鍵盤事件

DOM3級規定,調用createEvent()並傳入"KeyboardEvent"就可以創建一個鍵盤事件。返回的事件對象包含一個initKeyEvent()方法
  • type:要觸發的事件類型:如"keydown"
  • bubbles:事件是否應該冒泡。true
  • cancelable:事件是否可以取消。true
  • view:事件關聯的視圖。幾乎總是設置爲document.defaultView
  • key:按下的鍵的鍵碼
  • location:按鍵的位置。0主鍵盤,1左,2右,3數字鍵,4移動設備,5手柄
  • repeat:一行中按了這個鍵多少次
	var testbox = document.getElementById("text");
	EventUtil.addHandler(testbox,"keydown",function(event){
		console.log(event.type);
		console.log(event);
	});
	var event = null;
	//DOM3級方式創建事件對象
	if(document.implementation.hasFeature("KeyboardEvents","3.0")){
		event = document.createEvent("KeyboardEvent");
		event.initKeyboardEvent("keydown",true,true,document.defaultView,"a",0,"Shift",0);
	}
	//觸發事件
	testbox.dispatchEvent(event);

3.模擬其他事件

變動事件  createEvent("MutationEvents")創建事件對象,包含initMutationEvent()初始化方法。接收參數如下:

type,bubbles,cancelable,relatedNode,preValue,newValue,attrName,attrChange

	EventUtil.addHandler(document,"DOMNodeInserted",function(event){
		console.log(event);
		console.log("insert");
	});
	var event = document.createEvent("MutationEvents");
	var node = document.createElement("li");
	event.initMutationEvent("DOMNodeInserted",true,false,node,"","","",0);
	document.dispatchEvent(event);
HTML事件  createEvent("HTMLEvents")創建事件對象,initEvent()初始化
	var text = document.getElementById("text");
	EventUtil.addHandler(text,"focus",function(event){
		console.log(event);
	});
	var event = document.createEvent("HTMLEvents");
	event.initEvent("focus",true,false);
	text.dispatchEvent(event);

4.自定義DOM事件

createEvent("CustomEvent")創建對象,initCustomEvent()初始化對象,接收4個參數:

  • type:觸發事件類型,如“keydown”
  • bubbles:事件是否冒泡
  • cancelable:事件是否可以取消
  • detail:任意值,保存在event對象的detail屬性中
	var div = document.getElementById("myDiv");
	EventUtil.addHandler(div,"myevent",function(event){
		console.log("div:"+event.detail);
	});
	EventUtil.addHandler(document,"myevent",function(event){
		console.log("document:"+event.detail);
	});
	//模擬觸發自定義事件
	if(document.implementation.hasFeature("CustomEvents","3.0")){
		event  = document.createEvent("CustomEvent");
		event.initCustomEvent("myevent",true,false,"hehe");
		div.dispatchEvent(event);
	}

發佈了49 篇原創文章 · 獲贊 12 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章