http://developer.apple.com.cn/internet/webcontent/eventmodels.html
事件使得客戶(hù)端的 JavaScript 有機(jī)會(huì)被激活,并得以運(yùn)行。在一個(gè) Web 頁(yè)面裝載之后,運(yùn)行腳本的唯一方式,就是響應(yīng)系統(tǒng)或者用戶(hù)的動(dòng)作。雖然從第一個(gè)支持腳本編程的瀏覽器面世以來(lái),簡(jiǎn)單的事件被實(shí)現(xiàn)為 JavaScript 的一部分;但是大多數(shù)最近出現(xiàn)的瀏覽器都實(shí)現(xiàn)了強(qiáng)壯的事件模型,使腳本可以更加智能地處理事件。現(xiàn)在的問(wèn)題在于:為了支持各種瀏覽器,您必須和多個(gè)先進(jìn)的事件模型做斗爭(zhēng),準(zhǔn)確地說(shuō),是三個(gè)。
這三個(gè)事件模型分別和下面的文檔對(duì)象模型(Document Object Model,即 DOM)三巨頭結(jié)盟:Netscape Navigator 4 (NN4),Macintosh 和 Windows 系統(tǒng)的 Internet Explorer 4 及其更新版本(IE4+),以及在 Safari 中得到實(shí)現(xiàn)的 W3C DOM。盡管這些模型之間有些地方存在一些本質(zhì)的差別,但是在一些簡(jiǎn)易的 JavaScript 的幫助下,它們都可以同時(shí)適用于同一個(gè)文檔。本文主要著眼于相互沖突的事件模型中的兩個(gè)關(guān)鍵方面:
- 把一個(gè)事件和 HTML 元素綁定起來(lái)的方法。
- 在事件被觸發(fā)后如何對(duì)之進(jìn)行處理。
事件綁定的方法
事件綁定是指構(gòu)造一個(gè)響應(yīng)系統(tǒng)或者用戶(hù)動(dòng)作的 HTML 元素的過(guò)程。在不同的瀏覽器版本中,有不少于五種事件綁定技術(shù)。下面我們快速地介紹一下這些技術(shù)。
事件綁定方法I:綁定元素屬性
最簡(jiǎn)單和向后兼容性最好的事件綁定方法是把事件綁定到元素標(biāo)識(shí)的屬性。事件屬性名稱(chēng)由事件類(lèi)型外加一個(gè)“on”前綴構(gòu)成。盡管HTML屬性并不是大小寫(xiě)敏感的,人們還是定義了一個(gè)規(guī)則,規(guī)定事件類(lèi)型的每一個(gè)“詞”的首字母大寫(xiě),比如 onClick
和 onMouseOver
。這些屬性也被稱(chēng)為事件處理器,因?yàn)樗鼈冎甘玖嗽厝绾巍疤幚怼碧囟ǖ氖录?lèi)型。
正確的事件處理器屬性的值在形式上是被引號(hào)包含的 JavaScript 語(yǔ)句。最常見(jiàn)的值是一條調(diào)用某個(gè)腳本函數(shù)的語(yǔ)句,而被調(diào)用的函數(shù)在位于文檔前部的 <SCRIPT> 標(biāo)識(shí)中定義--該標(biāo)識(shí)通常位于 <HEAD> 部分。舉例來(lái)說(shuō),下面的函數(shù):
function myFunc() {
// script statements here
}
可以被定義為一個(gè)按鍵控件的事件處理器,按鍵的定義如下:
<INPUT TYPE="button" NAME="myButton" VALUE="Click Here"
onClick="myFunc()">
把事件綁定到元素屬性上有一個(gè)優(yōu)點(diǎn),即可以支持開(kāi)發(fā)者把參數(shù)傳遞給事件處理器函數(shù)。接收事件的元素的引用則由一個(gè)特殊的參數(shù)值--this
關(guān)鍵字來(lái)傳遞。下面的代碼演示一個(gè)函數(shù)如何借助傳入?yún)?shù),把任意數(shù)目的文本框的內(nèi)容轉(zhuǎn)化為大寫(xiě):
<SCRIPT LANGUAGE="JavaScript">
function convertToUpper(textbox) {
textbox.value = textbox.value.toUpperCase();
}
</SCRIPT>
...
<FORM ....>
<INPUT TYPE="text" NAME="first_name" onChange="convertToUpper(this)">
<INPUT TYPE="text" NAME="last_name" onChange="convertToUpper(this)">
...
</FORM>
事件綁定方法II:綁定對(duì)象屬性
對(duì)于 NN3+ 和 IE4+ 這兩類(lèi)瀏覽器,腳本編程人員可以以腳本語(yǔ)句的方式把事件綁定到對(duì)象上,而不是綁定到元素標(biāo)識(shí)的屬性上。每一個(gè)負(fù)責(zé)事件響應(yīng)的元素對(duì)象都為自己能夠識(shí)別的事件設(shè)置了相應(yīng)的屬性。對(duì)象屬性名稱(chēng)是元素標(biāo)識(shí)屬性的小寫(xiě)形式,比如 onmouseover
。NN4 還接受 interCap(即首字小寫(xiě),之后的每一個(gè)詞的首字大寫(xiě))版本的屬性名,但是考慮到跨瀏覽器的兼容性,所有字母都是小寫(xiě)的名稱(chēng)會(huì)更安全一些。
當(dāng)您把一個(gè)函數(shù)的引用賦值給一個(gè)事件屬性的時(shí)候,就發(fā)生了綁定。函數(shù)的引用是指函數(shù)的名稱(chēng),但是不帶函數(shù)定義中的括號(hào)。因此,如果要為一個(gè)名為 myButton
的按鍵的點(diǎn)擊事件(click)進(jìn)行綁定,使之激活一個(gè)定義為 myFunc()
的函數(shù),則其賦值語(yǔ)句如下所示:
document.forms[0].myButton.onclick = myFunc;
您應(yīng)該注意一點(diǎn):在事件觸發(fā)的時(shí)候,沒(méi)有辦法向事件函數(shù)傳遞參數(shù)。本文在稍候?qū)κ录幚磉^(guò)程的討論中還會(huì)回顧這個(gè)問(wèn)題。
事件綁定方法III: 綁定 IE4+<SCRIPT FOR> 標(biāo)識(shí)
在 IE4+ 中,Microsoft 對(duì) <SCRIPT> 標(biāo)識(shí)實(shí)現(xiàn)了自己的擴(kuò)展,可以將它包含的腳本語(yǔ)句和某個(gè)元素的一個(gè)事件類(lèi)型進(jìn)行綁定。支持這個(gè)綁定的標(biāo)識(shí)屬性(還沒(méi)有被 W3C 批準(zhǔn)為 HTML 的一部分)是 FOR
和 EVENT
。
FOR
屬性的值必須是您為元素的 ID 屬性分配的唯一標(biāo)識(shí)符。然后,您必須把事件的名稱(chēng)(onmouseover,onclick,等等)分配給 EVENT
屬性。在上面的按鍵實(shí)例的基礎(chǔ)上,我們必須對(duì)按鍵標(biāo)識(shí)進(jìn)行修改,使之包含一個(gè) ID
屬性:
<INPUT TYPE="button" NAME="myButton" ID="button1" VALUE="Click Here">
腳本語(yǔ)句并不在函數(shù)中,而是在 <SCRIPT> 標(biāo)識(shí)中,如下所示:
<SCRIPT FOR="button1" EVENT="onclick">
// script statements here
</SCRIPT>
當(dāng)然,標(biāo)識(shí)中的語(yǔ)句可以調(diào)用頁(yè)面上其它地方定義的任何函數(shù)(或者從.js文件中導(dǎo)入的函數(shù))。然而,這種綁定方式意味著您必須為每一個(gè)元素和每一個(gè)事件創(chuàng)建一個(gè) <SCRIPT FOR> 標(biāo)識(shí)。
您還必須小心,只能把這種綁定方法部署在僅供 IE4+ 瀏覽器瀏覽的頁(yè)面。其它任何支持腳本編程而又沒(méi)有實(shí)現(xiàn)這個(gè)特殊的 <SCRIPT> 標(biāo)識(shí)的瀏覽器(包括 IE3),都將把它作為常規(guī)的 <SCRIPT> 標(biāo)識(shí)來(lái)處理,并試圖在頁(yè)面裝載的時(shí)候執(zhí)行這些腳本語(yǔ)句--這不可避免地引起腳本錯(cuò)誤。
事件綁定方法IV:使用 IE5/Windows 的 attachEvent() 方法
早在 W3C DOM 工作組磨礪出標(biāo)準(zhǔn)的事件模型之前,attachEvent()
方法已經(jīng)被實(shí)現(xiàn)了,并且可被用于 Windows 版的 IE5 或更新版本的瀏覽器上的每一個(gè) HTML 元素。
attachEvent()
方法的用法如下所示:
elemObject.attachEvent("eventName", functionReference);
eventName 參數(shù)的值是表示事件名稱(chēng)的字符串,比如 onmousedown
。functionReference 參數(shù)是一個(gè)不帶括號(hào)的函數(shù)引用,和早些時(shí)候描述的事件屬性方法中一樣。因此對(duì)于上面例子的按鍵對(duì)象,可以通過(guò)如下的腳本語(yǔ)句把函數(shù)綁定到按鍵的 click 事件:
document.getElementById("button1").attachEvent("onclick", myFunc);
由于 attachEvent()
方法必須嚴(yán)格工作在 IE5+/Windows 的環(huán)境中,所以您既可以使用 W3C DOM 的元素引用方式(如上文所示),也可以使用 IE4+ 的引用方式:
document.all.button1.attachEvent("onclick", myFunc);
這個(gè)方法有一個(gè)值得注意的地方:您不能在元素被載入瀏覽器之前執(zhí)行這個(gè)語(yǔ)句。該對(duì)象的引用在相應(yīng)的 HTML 按鍵元素被瀏覽器創(chuàng)建之前,都是無(wú)效的。因此,要讓這樣的綁定語(yǔ)句或者在頁(yè)面的底部運(yùn)行,或者在 BODY 元素的 onLoad
事件處理器調(diào)用的函數(shù)中運(yùn)行。
事件綁定方法V:使用 W3C DOM 的 addEventListener() 方法
Safari 使用的是 W3C DOM 級(jí)別2定義的事件綁定機(jī)制,這個(gè)機(jī)制和 IE5/Windows 的 attachEvent()
方法很類(lèi)似,但是有自己的語(yǔ)法。W3C DOM 規(guī)范為 DOM 層次中的每一個(gè)結(jié)點(diǎn)都定義了一個(gè) addEventListener()
方法。HTML 元素是 DOM 結(jié)點(diǎn)中的一類(lèi),在一對(duì)元素標(biāo)識(shí)內(nèi)部的文本結(jié)點(diǎn)也是一個(gè)結(jié)點(diǎn),也能夠接收事件。這一點(diǎn)在 NN6 事件處理過(guò)程中經(jīng)常得到體現(xiàn),在本文的后面部分您將會(huì)看到。
addEventListener()
方法的語(yǔ)法如下所示:
nodeReference.addEventListener("eventType", listenerReference, captureFlag);
用 W3C DOM 規(guī)范中的行話(huà)來(lái)說(shuō),addEventListener()
方法為指定的結(jié)點(diǎn)注冊(cè)了一個(gè)事件,表示該結(jié)點(diǎn)希望處理相應(yīng)的事件。這個(gè)方法的第一個(gè)參數(shù)是一個(gè)聲明事件類(lèi)型的字符串(不帶"on"前綴),比如 click
,mousedown
,和 keypress
。addEventListener()
方法的第二個(gè)參數(shù)可以和早些時(shí)候描述過(guò)的函數(shù)引用同樣對(duì)待。第三個(gè)參數(shù)則是一個(gè) Boolean 值,指明該結(jié)點(diǎn)是否以DOM中所謂的捕捉模式來(lái)偵聽(tīng)事件。事件的捕捉和派發(fā)---綜合起來(lái)稱(chēng)為事件的傳播--最后由另一篇文章來(lái)描述。對(duì)于一個(gè)典型的事件偵聽(tīng)器來(lái)說(shuō),第三個(gè)參數(shù)應(yīng)該為 false(假)
。
那種綁定方法最好?
如果您足夠幸運(yùn),只需要為某一個(gè)操作系統(tǒng)上特定版本的瀏覽器創(chuàng)建應(yīng)用程序,則可以為選定的瀏覽器選擇最現(xiàn)代的綁定方式。但是對(duì)于跨瀏覽器的網(wǎng)站作者來(lái)說(shuō),選擇綁定方法則需要面對(duì)實(shí)質(zhì)性的挑戰(zhàn)。
如果您只計(jì)劃支持 IE5/Mac,則可以不考慮 attachEvent()
和 addEventListener()
方法,因?yàn)?IE5/Mac 對(duì)這兩種方法都不支持。這種情況下,比較實(shí)際的選擇有兩種,要么綁定標(biāo)識(shí)屬性,要么綁定對(duì)象屬性。這時(shí)就需要費(fèi)心思了。
一方面,W3C DOM Level 2 承認(rèn)基于標(biāo)識(shí)屬性的方法,并將它推薦為 addEventListener()
方法的可接受代替方法。為了和數(shù)以百萬(wàn)計(jì)的腳本相兼容,所有支持腳本編程的瀏覽器都支持基于標(biāo)識(shí)屬性的事件綁定方法。一些自動(dòng)化的頁(yè)面制作工具,比如 DreamWeaver,也把事件處理器的屬性嵌入到 HTML 標(biāo)識(shí)中。
但是另一方面,在元素標(biāo)識(shí)文件中嵌入面向腳本的信息,又不能將內(nèi)容從風(fēng)格及行為中分離開(kāi)來(lái),這和當(dāng)前的流行趨勢(shì)相違背。把事件綁定到對(duì)象屬性上的方法聽(tīng)起來(lái)方向是對(duì)的,但是在 W3C 關(guān)于 HTML,XHTML,或者 DOM 的標(biāo)準(zhǔn)中,并沒(méi)有對(duì)事件屬性提供“官方”的支持。盡管如此,在實(shí)際生活中,除了第一代支持腳本編程的瀏覽器之外,其它瀏覽器都支持這種方法。
一個(gè)純標(biāo)準(zhǔn)論者會(huì)認(rèn)為上述的兩種方法都有缺點(diǎn),但是對(duì)于講究實(shí)際的開(kāi)發(fā)者來(lái)說(shuō),即使考慮到未來(lái)主流瀏覽器的兼容性,這兩種方法都是“安全”的。
事件的信息礦:事件對(duì)象
所有這三種事件模型的核心都是一個(gè)事件對(duì)象--它是一個(gè)抽象的實(shí)體,其屬性中包含很多對(duì)事件處理函數(shù)具有潛在價(jià)值的信息。從本文早些時(shí)候?qū)κ录壎夹g(shù)的討論中,您可能可以推斷出事件對(duì)象對(duì)腳本之所以至關(guān)重要,原因之一是除了基于標(biāo)識(shí)屬性的綁定方法以外,其它綁定方法都不支持將參數(shù)傳遞到事件處理函數(shù)中。
事件對(duì)象通過(guò)提供足夠的“掛鉤”,使事件處理函數(shù)可以讀取事件的特征,從而填補(bǔ)了這個(gè)縫隙。因此,事件處理函數(shù)可以得到接收事件的元素的引用,以及其它一些有用的信息,比如鼠標(biāo)動(dòng)作的坐標(biāo),鼠標(biāo)使用的按鍵,鍵盤(pán)上被按壓的鍵,以及在事件發(fā)生的過(guò)程中是否有修飾鍵被按下(比如檢測(cè) Shift-click 事件)。
訪(fǎng)問(wèn)事件對(duì)象
雖然事件對(duì)象的精確構(gòu)成因?yàn)楸疚挠懻摰娜N DOM(NN4,IE4+,以及 W3C/Safari)的不同而有所變化,但是,一個(gè)事件處理函數(shù)只能通過(guò)以下兩種方式之一來(lái)訪(fǎng)問(wèn)事件對(duì)象:NN 方式和 IE 方式。W3C/Safari DOM 事件對(duì)象公布給腳本的接口方式和 NN4 的事件對(duì)象一樣;而 IE4+ 則有自己的方法。
IE4+ 的事件對(duì)象更加易于描述,因此我們首先對(duì)它進(jìn)行討論。簡(jiǎn)單地說(shuō),事件對(duì)象是 window
對(duì)象的一個(gè)屬性。這意味著在所有的實(shí)例中只有一個(gè)事件對(duì)象。舉例來(lái)說(shuō),在鍵盤(pán)上簡(jiǎn)單地按壓和松開(kāi)一個(gè)按鍵,會(huì)產(chǎn)生三個(gè)事件:onKeyDown
,onKeyPress
,和 onKeyUp
(事件的發(fā)生順序和這里的列舉順序相同)。如果 onKeyDown
事 件激活的函數(shù)花費(fèi)很長(zhǎng)的時(shí)間進(jìn)行處理,則瀏覽器就會(huì)把其它兩個(gè)事件保持在隊(duì)列中,直到 onMouseDown
事件處理完成為止。
而對(duì)于 NN4 和 W3C DOM 來(lái)說(shuō),事件對(duì)象看起來(lái)就更加抽象一些。除了基于標(biāo)識(shí)屬性風(fēng)格的綁定方法之外,其它綁定方法都是把事件對(duì)象自動(dòng)傳遞給與事件相綁定的函數(shù)。傳遞給函數(shù)的是一個(gè)單一的參數(shù)。開(kāi)發(fā)者需要在函數(shù)中定義一個(gè)參數(shù)變量,來(lái)“接收”該參數(shù)的值。為了避免和IE中的 window.event
對(duì)象互相沖突,請(qǐng)不要把參數(shù)命名為 event。舉例來(lái)說(shuō),把它命名為 evt
就相當(dāng)好,相應(yīng)的事件函數(shù)的定義大致如下:
function myFunc(evt) {
// script statements here
}
然而,如果您使用的是基于標(biāo)識(shí)屬性的事件綁定技術(shù),就必須顯式地把事件作為一個(gè)參數(shù)傳遞到您調(diào)用的函數(shù)。為了完成事件的傳遞,需要把 event
這個(gè)關(guān)鍵字作為參數(shù)進(jìn)行傳遞:
onClick = "myFunc(event)"
外部傳入的參數(shù)是您的事件處理函數(shù)和 NN 的事件對(duì)象之間的唯一聯(lián)系紐帶。如果在主事件處理函數(shù)內(nèi)部調(diào)用的其它函數(shù)需要該對(duì)象或者該對(duì)象的屬性值,則您可以把該對(duì)象或其屬性值作為參數(shù)中繼給這些函數(shù)。
如果您想知道 IE 是否把事件的引用保存在 window.event
屬性中,那答案是“是”。使用這個(gè)語(yǔ)法交集是相當(dāng)安全的,因?yàn)樵?NN 和 IE 這兩個(gè)瀏覽器,被傳遞到事件處理函數(shù)的事件對(duì)象都有您所期望的當(dāng)前事件的屬性值。
兼容兩種事件對(duì)象引用
設(shè)想在處理事件時(shí),我們需要在一個(gè)事件函數(shù)中考察一個(gè)或者多個(gè)事件屬性。這是一個(gè)簡(jiǎn)單的技術(shù),可以使事件處理函數(shù)和作為參數(shù)傳入的事件對(duì)象協(xié)同工作,或者從 window.event
屬性中讀取信息。而且,這個(gè)技術(shù)不必處理不同的瀏覽器版本之間的細(xì)微差別。
在開(kāi)始的時(shí)候,需要在您的事件處理函數(shù)中定義一個(gè)參數(shù)變量,準(zhǔn)備接收可能傳入的事件對(duì)象。然后,通過(guò)簡(jiǎn)單的條件表達(dá)式把瀏覽器的事件對(duì)象賦值給上述的參數(shù)變量:
function myFunc(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
// process event here
}
如果事件對(duì)象真的以參數(shù)的形式傳進(jìn)來(lái)了,則在函數(shù)內(nèi)部,事件對(duì)象就被保留在 evt
這個(gè)局部變量中。如果這個(gè)參數(shù)是 null
,而且瀏覽器的 window
對(duì)象包含有一個(gè) event
屬性,則 window.event
對(duì)象就會(huì)把自己賦值給 evt
變量。
然而,為了完成這個(gè)工作,還應(yīng)該再包含一層或者多層條件控制,以便優(yōu)雅地適應(yīng)那些在事件模型中沒(méi)有定義事件對(duì)象的的早期瀏覽器:
function myFunc(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
// process event here
}
}
為了把同樣的方式應(yīng)用到所有事件處理函數(shù)的構(gòu)建中,您可以定義一個(gè)函數(shù)來(lái)兼容兩種事件,即由綁定的標(biāo)識(shí)屬性顯式傳入的事件對(duì)象,以及由綁定的事件屬性隱式傳入的事件對(duì)象。這樣即使您在開(kāi)發(fā)過(guò)程中改變了事件綁定的風(fēng)格,這個(gè)函數(shù)也不必改變。
瑞典自助餐式地選擇事件對(duì)象
然而,建立一個(gè)指向事件對(duì)象的引用只是戰(zhàn)斗的一部分。來(lái)自不同事件模型的每一個(gè)事件對(duì)象都擁有自己的一套屬性,以容納事件的細(xì)節(jié)。下面的表格列出了最常用的屬性,以及這些屬性在上述三種事件對(duì)象類(lèi)型中的名稱(chēng)。
表格 1. 流行的事件對(duì)象屬性
描述 |
NN4 |
IE4+ |
W3C/Safari |
Event target |
target
|
srcElement
|
target
|
Event type |
type
|
type
|
type
|
X coordinate on page |
pageX
|
* |
pageX
|
Y coordinate on page |
pageY
|
* |
pageY
|
Mouse button |
which
|
button
|
button
|
Keyboard key |
which
|
keyCode
|
keyCode
|
標(biāo)注*的屬性值可以通過(guò)對(duì) event.clientX + document.body.scrollTop
或者 event.clientY + document.body.scrollTop
進(jìn)行求值來(lái)得到。
Macintosh 版本的IE5在通常情況下都遵循 IE4+ 的事件對(duì)象模型,但是有一個(gè)例外,即 IE5/Mac 的事件對(duì)象既定義了 srcElement
屬性,也定義了 target
屬性,這兩個(gè)屬性都指向接收事件的元素。
需要抽象的最重要的事件對(duì)象屬性可能得算指向接收事件的 HTML 元素的引用。NN4 和 W3C 的事件對(duì)象采用相同的屬性名(target
),而 IE4+ 的事件對(duì)象則使用 srcElement
屬性。這時(shí)候,對(duì)象檢測(cè)技術(shù)(而不是費(fèi)力勞神而又具有危險(xiǎn)傾向的瀏覽器版本識(shí)別方法)再次拯救了我們。對(duì)于那些非文本容器的元素,一個(gè)簡(jiǎn)單的條件表達(dá)式就可以輕松處理腳本語(yǔ)法上的差別:
var elem = (evt.target) ? evt.target : evt.srcElement
從現(xiàn)在開(kāi)始,您的腳本就可以讀寫(xiě)任何瀏覽器對(duì)象模型公布出來(lái)的元素對(duì)象屬性了。
W3C DOM結(jié)點(diǎn)的事件目標(biāo)
W3C DOM 的結(jié)點(diǎn)架構(gòu)使得文檔中的每一個(gè)結(jié)點(diǎn)都可以接收事件。在支持這一架構(gòu)的瀏覽器中,發(fā)生在嵌套文本頂上的事件并不調(diào)用分配給文本容器的事件處理器,相應(yīng)的文本結(jié)點(diǎn)才是該事件的目標(biāo)結(jié)點(diǎn)。考慮如下場(chǎng)景:
在事件實(shí)例,當(dāng)鼠標(biāo)的指針在一個(gè) SPAN 元素包含的文本頂上滾動(dòng)時(shí),該文本就會(huì)被高亮顯示。 事件綁定的過(guò)程通過(guò)對(duì)象屬性在 init()
函數(shù)中進(jìn)行。從表面上看,當(dāng)用戶(hù)在 SPAN 元素頂上滾動(dòng)鼠標(biāo)時(shí),onMouseOver
事件動(dòng)作函數(shù)就為該元素指派一個(gè)與風(fēng)格表單規(guī)則相關(guān)聯(lián)的類(lèi)名(highlight
),該風(fēng)格規(guī)則把文本的顯示風(fēng)格定義為粗體,黃色背景;而在 onMouseOut
函數(shù)中,則把風(fēng)格恢復(fù)為原始的版本(類(lèi) normal
)。請(qǐng)注意一個(gè) toggleHighlight()
函數(shù)是如何在事件對(duì)象的 type
屬性的幫助下,執(zhí)行兩個(gè)動(dòng)作的(該屬性在所有事件模型對(duì)象中的名稱(chēng)是相同的)。請(qǐng)試一下這個(gè)事件實(shí)例。
但是如果您把例子裝載到 NN6,則鼠標(biāo)事件的真正目標(biāo)就是 SPAN 元素中的文本結(jié)點(diǎn)了。本文并不討論事件的傳播機(jī)制,但是請(qǐng)相信,W3C DOM 事件模型的缺省行為會(huì)使事件沿著結(jié)點(diǎn)的包含層次向上傳播(和 IE4+ 中事件通過(guò)元素容器向上傳播的機(jī)制很類(lèi)似)。因此,在這個(gè)事件實(shí)例中。鼠標(biāo)事件會(huì)從其真正的目標(biāo)向上傳遞到文本結(jié)點(diǎn)的容器(也就是 SPAN 元素)。這些事件觸發(fā)了 SPAN 元素中相應(yīng)的事件處理器。
雖然事件處理器屬于 SPAN 元素,事件對(duì)象還是保留文本對(duì)象的引用,并將它作為事件的原始目標(biāo)。然而,只有對(duì)文本結(jié)點(diǎn)的容器進(jìn)行動(dòng)作,才能修改它的風(fēng)格。為了實(shí)現(xiàn) toggleHighlight()
函數(shù)的等價(jià)操作,使之可以修改SPAN容器的 className
屬性,該函數(shù)需要派生出一個(gè)指向文本結(jié)點(diǎn)容器的引用。
一個(gè)策略是使用 W3C DOM 事件對(duì)象的 currentTarget
屬性,該屬性返回一個(gè)處理事件的結(jié)點(diǎn)的引用。腳本中的決策樹(shù)需要考慮這個(gè)屬性,增加代碼之后的 toggleHighlight()
函數(shù)如下所示:
function toggleHighlight(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
var elem
if (evt.target) {
if (evt.currentTarget && (evt.currentTarget != evt.target)) {
elem = evt.currentTarget
} else {
elem = evt.target
}
} else {
elem = evt.srcElement
}
elem.className = (evt.type == "mouseover") ? "highlight" : "normal"
}
}
另一個(gè)可選的方法是考察由 target
屬性返回的對(duì)象的 ronodeType
屬性。一個(gè)能夠把事件定向給文本結(jié)點(diǎn)的瀏覽器,也可以把一個(gè)文本結(jié)點(diǎn)的 nodeType
屬性值報(bào)告為3,而不是報(bào)告為元素結(jié)點(diǎn)的類(lèi)型(其值為1)。如果事件的目標(biāo)是一個(gè)文本結(jié)點(diǎn),則腳本程序就可以通過(guò)該文本結(jié)點(diǎn)的 parentNode
屬性來(lái)得到其上級(jí)元素結(jié)點(diǎn)的引用。這種方法的決策樹(shù)在某種程度上得到更多的改進(jìn):
function toggleHighlight(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
var elem
if (evt.target) {
elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target
} else {
elem = evt.srcElement
}
elem.className = (evt.type == "mouseover") ? "highlight" : "normal"
}
}
如果您正在用遵循 W3 的瀏覽器閱讀本文,則請(qǐng)嘗試這個(gè)修改過(guò)的版本,看看鼠標(biāo)滾動(dòng)時(shí)的風(fēng)格變化。
這個(gè)頁(yè)面使用了嵌入到事件實(shí)例中的最新版本的 toggleHighlight()
函數(shù),展示了如何使用 JavaScript 為那些能夠顯示期望效果的瀏覽器增加額外的價(jià)值,同時(shí)也可以那些基本的內(nèi)容提供給仍然使用著較老版本或者不支持腳本編程的瀏覽器的用戶(hù),只不過(guò)在模式上不那么動(dòng)人和便于交互。
一個(gè)事件處理函數(shù)的模板
并不是每個(gè)事件處理函數(shù)都處理頁(yè)面元素對(duì)象中同樣的屬性或者行為,但是,從上文的討論可以派生出來(lái)的一個(gè)模板,您可以在這個(gè)模板的幫助下開(kāi)始編碼。模板如下:
function functionName(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
var elem
if (evt.target) {
elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target
} else {
elem = evt.srcElement
}
if (elem) {
// process event here
}
}
}
請(qǐng)把第一行的函數(shù)名替換為您希望的函數(shù)名,并在注視指示的地方開(kāi)始書(shū)寫(xiě)具體事件的代碼。這個(gè)格式應(yīng)該可以為您提供一個(gè)起點(diǎn),適合于您采用的任何跨瀏覽器的事件綁定風(fēng)格。如果您需要在一個(gè)頁(yè)面中多次使用這個(gè)格式,則可以進(jìn)一步精簡(jiǎn)代碼,即把讀取目標(biāo)的代碼抽象成一個(gè)可重用的工具函數(shù),然后在每一個(gè)事件處理函數(shù)中進(jìn)行調(diào)用:
// shared function
function getTargetElement(evt) {
var elem
if (evt.target) {
elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target
} else {
elem = evt.srcElement
}
return elem
}
function functionName(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
var elem = getTargetElement(evt)
if (elem) {
// process event here
}
}
}
有了這類(lèi)框架,您現(xiàn)在應(yīng)該可以把更多的注意力集中在各個(gè)事件處理函數(shù)要求的具體動(dòng)作中了。
查看實(shí)例:
下載腳本