13.1 事件流
“DOM2級事件”規(guī)定事件流包括3個階段:事件捕獲階段,處于目標(biāo)階段,事件冒泡階段。
事件捕獲表示從最外層節(jié)點開始捕獲事件,然后逐級像內(nèi)傳播直到具體節(jié)點。
事件冒泡表示事件由最具體元素開始接受元素,然后逐級向上傳播到文檔節(jié)點。
13.2 事件處理程序
13.2.1 DOM2級事件處理程序
“DOM2級事件”為所有DOM節(jié)點定義了addEventListener()和removeEventListener()兩個方法,分別用于指定事件處理程序和移除事件處理程序。都接收3個參數(shù):事件名稱、事件處理程序、一個布爾值,布爾值為true,則在捕獲階段調(diào)用事件處理程序,布爾值為false,則在冒泡階段調(diào)用事件處理程序。
使用DOM2級事件處理程序可以為同一個事件添加多個程序,會按照添加的順序觸發(fā)。addEventListener()添加的事件處理程序只能用removeEventListener()來移除,移除時傳入的參數(shù)與添加時的相同,無法刪除匿名函數(shù),因為兩個一樣的匿名函數(shù)并不是同一個函數(shù)。
13.2.2 IE事件處理程序
IE使用attachEvent()和detachEvent(),都接收兩個參數(shù),事件名稱和事件處理程序,只在冒泡階段觸發(fā)。事件名稱需要添加“on”前綴,同一事件多個程序會按添加的相反順序觸發(fā)。
13.3 事件對象
在DOM上觸發(fā)事件時,會產(chǎn)生一個event事件對象。這個對象中包含著所有與事件有關(guān)的信息。
13.3.1 DOM中的事件對象
兼容DOM的瀏覽器會將一個event對象傳入事件處理程序中,event對象所有屬性:
bubble,布爾值,只讀,表示事件是否冒泡;
cancelable,布爾值,只讀,表示是否可以取消事件默認(rèn)行為;
currentTarget,節(jié)點,只讀,當(dāng)前正在處理時間的元素,在事件處理程序內(nèi)部總是等于this值;
defaultPrevented,布爾值,只讀,為true表示已經(jīng)調(diào)用了preventDefault()方法;
detail,數(shù)值信息,只讀,事件相關(guān)細(xì)節(jié)信息;
eventPhase,數(shù)值信息,只讀,表示調(diào)用事件處理程序的階段,1表示捕獲,2表示“處于目標(biāo)”,3表示冒泡;
preventDefault(),方法,取消事件的默認(rèn)行為,cancelable為true時才可以使用;
stopImmediatePropagation(),方法,取消事件進(jìn)一步冒泡,同時阻止任何事件處理程序被調(diào)用;
stopPropagation(),方法,取消事件的進(jìn)一步冒泡,bubble為true時才可以使用;
target,節(jié)點,事件的實際目標(biāo);
trusted,布爾值,只讀,為true表示事件是由瀏覽器生成的,為false表示事件由JavaScript添加;
type,字符串,觸發(fā)的事件類型;
view,事件發(fā)生的window對象。
13.3.2 IE中的事件對象
使用DOM0級添加事件處理程序時,event對象是window的一個屬性,使用attachEvent()時,event可以通過window對象訪問,也會作為參數(shù)傳入事件處理程序。IE事件對象屬性:
cancelBubble,布爾值,默認(rèn)為false,設(shè)置為true就可以取消事件冒泡;
returnValue,布爾值,默認(rèn)為true,設(shè)置為false可以取消事件默認(rèn)行為;
srcElement,節(jié)點,事件的實際目標(biāo);
type,字符串,事件的名稱。
13.3.3 跨瀏覽器的事件模型
var EventUtil = {
// 添加事件
addHandler: function (ele, type, handler) {
if(ele.addEventListener){
ele.addEventListener(type,handler,false);
}else if(ele.attachEvent){
ele.attachEvent("on"+type,handler);
}else{
ele["on"+type] = handler;
}
},
// 移除事件
removeHandler: function (ele, type, handler) {
if(ele.removeEventListener){
ele.removeEventListener(type,handler,false);
}else if(ele.detachEvent){
ele.detachEvent("on"+type,handler);
}else{
ele["on"+type] = null;
}
},
// 獲取事件對象
getEvent:function (event) {
return event ? event : window.event;
},
// 獲取事件實際目標(biāo)
getTarget:function (event) {
return event.target || event.srcElement;
},
// 阻止默認(rèn)事件
preventDefault: function (event) {
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
// 阻止事件冒泡
stopPropagation:function (event) {
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
},
// 獲取mouseover和mouseout事件觸發(fā)時的相關(guān)元素
getRelatedTarget:function (event) {
if(event.relatedTarget){
return event.relatedTarget;
}else if(event.toElement){
return event.toElement;
}else if(event.fromElement){
return event.fromElement;
}else{
return null;
}
},
// 獲取mousedown和mouseup事件觸發(fā)時的按鍵信息
getButton:function (event) {
if(document.implementation.hasFeature("MouseEvents","2.0")){
return event.button;
}else{
switch (event.button){
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;
case 2:
case 6:
return 2;
case 4:
return 1;
}
}
},
// 獲取mousewheel事件(firefox下為DOMMouseScroll事件)鼠標(biāo)滾輪增量值
getWheelDelta:function (event) {
if(event.wheelDelta){
return event.wheelDelta;
}else {
return -event.detail * 40;
}
},
// 獲取鍵盤事件中按鍵的字符編碼
getCharCode:function (event) {
if(typeof event.charCode === "number"){
return event.charCode;
}else{
return event.keyCode;
}
},
// 獲取剪貼板中的數(shù)據(jù)
getClipboardText:function (event) {
var clip = (event.clipboardData || window.clipboardData);
return clip.getData("text");
},
// 設(shè)置剪貼板中的數(shù)據(jù)
setClipboardText:function (event,value) {
if(event.clipboardData){
return event.clipboardData.setData("text/plain",value);
}else if(window.clipboardData){
return window.clipboardData.setData("text",value);
}
}
};
13.4 事件類型
13.4.1 UI事件
load事件,當(dāng)頁面完全加載后(包括所有圖像,JavaScript文件,CSS文件等外部資源),就會在window上面觸發(fā)該事件。
圖像上面也可以觸發(fā)load事件,當(dāng)圖像加載完畢時就會觸發(fā)綁定在img圖像上的load事件,在新建圖像元素時(無論是DOM創(chuàng)建img標(biāo)簽還是創(chuàng)建新的Image對象),只要設(shè)置了src屬性,圖像就會開始下載。為保證能觸發(fā)事件,要先綁定事件,再為圖像設(shè)置src屬性。
<script>元素也會觸發(fā)load事件,與圖像不同,只有在設(shè)置了<script>元素的src屬性,并且將該元素添加到文檔后,才會開始下載JavaScript文件,所以綁定事件處理程序和設(shè)置src屬性的先后順序沒有需求。
unload事件,文檔被完全卸載后觸發(fā),從一個頁面切換到另一個頁面時,就會觸發(fā)unload事件。
resize事件,當(dāng)瀏覽器窗口被調(diào)整到一個新的高度或?qū)挾葧r(包括最大化和最小化),就會觸發(fā)resize事件。瀏覽器窗口變化的過程中,resize事件會被不斷的觸發(fā)。
scroll事件,當(dāng)用戶滾動帶滾動條的元素中的內(nèi)容時,在該元素上面觸發(fā)。scroll事件也會在文檔被滾動期間不斷觸發(fā)。
13.4.2 焦點事件
focus事件,元素獲得焦點時觸發(fā),不冒泡。
focusin事件,與focus等價,但會冒泡。在focus之后觸發(fā)。
blur事件,元素失去焦點時觸發(fā),不冒泡。
focusout事件,與blur等價,但會冒泡。在blur之后觸發(fā)。
13.4.3 鼠標(biāo)與滾輪事件
click事件,單機鼠標(biāo)左鍵或按下回車時觸發(fā),表示既可以通過鼠標(biāo)也可以通過鍵盤觸發(fā)。
dblclick事件,雙擊鼠標(biāo)左鍵時觸發(fā)。
mousedown事件,按下鼠標(biāo)任意按鍵時觸發(fā),不能通過鍵盤觸發(fā)。
mouseup事件,釋放鼠標(biāo)任意按鍵時觸發(fā),不能通過鍵盤觸發(fā)。
mouseleave事件,在位于元素上方的鼠標(biāo)光標(biāo)移動到元素范圍之外時觸發(fā)。不冒泡,子元素包含的范圍也是元素的范圍。
mouseenter事件,在鼠標(biāo)光標(biāo)從元素外部首次移動到元素范圍之內(nèi)時觸發(fā)。不冒泡,子元素包含的范圍也是元素的范圍。
mouseout事件,鼠標(biāo)指針從一個元素上方,移入另一個元素時觸發(fā),另一個元素可以是這個元素內(nèi)部的子元素。
mouseover事件,鼠標(biāo)指針位于一個元素外部,然后用戶將其首次移入這個元素邊界之內(nèi)時觸發(fā)。
如果mousedown和mouseup中一個被取消,則不會觸發(fā)click事件,同樣只有觸發(fā)兩次click事件,才會觸發(fā)dblclick事件。
clientX和clientY屬性,這兩個屬性保存了鼠標(biāo)事件發(fā)生時,鼠標(biāo)指針在視口中的水平和垂直坐標(biāo)。
pageX和pageY屬性,這兩個屬性保存了鼠標(biāo)事件發(fā)生時,鼠標(biāo)指針在整個頁面中的水平和垂直坐標(biāo)。頁面沒有滾動時,與clientX和clientY屬性的值相等。
screenX和screenY屬性,這兩個屬性保存了鼠標(biāo)事件發(fā)生時,鼠標(biāo)指針相對于整個顯示器屏幕的水平和垂直坐標(biāo)。
shiftkey、ctrlKey、altKey、metaKey4個修改鍵屬性為布爾值,分表表示事件發(fā)生時,Shift、Ctrl、ALT、Meta(Window鍵或Cmd鍵)4個按鍵是否被按下。
mouseover和mouseout事件發(fā)生時,DOM為事件對象提供了relatedTarget屬性來表示相關(guān)元素,mouseover的相關(guān)元素是失去光標(biāo)的那個元素,mouseout的相關(guān)元素是獲得光標(biāo)的元素,其他事件的這個值為null。在IE中,mouseover的相關(guān)元素保存在fromElement屬性中,mouseout的相關(guān)元素保存在toElement屬性中。
mousedown和mouseup事件發(fā)生時,DOM為事件對象提供了button屬性來表示鼠標(biāo)的按鍵,0表示鼠標(biāo)主按鍵,1表示鼠標(biāo)中鍵(鼠標(biāo)滾輪按鍵),2表示鼠標(biāo)次按鍵。
事件對象中還有一個detail屬性,表示在指定位置單擊了多少次,相繼發(fā)生一次mousedown和一次mouseup算做一次單擊,鼠標(biāo)移動了位置后detail會被重置為0。
滾輪事件mousewheel,當(dāng)用戶通過鼠標(biāo)滾輪與頁面交互時,會觸發(fā)mousewheel事件,事件觸發(fā)時事件對象上有一個wheelDelta屬性,向上滾動時,這個值是120的倍數(shù),向下滾動時,這個值是-120的倍數(shù)。
在Firefox中滾輪事件為DOMMouseScroll,其表示滾輪信息的屬性為detail,向上滾動時,這個值為-3,向下滾動時,這個值為3。
觸摸設(shè)備上不支持dblclick事件,雙擊瀏覽器窗口會放大畫面,沒有辦法改變其行為。
兩只手指放在屏幕上且頁面隨手指移動時會觸發(fā)mousewheel事件和scroll事件。
13.4.4 鍵盤與文本事件
keydown事件,按下任意按鍵時觸發(fā),按住不放則重復(fù)觸發(fā)。
keypress事件,按下任意字符按鍵時觸發(fā),按住不放則重復(fù)觸發(fā)
keyup事件,釋放鍵盤上的按鍵時觸發(fā)。
textInput事件,在文本插入文本框之前觸發(fā)。
這4個事件的發(fā)生順序為keydown,keypress,textInput,keyup,這4個事件也同樣支持修改鍵屬性。
發(fā)生keydown和keyup事件時,事件對象keycode中會包含一個鍵碼表示是哪個按鍵,與ASCII碼中的小寫字母或數(shù)字的編碼相同。與大小寫無關(guān)。唯一不同點為分號,IE/Safari/Chrome返回186,F(xiàn)irefox/Opera返回59。
keypress事件發(fā)生時,事件對象有一個charCode屬性,表示所按下按鍵的ASCII編碼,使用String.fromCharCode()方法可以將其轉(zhuǎn)為實際的字符。
keypress事件和textInput事件的區(qū)別:一是所有可以獲得焦點的元素都會觸發(fā)keypress事件,但只有可編輯區(qū)域會觸發(fā)textInput事件,二是只有按下能夠?qū)嶋H輸入字符的按鍵才會觸發(fā)textInput事件,但按下影響文本顯示的按鍵也會觸發(fā)keypress事件(例如退格按鍵)。
13.4.6 變動事件
DOMSubtreeModified事件,當(dāng)DOM結(jié)構(gòu)中發(fā)生任何變化時觸發(fā),這個事件在其他任何變動事件觸發(fā)后都會觸發(fā)。
DOMNodeInserted事件,在一個節(jié)點作為子節(jié)點被插入到另一個節(jié)點中時觸發(fā)。
DOMNodeRemoved事件,在節(jié)點從其父節(jié)點中被移除時觸發(fā)。
DOMNodeInsertedIntoDocument事件,在一個節(jié)點被直接插入文檔,或通過子樹間接插入文檔之后觸發(fā),在DOMNodeInserted之后觸發(fā)。
DOMNodeRemoveFromDocument事件,在一個節(jié)點被直接從文檔中移除,或通過子樹間接從文檔中移除之后觸發(fā),在DOMNodeRemoved之后觸發(fā)。
DOMAttrModified事件,在特性被修改之后觸發(fā)。
DOMCharacterDataModified事件,在文本節(jié)點的值發(fā)生變化時觸發(fā)。
在移除節(jié)點時,首先會觸發(fā)DOMNodeRemoved事件,事件目標(biāo)為被刪除的節(jié)點,而事件對象的relatedNode屬性中包含著目標(biāo)父節(jié)點的引用,與parentNode相同。被刪除的節(jié)點和其子節(jié)點上,會相繼觸發(fā)DOMRemovedFromDocument事件,這個事件不冒泡。緊隨其后的是DOMSubtreeModified事件,這個事件的事件目標(biāo)是被移除節(jié)點的父節(jié)點。
在插入節(jié)點時,首先會觸發(fā)DOMNodeInserted事件,事件目標(biāo)為被插入的節(jié)點,而事件對象的relatedNode屬性中包含著目標(biāo)父節(jié)點的引用,緊接著會觸發(fā)DOMNodeInsertedIntoDocument事件,事件目標(biāo)是被插入的節(jié)點,這個事件不冒泡。最后觸發(fā)DOMSubtreeModified事件,事件目標(biāo)是被插入節(jié)點的父節(jié)點。
13.4.7 HTML5事件
contextmenu事件,鼠標(biāo)事件的一種,單機鼠標(biāo)右鍵觸發(fā)事件,可以調(diào)出瀏覽器的上下文菜單,會冒泡,可以使用event.preventDefalut()取消默認(rèn)的瀏覽器上下文菜單,從而實現(xiàn)自定義菜單的功能。
beforeunload事件,這個事件會在瀏覽器卸載頁面之前觸發(fā),可以通過它來取消卸載并繼續(xù)使用原有頁面。通過設(shè)置event.returnValue的值(IE、Firefox)或?qū)⑿枰@示的信息作為返回值返回(Safari、Chrome)可以在頁面卸載前為用戶顯示對話框提示用戶是否確認(rèn)卸載。
DOMContentLoaded事件,在形成完整的DOM樹之后就會觸發(fā),不用等待圖片之類的外部資源下載。不支持本事件的瀏覽器,可以設(shè)置一個延遲為0的超時調(diào)用:
setTimeout(function () { },0);
超時調(diào)用為0的意思為:在當(dāng)前JavaScript處理完成后立即運行這個函數(shù)。必須將其作為頁面中的第一個超時調(diào)用。
readystatechange事件,提供與文檔或元素的加載狀態(tài)有關(guān)信息,支持本事件的對象(不是事件對象)都有一個readyState屬性,表示元素的加載狀態(tài),可能包含下列5個值:
1.uninitialized:對象存在但未初始化;
2.loading:對象正在加載數(shù)據(jù);
3.loaded:對象加載數(shù)據(jù)完成;
4.interactive:可以操作對象了,但還沒完全加載;
5.complete:對象加載完畢;
并非所有對象都有全部的5個階段,而且5個階段的順序不一定,檢測操作時,需要同時檢測interactive和complete兩個階段。檢測完成時需要同時檢測loaded和complete兩個階段。并在執(zhí)行一次操作后就將事件處理程序移除,以防多次執(zhí)行。
pageshow事件,當(dāng)頁面是通過前進(jìn)后退等按鈕從內(nèi)存中直接加載,會跳過load事件(初次打開頁面時已經(jīng)執(zhí)行過load事件),就會觸發(fā)pageshow事件,事件目標(biāo)是document,但事件處理程序必須添加到window對象,初次加載時,pageshow事件在load事件之后觸發(fā)。事件對象的persisted屬性保存著一個布爾值屬性,如果頁面被保存在了內(nèi)存當(dāng)中,則為true,否則為false。
pagehide事件,該事件會在瀏覽器卸載頁面時觸發(fā),并且在unload事件之前觸發(fā),事件目標(biāo)是document,但事件處理程序必須添加到window對象,事件對象也包含persisted屬性。
當(dāng)?shù)谝淮斡|發(fā)pageshow時(首次加載頁面),persisted屬性的值一定是false,而在第一次觸發(fā)pagehide事件時(首次卸載頁面),persisted的值會變成true。
hashchange事件,URL的參數(shù)列表(#號后面的所有字符串)發(fā)生變化時觸發(fā),必須把事件處理程序綁定在window對象上,事件對象包含oldURL和newURL兩個屬性,分別保存變化前后的完整URL。最好還是使用location對象來確定當(dāng)前的參數(shù)列表。
13.4.8 設(shè)備事件
orientationchange事件,在橫屏和豎屏之間切換會觸發(fā)此事件,window.orientation屬性保存狀態(tài)信息,0為豎屏正放,90為主屏按鈕在右橫放,-90為主屏按鈕在左橫放。
deviceorientation事件,設(shè)備方向發(fā)生改變時觸發(fā)此事件,觸發(fā)時,事件對象包含著每個軸相對于設(shè)備靜止?fàn)顟B(tài)下發(fā)生的變化信息。
alpha屬性,圍繞Z軸旋轉(zhuǎn)時,Y軸變化的度數(shù)差
beta屬性,圍繞X軸旋轉(zhuǎn)時,Z軸變化的度數(shù)差
gamma屬性,圍繞Y軸旋轉(zhuǎn)時,Z軸的變化度數(shù)差
devicemotion事件,設(shè)備發(fā)生移動的時候觸發(fā),事件對象包含下列屬性:
acceleration屬性,一個包含x,y,z屬性的對象,在不考慮重力的情況下在每個方向上的加速度。
accelerationIncludingGravity屬性,一個包含x,y,z屬性的對象,在考慮重力的情況下在每個方向上的加速度。
interval屬性,以毫秒表示的時間值。
rotationRate屬性,包含alpha,beta,gamma三個方向?qū)傩缘膶ο?/p>
13.4.9 觸摸與手勢事件
touchstart事件,手指觸摸屏幕時觸發(fā),即使已經(jīng)有一個手指放在屏幕上也會觸發(fā)。
touchmove事件,當(dāng)手指在屏幕上滑動時連續(xù)觸發(fā),事件發(fā)生時,調(diào)用preventDefault()可以阻止?jié)L動。
touchend事件,當(dāng)手指從屏幕上移開時觸發(fā)。
每個觸摸事件的事件對象都包含了鼠標(biāo)事件中的常見屬性,觸摸事件對象還包含下列3個用于跟蹤觸摸的屬性:
touches屬性,表示當(dāng)前跟蹤的觸摸操作的Touch對象的數(shù)組。
targetTouches屬性,特定于事件目標(biāo)的Touch對象的數(shù)組。
changeTouches屬性,表示自上次觸摸以來發(fā)生了什么改變的Touch對象的數(shù)組。
在touchend事件發(fā)生時,touches集合中就沒有任何Touch對象了,因為不存在活動的觸摸操作,此時必須轉(zhuǎn)而使用changeTouches集合。
觸摸屏幕上的元素時,事件觸發(fā)順序如下:touchstart,mouseover,mousemove,mousedown,mouseup,click,touchend。
手勢事件,當(dāng)兩個手指觸摸屏幕時就會產(chǎn)生手勢,觸發(fā)手勢事件。有3個手勢事件:
gesturestart事件,當(dāng)一個手指已經(jīng)按在屏幕上,另一個手指又觸摸屏幕時觸發(fā)。
gesturechange事件,當(dāng)觸摸屏幕的任何一個手指的位置發(fā)生變化時觸發(fā)。
gestureend事件,當(dāng)任何一個手指從屏幕上面移開時觸發(fā)。
只有當(dāng)兩只手指都觸摸到事件的接收容器上時才會觸發(fā)手勢事件,放上第一只手指時,會觸發(fā)touchstart事件,放上第二只手指時,會先觸發(fā)gesturestart事件,再觸發(fā)第二只手指的touchstart事件,有一個或兩個手指在屏幕上滑動時,將會觸發(fā)gesturechange事件,只要有一個手指移開,就會觸發(fā)gestureend事件,緊接著又會觸發(fā)該手指的touchend事件。
與觸摸事件一樣,手勢事件的事件對象也包含所有常見的屬性,還包含額外的兩個屬性rotation和scale屬性。
rotation屬性表示手指變化引起的旋轉(zhuǎn)角度,負(fù)值表示逆時針旋轉(zhuǎn),正值表示順時針旋轉(zhuǎn)(從0開始),scale屬性表示兩個手指間距離的變化情況,距離拉大增長,距離縮短減?。◤?開始)。