JavaScript
與 HTML
之間的交互是通過事件實現(xiàn)的寸宵。事件崖面,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間元咙。可以使用偵聽器(或處理程序)來預(yù)訂事件巫员,以便事件發(fā)生時執(zhí)行相應(yīng)的代碼庶香。這種在傳統(tǒng)軟件工程中被稱為觀察員模式的模型,支持頁面的行為(JavaScript
代碼)與頁面的外觀(HTML
和 CSS
代碼)之間的松散耦合简识。
事件流
事件流描述的是從頁面中接收事件的順序赶掖。但有意思的是, IE
和 Netscape
開發(fā)團(tuán)隊居然提出了差不多是完全相反的事件流的概念七扰。 IE
的事件流是事件冒泡流奢赂,而 Netscape Communicator
的事件流是事件捕獲流。
-
IE
的事件流叫做事件冒泡(event bubbling
)颈走,即事件開始時由最具體的元素(文檔中嵌套層次最深的那節(jié)點)接收膳灶,然后逐級向上傳播到較為不具體的節(jié)點(文檔)。
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
click
點擊的順序:div => body => html => document
- 事件捕獲的思想是不太具體的節(jié)點應(yīng)該更早接收到事件立由,而最具體的節(jié)點應(yīng)該最后接收到事件轧钓。事件捕獲的用意在于在事件到達(dá)預(yù)定目標(biāo)之前捕獲它富岳。
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
click
點擊的順序: document => html => body => div
DOM事件流
DOM2級事件
規(guī)定的事件流包括三個階段:事件捕獲階段苔货、處于目標(biāo)階段和事件冒泡階段太惠。首先發(fā)生的是事件捕獲侵歇,為截獲事件提供了機(jī)會。然后是實際的目標(biāo)接收到事件柑蛇。最后一個階段是冒泡階段苟耻,可以在這個階段對事件做出響應(yīng)吱瘩。
IE9
荷逞、Opera
媒咳、Firefox
、Chrome
和Safari
都支持DOM
事件流种远;IE8
及更早版本不支持DOM
事件流伟葫。
事件處理程序
事件就是用戶和瀏覽器自身執(zhí)行的某種動作,事件處理程序就是響應(yīng)某個事件的函數(shù)就叫做事件處理函數(shù)院促,事件處理程序以on
開頭。click
事件的事件處理程序就是 onclick
斧抱,load
事件的事件處理程序就是onload
常拓。
HTML 事件處理程序
<input type="button" value="Click Me" onclick="alert('Clicked')" />
當(dāng)單擊這個按鈕時,就會顯示一個警告框辉浦。這個操作是通過指定 onclick
特性并將一些 JavaScript
代碼作為它的值來定義的弄抬。
Dom0 及事件處理程序
通過 JavaScript
指定事件處理程序的傳統(tǒng)方式,就是將一個函數(shù)賦值給一個事件處理程序?qū)傩韵芙肌_@種為事件處理程序賦值的方法是在第四代Web
瀏覽器中出現(xiàn)的掂恕,而且至今仍然為所有現(xiàn)代瀏覽器所支持拖陆。原因一是簡單,二是具有跨瀏覽器的優(yōu)勢懊亡。
每個元素(包括 window
和 document
)都有自己的事件處理程序?qū)傩砸绬@些屬性通常全部小寫,例如onclick
店枣。將這種屬性的值設(shè)置為一個函數(shù)速警,就可以指定事件處理程序,如下所示:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};
使用
DOM0
級方法指定的事件處理程序被認(rèn)為是元素的方法鸯两。因此闷旧,這時候的事件處理程序是在元素的作用域中運行;換句話說钧唐,程序中的this
引用當(dāng)前元素忙灼。來看一個例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id); //"myBtn"
};
以這種方式添加的事件處理程序會在事件流的冒泡階段被處理钝侠。
也可以刪除通過
DOM0
級方法指定的事件處理程序该园,只要像下面這樣將事件處理程序?qū)傩缘闹翟O(shè)置為 null 即可:
btn.onclick = null; //刪除事件處理程序
將事件處理程序設(shè)置為null
之后,再單擊按鈕將不會有任何動作發(fā)生机错。
DOM2 級事件處理程序
DOM2 級事件
定義了兩個方法爬范,用于處理指定和刪除事件處理程序的操作: addEventListener()
和removeEventListener()
。所有 DOM
節(jié)點中都包含這兩個方法弱匪,并且它們都接受 3 個參數(shù):要處理的事件名青瀑、作為事件處理程序的函數(shù)和一個布爾值。
最后這個布爾值參數(shù)如果是 true
萧诫,表示在捕獲階段調(diào)用事件處理程序斥难;如果是false
,表示在冒泡階段調(diào)用事件處理程序帘饶。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
這里為按鈕添加了兩個事件處理程序哑诊。這兩個事件處理程序會按照添加它們的順序觸發(fā),因此首先會顯示元素的 ID
及刻,其次會顯示"Hello world!"
消息镀裤。
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
因為在 addEventListener()
和removeEventListener()
中使
用了相同的函數(shù)〗煞梗可以考慮將事件處理函數(shù)單獨抽取出來暑劝。
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//這里省略了其他代碼
btn.removeEventListener("click", handler, false); //有效!
IE9
颗搂、Firefox
担猛、Safari
、Chrome
和Opera
支持DOM2
級事件處理程序。
IE事件處理程序
IE
實現(xiàn)了與DOM
中類似的兩個方法: attachEvent()
和 detachEvent()
傅联。這兩個方法接受相同的兩個參數(shù):事件處理程序名稱與事件處理程序函數(shù)先改。
由于
IE8
及更早版本只支持事件冒泡,所以通過attachEvent()
添加的事件處理程序都會被添加到冒泡階段蒸走。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
注意仇奶,
attachEvent()
的第一個參數(shù)是onclick
,而非DOM
的addEventListener()
方法中的click
载碌。
在IE
中使用 attachEvent()
與使用DOM0
級方法的主要區(qū)別在于事件處理程序的作用域猜嘱。在使用 DOM0
級方法的情況下,事件處理程序會在其所屬元素的作用域內(nèi)運行嫁艇;
在使用
attachEvent()
方法的情況下朗伶,事件處理程序會在全局作用域中運行,因此this
等于window
步咪!
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert(this === window); //true
});
不過论皆,與
DOM
方法不同的是,這些事件處理程序不是以添加它們的順序執(zhí)行猾漫,而是以相反的順序被觸發(fā)点晴。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
btn.attachEvent("onclick", function(){
alert("Hello world!");
});
這里調(diào)用了兩次 attachEvent()
,為同一個按鈕添加了兩個不同的事件處理程序悯周。單擊這個例子中的按鈕粒督,首先看到的是"Hello world!"
,然后才是"Clicked"
禽翼。
跨瀏覽器的事件處理程序
// EventUtil 的用法如下所示屠橄。
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} 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;
}
}
};
var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//這里省略了其他代碼
EventUtil.removeHandler(btn, "click", handler);
這兩個方法首先都會檢測傳入的元素中是否存在DOM2
級方法。如果存在 DOM2
級方法闰挡,則使用該方法:傳入事件類型锐墙、事件處理程序函數(shù)和第三個參數(shù) false
(表示冒泡階段)。
如果存在的是IE
的方法长酗,則采取第二種方案溪北。注意,為了在 IE8
及更早版本中運行夺脾,此時的事件類型必須加上"on"
前綴之拨。
最后一種可能就是使用DOM0
級方法(在現(xiàn)代瀏覽器中,應(yīng)該不會執(zhí)行這里的代碼)咧叭。此時敦锌,我們使用的是方括號語法來將屬性名指定為事件處理程序,或者將屬性設(shè)置為 null佳簸。
事件對象
在觸發(fā) DOM
上的某個事件時,會產(chǎn)生一個事件對象event
,這個對象中包含著所有與事件有關(guān)的信息生均。包括導(dǎo)致事件的元素听想、事件的類型以及其他與特定事件相關(guān)的信息。例如马胧,鼠標(biāo)操作導(dǎo)致的事件對象中汉买,會包含鼠標(biāo)位置的信息,而鍵盤操作導(dǎo)致的事件對象中佩脊,會包含與按下的鍵有關(guān)的信息蛙粘。
DOM中的事件對象
兼容DOM
的瀏覽器會將一個 event
對象傳入到事件處理程序中。無論指定事件處理程序時使用什么方法(DOM0 級
或 DOM2 級
)威彰,都會傳入event
對象出牧。來看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);
屬性/方法 | 類 型 | 讀/寫 | 說 明 |
---|---|---|---|
bubbles |
Boolean |
只讀 | 表明事件是否冒泡 |
cancelable |
Boolean |
只讀 | 表明是否可以取消事件的默認(rèn)行為 |
currentTarget |
Element |
只讀 | 其事件處理程序當(dāng)前正在處理事件的那個元素 |
defaultPrevented |
Boolean |
只讀 | 為true 表 示 已 經(jīng) 調(diào) 用 了 preventDefault() (DOM3級 事件中新增) |
detail |
Integer |
只讀 | 與事件相關(guān)的細(xì)節(jié)信息 |
eventPhase |
Integer |
只讀 | 調(diào)用事件處理程序的階段: 1 表示捕獲階段歇盼, 2 表示“處于目標(biāo)”舔痕, 3 表示冒泡階段 |
preventDefault() |
Function |
只讀 | 取消事件的默認(rèn)行為 。 如 果 cancelable 是 true 豹缀,則可以使用這個方法 |
stopImmediatePropagation() |
Function |
只讀 | 取消事件的進(jìn)一步捕獲或冒泡伯复,同時阻止任何事件處理程序被調(diào)用(DOM3 級事件中新增) |
stopPropagation() |
Function |
只讀 | 取消事件的進(jìn)一步捕獲或冒泡。如果bubbles 為true 邢笙,則可以使用這個方法 |
target |
Element |
只讀 | 事件的目標(biāo) |
trusted |
Boolean |
只讀 | 為true 表示事件是瀏覽器生成的啸如。為false 表示事件是由開發(fā)人員通過 JavaScript 創(chuàng)建的(DOM3級 事件中新增) |
type |
String |
只讀 | 被觸發(fā)的事件的類型 |
view |
AbstractView |
只讀 | 與事件關(guān)聯(lián)的抽象視圖。等同于發(fā)生事件的 window 對象 |
在需要通過一個函數(shù)處理多個事件時氮惯,可以使用 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;
這個例子定義了一個名為 handler
的函數(shù),用于處理 3 種事件:click
筐骇、mouseover
和 mouseout
债鸡。
當(dāng)單擊按鈕時,會出現(xiàn)一個與前面例子中一樣的警告框铛纬。當(dāng)按鈕移動到按鈕上面時厌均,背景顏色應(yīng)該會變成紅色,而當(dāng)鼠標(biāo)移動出按鈕的范圍時告唆,背景顏色應(yīng)該會恢復(fù)為默認(rèn)值棺弊。這里通過檢測 event.type
屬性,讓函數(shù)能夠確定發(fā)生了什么事件擒悬,并執(zhí)行相應(yīng)的操作模她。
IE 中的事件對象
在使用 DOM0 級
方法添加事件處理程序時,event
對象作為 window
對象的一個屬性存在懂牧。來看下面的例子侈净。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
};
我們通過 window.event
取得了 event
對象尊勿,并檢測了被觸發(fā)事件的類型(IE
中的 type
屬性與DOM
中的 type
屬性是相同的)。
如果事件處理程序是使用
attachEvent()
添加的畜侦,那么就會有一個event
對象作為參數(shù)被傳入事件處理程序函數(shù)中元扔,如下所示。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
alert(event.type); //"click"
});
因為事件處理程序的作用域是根據(jù)指定它的方式來確定的旋膳,所以不能認(rèn)為 this
會始終等于事件目標(biāo)澎语。故而,最好還是使用 event.srcElement
比較保險验懊。例如:
屬性/方法 | 類 型 | 讀/寫 | 說 明 |
---|---|---|---|
cancelBubble |
Boolean |
讀/寫 | 默認(rèn)值為false 擅羞,但將其設(shè)置為true 就可以取消事件冒泡(與DOM 中的stopPropagation() 方法的作用相同) |
returnValue |
Boolean |
讀/寫 | 默認(rèn)值為true ,但將其設(shè)置為false 就可以取消事件的默認(rèn)行為(與DOM 中的preventDefault() 方法的作用相同) |
srcElement |
Element |
只讀 | 事件的目標(biāo)(與DOM 中的target 屬性相同) |
type |
String |
只讀 | 被觸發(fā)的事件的類型 |
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick", function(event){
alert(event.srcElement === this); //false
// this => window
});
returnValue
屬性相當(dāng)于DOM
中的preventDefault()
方法义图,它們的作用都是取消給定事件的默認(rèn)行為减俏。只要將returnValue
設(shè)置為false
,就可以阻止默認(rèn)行為歌溉。來看下面的例子垄懂。
var link = document.getElementById("myLink");
link.onclick = function(){
window.event.returnValue = false;
};
跨瀏覽器的事件對象
雖然 DOM
和 IE
中的event
對象不同,但基于它們之間的相似性依舊可以拿出跨瀏覽器的方案來痛垛。IE
中 event
對象的全部信息和方法 DOM
對象中都有草慧,只不過實現(xiàn)方式不一樣。不過匙头,這種對應(yīng)關(guān)系讓實現(xiàn)兩種事件模型之間的映射非常容易漫谷。可以對前面介紹的 EventUtil
對象加以增強(qiáng)蹂析,添加如下方法以求同存異舔示。
var EventUtil = {
addHandler: 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;
}
},
removeHandler: function(element, type, handler){
//省略的代碼
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
事件類型
UI事件
load
:當(dāng)頁面完全加載后在 window
上面觸發(fā),當(dāng)所有框架都加載完畢時在框架集上面觸發(fā)电抚,當(dāng)圖像加載完畢時在<img>
元素上面觸發(fā)惕稻,或者當(dāng)嵌入的內(nèi)容加載完畢時在<object>
元素上面觸發(fā)。
unload
:當(dāng)頁面完全卸載后在window
上面觸發(fā)蝙叛,當(dāng)所有框架都卸載后在框架集上面觸發(fā)俺祠,或者當(dāng)嵌入的內(nèi)容卸載完畢后在<object>
元素上面觸發(fā)。
abort
:在用戶停止下載過程時借帘,如果嵌入的內(nèi)容沒有加載完蜘渣,則在<object>
元素上面觸發(fā)。
error
:當(dāng)發(fā)生JavaScript
錯誤時在 window
上面觸發(fā)肺然,當(dāng)無法加載圖像時在<img>
元素上面觸發(fā)蔫缸,當(dāng)無法加載嵌入內(nèi)容時在<object>
元素上面觸發(fā),或者當(dāng)有一或多個框架無法加載時在框架集上面觸發(fā)际起。
select
:當(dāng)用戶選擇文本框(<input>
或<texterea>
)中的一或多個字符時觸發(fā)拾碌。
resize
:當(dāng)窗口或框架的大小變化時在 window
或框架上面觸發(fā)吐葱。
scroll
:當(dāng)用戶滾動帶滾動條的元素中的內(nèi)容時,在該元素上面觸發(fā)倦沧。 <body>
元素中包含所加載頁面的滾動條唇撬。
鼠標(biāo)與滾輪事件
click
:在用戶單擊主鼠標(biāo)按鈕(一般是左邊的按鈕)或者按下回車鍵時觸發(fā)。這一點對確保易訪問性很重要展融,意味著onclick
事件處理程序既可以通過鍵盤也可以通過鼠標(biāo)執(zhí)行。
dblclick
:在用戶雙擊主鼠標(biāo)按鈕(一般是左邊的按鈕)時觸發(fā)豫柬。從技術(shù)上說告希,這個事件并不是DOM2 級事件
規(guī)范中規(guī)定的,但鑒于它得到了廣泛支持烧给,所以 DOM3 級事件
將其納入了標(biāo)準(zhǔn)燕偶。
mousedown
:在用戶按下了任意鼠標(biāo)按鈕時觸發(fā)。不能通過鍵盤觸發(fā)這個事件础嫡。
mouseenter
:在鼠標(biāo)光標(biāo)從元素外部首次移動到元素范圍之內(nèi)時觸發(fā)指么。這個事件不冒泡,而且在光標(biāo)移動到后代元素上不會觸發(fā)榴鼎。 DOM2 級事件
并沒有定義這個事件伯诬,但 DOM3 級事件
將它納入了規(guī)范。 IE
巫财、 Firefox 9+
和Opera
支持這個事件盗似。
mouseleave
:在位于元素上方的鼠標(biāo)光標(biāo)移動到元素范圍之外時觸發(fā)。這個事件不冒泡平项,而且在光標(biāo)移動到后代元素上不會觸發(fā)赫舒。 DOM2 級事件
并沒有定義這個事件,但DOM3 級事件
將它納入了規(guī)范闽瓢。IE
接癌、 Firefox 9+
和 Opera
支持這個事件。
mousemove
:當(dāng)鼠標(biāo)指針在元素內(nèi)部移動時重復(fù)地觸發(fā)扣讼。不能通過鍵盤觸發(fā)這個事件缺猛。
mouseout
:在鼠標(biāo)指針位于一個元素上方,然后用戶將其移入另一個元素時觸發(fā)届谈。又移入的另一個元素可能位于前一個元素的外部枯夜,也可能是這個元素的子元素。不能通過鍵盤觸發(fā)這個事件艰山。
mouseover
:在鼠標(biāo)指針位于一個元素外部湖雹,然后用戶將其首次移入另一個元素邊界之內(nèi)時觸發(fā)。不能通過鍵盤觸發(fā)這個事件曙搬。
mouseup
:在用戶釋放鼠標(biāo)按鈕時觸發(fā)摔吏。不能通過鍵盤觸發(fā)這個事件鸽嫂。
頁面上的所有元素都支持鼠標(biāo)事件。除了
mouseenter
和mouseleave
征讲,所有鼠標(biāo)事件都會冒泡据某,也可以被取消,而取消鼠標(biāo)事件將會影響瀏覽器的默認(rèn)行為诗箍。取消鼠標(biāo)事件的默認(rèn)行為還會影響其他事件癣籽,因為鼠標(biāo)事件與其他事件是密不可分的關(guān)系。
只有在同一個元素上相繼觸發(fā) mousedown
和 mouseup
事件滤祖,才會觸發(fā) click 事件筷狼;如果 mousedown
或 mouseup
中的一個被取消,就不會觸發(fā)click
事件匠童。類似地埂材,只有觸發(fā)兩次 click
事件,才會觸發(fā)一次dblclick
事件汤求。如果有代碼阻止了連續(xù)兩次觸發(fā) click
事件(可能是直接取消click
事件俏险,也可能通過取消 mousedown
或 mouseup
間接實現(xiàn)),那么就不會觸發(fā) dblclick
事件了扬绪。
這 4 個事件觸發(fā)的順序始終如下:
(1)mousedown
(2) mouseup
(3)click
(4) mousedown
(5) mouseup
(6)click
(7) dblclick
客戶端的坐標(biāo)位置
鼠標(biāo)事件都是在瀏覽器視口中的特定位置上發(fā)生的竖独。這個位置信息保存在事件對象的 clientX
和clientY
屬性中。所有瀏覽器都支持這兩個屬性勒奇,它們的值表示事件發(fā)生時鼠標(biāo)指針在視口中的水平
和垂直坐標(biāo)预鬓。
頁面的坐標(biāo)位置
通過客戶區(qū)坐標(biāo)能夠知道鼠標(biāo)是在視口中什么位置發(fā)生的,而頁面坐標(biāo)通過事件對象的pageX
和 pageY
屬性赊颠,能告訴你事件是在頁面中的什么位置發(fā)生的格二。換句話說,這兩個屬性表示鼠標(biāo)光標(biāo)在頁面中的位置竣蹦,因此坐標(biāo)是從頁面本身而非視口的左邊和頂邊計算的顶猜。
屏幕坐標(biāo)位置
鼠標(biāo)事件發(fā)生時,不僅會有相對于瀏覽器窗口的位置痘括,還有一個相對于整個電腦屏幕的位置长窄。而通過 screenX
和 screenY
屬性就可以確定鼠標(biāo)事件發(fā)生時鼠標(biāo)指針相對于整個屏幕的坐標(biāo)信息。
HTML5 事件
很多瀏覽器出于不同的目的——滿足用戶需求或解決特殊問題纲菌,還實現(xiàn)了一些自定義的事件挠日。HTML5
詳盡列出了瀏覽器應(yīng)該支持的所有事件。
contextmenu 事件
為了實現(xiàn)上下文菜單翰舌,開發(fā)人員面臨的主要問題是如何確定應(yīng)該顯示上下文菜單(在Windows
中嚣潜,是右鍵單擊;在 Mac
中椅贱,是 Ctrl+
單擊)懂算,以及如何屏蔽與該操作關(guān)聯(lián)的默認(rèn)上下文菜單只冻。為解決這個問題,就出現(xiàn)了contextmenu
這個事件计技,用以表示何時應(yīng)該顯示上下文菜單喜德,以便開發(fā)人員取消默認(rèn)的上下文菜單而提供自定義的菜單。
由于contextmenu
事件是冒泡的垮媒,因此可以為document
指定一個事件處理程序舍悯,用以處理頁面中發(fā)生的所有此類事件。這個事件的目標(biāo)是發(fā)生用戶操作的元素睡雇。在所有瀏覽器中都可以取消這個事件:在兼容DOM
的瀏覽器中贱呐,使用 event.preventDefalut()
;在IE
中入桂,將 event.returnValue
的值設(shè)置為false
。
EventUtil.addHandler(window, "load", function(event){
var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "contextmenu", function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
var menu = document.getElementById("myMenu");
menu.style.left = event.clientX + "px";
menu.style.top = event.clientY + "px";
menu.style.visibility = "visible";
});
EventUtil.addHandler(document, "click", function(event){
document.getElementById("myMenu").style.visibility = "hidden";
});
});
DOMContentLoaded 事件
如前所述驳阎, window
的load
事件會在頁面中的一切都加載完畢時觸發(fā)抗愁,但這個過程可能會因為要加載的外部資源過多而頗費周折。而DOMContentLoaded
事件則在形成完整的DOM
樹之后就會觸發(fā)呵晚,不理會圖像蜘腌、 JavaScript
文件、CSS
文件或其他資源是否已經(jīng)下載完畢饵隙。
要處理 DOMContentLoaded
事件撮珠,可以為 document
或 window
添加相應(yīng)的事件處理程序(盡管這個事件會冒泡到 window
,但它的目標(biāo)實際上是document
)金矛。來看下面的例子芯急。
EventUtil.addHandler(document, "DOMContentLoaded", function(event){
alert("Content loaded");
});
readystatechange 事件
IE
為 DOM 文檔中的某些部分提供了 readystatechange 事件拓哟。這個事件的目的是提供與文檔或元素的加載狀態(tài)有關(guān)的信息圆雁,但這個事件的行為有時候也很難預(yù)料。支持 readystatechange 事件的每個對象都有一個 readyState 屬性擦耀,可能包含下列 5 個值中的一個饼酿。
uninitialized(未初始化):對象存在但尚未初始化榕酒。
loading(正在加載):對象正在加載數(shù)據(jù)。
loaded(加載完畢):對象加載數(shù)據(jù)完成故俐。
interactive(交互):可以操作對象了想鹰,但還沒有完全加載。
complete(完成):對象已經(jīng)加載完畢药版。
對于 document
而言辑舷,值為"interactive"
的readyState
會在與 DOMContentLoaded
大致相同的時刻觸發(fā)readystatechange
事件。此時刚陡,DOM
樹已經(jīng)加載完畢惩妇,可以安全地操作它了株汉,因此就會進(jìn)入交互(interactive
)階段。但與此同時歌殃,圖像及其他外部文件不一定可用乔妈。
ventUtil.addHandler(document, "readystatechange", function(event){
if (document.readyState == "interactive"){
alert("Content loaded");
}
});
交互階段可能會早于也可能會晚于完成階段出現(xiàn),無法確保順序氓皱。在包含較多外部資源的頁面中路召,交互階段更有可能早于完成階段出現(xiàn);而在頁面中包含較少外部資源的情況下波材,完成階段先于交互階段出現(xiàn)的可能性更大股淡。因此,為了盡可能搶到先機(jī)廷区,有必要同時檢測交互和完成階段唯灵,如下面的例子所示。
EventUtil.addHandler(document, "readystatechange", function(event){
if (document.readyState == "interactive" || document.readyState == "complete"){
EventUtil.removeHandler(document, "readystatechange", arguments.callee);
alert("Content loaded");
}
});
支持
readystatechange
事件的瀏覽器有IE
隙轻、Firfox 4+
和Opera
埠帕。
雖然使用
readystatechange
可以十分近似地模擬DOMContentLoaded
事件,但它們本質(zhì)上還是不同的玖绿。在不同頁面中敛瓷,load
事件與readystatechange
事件并不能保證以相同的順序觸發(fā)。
內(nèi)存和性能
由于事件處理程序可以為現(xiàn)代 Web
應(yīng)用程序提供交互能力斑匪,因此許多開發(fā)人員會不分青紅皂白地向頁面中添加大量的處理程序呐籽。在創(chuàng)建GUI
的語言(如C#
)中,為GUI
中的每個按鈕添加一個 onclick
事件處理程序是司空見慣的事蚀瘸,而且這樣做也不會導(dǎo)致什么問題狡蝶。可是在 JavaScript
中苍姜,添加到頁面上的事件處理程序數(shù)量將直接關(guān)系到頁面的整體運行性能牢酵。導(dǎo)致這一問題的原因是多方面的。
首先衙猪,每個函數(shù)都是對象馍乙,都會占用內(nèi)存;內(nèi)存中的對象越多垫释,性能就越差丝格。其次,必須事先指定所有事件處理程序而導(dǎo)致的 DOM
訪問次數(shù)棵譬,會延遲整個頁面的交互就緒時間显蝌。事實上,從如何利用好事件處理程序的
角度出發(fā),還是有一些方法能夠提升性能的曼尊。
事件委托
如果在一個復(fù)雜的Web
應(yīng)用程序中酬诀,對所有可單擊的元素都采用這種方式,那么結(jié)果就會有數(shù)不清的代碼用于添加事件處理程序骆撇。此時瞒御,可以利用事件委托技術(shù)解決這個問題。使用事件委托神郊,只需在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 "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.;
break;
case "sayHi":
alert("hi");
break;
}
});
移除事件處理程序
每當(dāng)將事件處理程序指定給元素時涌乳,運行中的瀏覽器代碼與支持頁面交互的JavaScript
代碼之間就會建立一個連接蜻懦。這種連接越多,頁面執(zhí)行起來就越慢夕晓。如前所述宛乃,可以采用事件委托技術(shù),限制建立的連接數(shù)量蒸辆。另外烤惊,在不需要的時候移除事件處理程序,也是解決這個問題的一種方案吁朦。內(nèi)存中留有那些過時不用的“空事件處理程序”(dangling event handler
),也是造成Web
應(yīng)用程序內(nèi)存與性能問題的主要原因渡贾。
如果你知道某個元素即將被移除逗宜,那么最好手工移除事件處理程序,如下面的例子所示空骚。
btn.onclick = function(){
//先執(zhí)行某些操作
btn.onclick = null; //移除事件處理程序
document.getElementById("myDiv").innerHTML = "Processing...";
}
在此纺讲,我們在設(shè)置<div>
的innerHTML
屬性之前,先移除了按鈕的事件處理程序囤屹。這樣就確保了內(nèi)存可以被再次利用熬甚,而從 DOM
中移除按鈕也做到了干凈利索。注意肋坚,在事件處理程序中刪除按鈕也能阻止事件冒泡乡括。目標(biāo)元素在文檔中是事件冒泡的前提。
參考文獻(xiàn)
《javascript 高級程序設(shè)計3》