DOM 級(jí)別
此文中涉及到DOM0和DOM2級(jí)事件,故先行解釋
文檔對(duì)象模型(
DOM)是一個(gè)平臺(tái),一個(gè)中立于語言的應(yīng)用程序編程接口(API),允許程序訪問并更改文檔的內(nèi)容、結(jié)構(gòu)和樣式。
DOM被分為不同的部分(核心、XML及HTML)和級(jí)別(DOM Level 1/2/3)
DOM分類
-
DOM核心:針對(duì)任何結(jié)構(gòu)化文檔的標(biāo)準(zhǔn)模型 -
DOM XML:只針對(duì)XML文檔的標(biāo)準(zhǔn)模型 -
DOM HTML:只針對(duì)HTML文檔的標(biāo)準(zhǔn)模型
DOM級(jí)別是描述DOM對(duì)象,方法和行為的規(guī)范集合?;谙惹凹?jí)別構(gòu)建的DOM規(guī)范的更高級(jí)別。變化發(fā)生在兩個(gè)方面:
- 增加一個(gè)全新的規(guī)格類別(例如,3級(jí)添加了“驗(yàn)證”和“加載和保存”規(guī)范,這在2級(jí)中不存在)
- 修改現(xiàn)有規(guī)范類別(例如更新“核心”規(guī)范)
DOM級(jí)別
-
DOM0級(jí)別:不是W3C規(guī)范。而僅僅是對(duì)在Netscape Navigator 3.0和Microsoft Internet Explorer 3.0中的等價(jià)功能性的一種定義。DOM1級(jí)別建立于此功能性之上 -
DOM1級(jí)別:專注于HTML和XML文檔模型。它含有文檔導(dǎo)航和處理功能。于1998年10月1日稱為W3C推薦標(biāo)準(zhǔn) -
DOM2級(jí)別:對(duì)DOM1級(jí)別添加了樣式表對(duì)象模型,并定義了操作附于文檔至上的樣式信息的功能性。同時(shí)還定義了一個(gè)事件模型,并提供了對(duì)XML命名空間的支持。作為一項(xiàng)W3C推薦標(biāo)準(zhǔn),于2000年11月13日發(fā)布 -
DOM3級(jí)別:規(guī)定了內(nèi)容模型(DTD和Schemas)和文檔驗(yàn)證。同時(shí)規(guī)定了文檔加載和保存、文檔查看、文檔格式化和關(guān)鍵事件
注:DOM1級(jí)標(biāo)準(zhǔn)中未定義事件相關(guān)的內(nèi)容,故沒有所謂的DOM1級(jí)別事件模型
事件
JavaScript和HTML的交互是通過事件實(shí)現(xiàn)的。事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互瞬間??梢允褂脗陕犉鳎ɑ蛱幚沓绦颍﹣眍A(yù)定事件,以便事件發(fā)生時(shí)執(zhí)行相應(yīng)的代碼。
網(wǎng)頁中的每個(gè)元素都可以產(chǎn)生某些可以觸發(fā)JavaScript函數(shù)的事件。比如說,我們可以在用戶點(diǎn)擊某按鈕時(shí)產(chǎn)生一個(gè)事件來觸發(fā)某個(gè)函數(shù)。事件在HTML中定義
事件舉例:
- 鼠標(biāo)點(diǎn)擊
- 頁面或圖像載入
- 鼠標(biāo)懸浮于頁面的某個(gè)熱點(diǎn)至上
- 在表單中選取輸入框
- 確認(rèn)表單
- 鍵盤按鍵
事件流
事件流描述的是從頁面中接受事件的順序
如圖2.1,目標(biāo)節(jié)點(diǎn)是<td>,當(dāng)點(diǎn)擊<td>事件的傳播如下所示:
[圖片上傳失敗...(image-5bef-1534760955972)]
事件冒泡
事件開始時(shí)由最具體的元素(文檔中嵌套層次最深的那個(gè)節(jié)點(diǎn))接收,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)(文檔)。
所由的現(xiàn)在瀏覽器都支持事件冒泡,但在具體是線上還是有一些差別。IE5.5及更在版本中的事件冒泡會(huì)跳過<html>元素(從<body>直接跳到document)。IE9、Firefox、Chrome和Safari則將事件一直冒泡到window對(duì)象。
即上圖中的3階段 ,事件傳播順序:<td> - <tr> - <tbody> - <table> - <body> - <html> - <document> - <window>
事件捕獲
不太具體的節(jié)點(diǎn)更早接收事件,而最具體的元素最后接收事件,與事件冒泡相反
即上圖中的1階段 ,事件傳播順序:<window> - <document> - <html> - <body> - <table> - <tbody> - <tr> - <td>
DOM 事件流
“DOM2級(jí)事件”規(guī)定的事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段和事件冒泡階段。首先發(fā)生的事件捕獲,為截獲事件提供了機(jī)會(huì)。然后是實(shí)際的目標(biāo)接收到事件。最后一個(gè)階段是冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。
IE9、Opera、Firefox、Chrome和Safari都支持DOM事件流;IE8及更早版本不支持DOM事件流。
即上圖中的1,2,3個(gè)階段
事件處理程序
事件就是用戶或?yàn)g覽器自身執(zhí)行的某種動(dòng)作。諸如
click、load和mouseover,都是事件的名字。而響應(yīng)某個(gè)事件的函數(shù)就叫做事件處理程序(或事件偵聽器)。事件處理程序的名字以on開頭,因此click事件的事件處理程序就是onclick,load事件的事件處理程序就是onload。為事件指定處理程序的方式有好幾種。
HTML事件處理程序
元素支持的每個(gè)元素都可以使用一個(gè)相應(yīng)事件處理程序同名的HTML屬性指定。這個(gè)屬性的值應(yīng)該是可以執(zhí)行的JavaScript代碼,我們可以為一個(gè)button添加click事件處理程序
<input type="button" value="Click Here" onclick="alert('Clicked');">
在HTML事件處理程序中可以包含要執(zhí)行的具體動(dòng)作,也可以調(diào)用在頁面其他地方定義的腳本,剛才的例子可以寫成如下形式:
<input type="button" value="Click Here" onclick="showMessage();">
在HTML中指定事件處理程序書寫很方便,但有兩個(gè)缺點(diǎn):
- 存在時(shí)差問題。因?yàn)橛脩艨赡軙?huì)在
HTML元素一出現(xiàn)在頁面上就觸發(fā)相應(yīng)的事件,但當(dāng)時(shí)的事件處理程序有可能尚不具備執(zhí)行條件。
以上面的例子說明,假設(shè)showMessage()函數(shù)是在按鈕下方、頁面的最底部定義的。如果用戶在頁面解析showMessage()函數(shù)之前就單擊了按鈕,就會(huì)引發(fā)錯(cuò)誤。為此,很多HTML事件處理程序都會(huì)被封裝在一個(gè)try-catch塊中,以便錯(cuò)誤不會(huì)浮出水面,如下面的例子所示:
<input type="button" value="Click Here" onclick="try{showMessage();}catch(ex){}">
這樣,如果在showMessage()函數(shù)有定義之前單機(jī)了按鈕,用戶將不會(huì)看到JavaScript錯(cuò)誤,因?yàn)樵跒g覽器有機(jī)會(huì)處理錯(cuò)誤之前,錯(cuò)誤就被捕獲了。
-
HTML代碼和JavaScript代碼緊密耦合。如果要更換事件處理程序,就要改動(dòng)這兩個(gè)地方:HTML代碼和JavaScript代碼。
DOM0 級(jí)事件處理程序
每個(gè)元素(包括window和document)都有自己的事件處理程序?qū)傩?,這些屬性全部小寫,例如onclick。將這種屬性的值設(shè)置為一個(gè)函數(shù),就可以指定事件處理程序。
使用DOM0 級(jí)方法指定的事件處理程序被認(rèn)為是元素的方法。因此,這時(shí)候的事件處理程序是在元素的作用域中進(jìn)行;換句話說,程序中的this引用當(dāng)前元素。例如:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id); // "myBtn"
}
也可以刪除通過DOM0 級(jí)方法指定的事件處理程序,只要像下面這樣將事件處理程序?qū)傩缘闹翟O(shè)為null即可:
btn.onclick = null; // 刪除事件處理程序
將事件處理程序設(shè)置為null后,再單擊按鈕將不會(huì)有任何動(dòng)作發(fā)生。
這種做法的缺點(diǎn):一次只能定義一個(gè)事件處理程序,不能同時(shí)定義多個(gè)事件處理程序,后定義的事件處理程序會(huì)覆蓋前面定義的事件處理程序。
DOM2 級(jí)事件處理程序
DOM2 級(jí)事件定義了兩個(gè)方法,用于處理指定和刪除事件處理程序的操作:addEventListener()和removeEventListener()。所有DOM節(jié)點(diǎn)中都包含這兩個(gè)方法,并且它們都接受3個(gè)參數(shù):要處理的事件名、作為事件處理程序的函數(shù)和一個(gè)布爾值。最后這個(gè)布爾值參數(shù)如果是true,表示再捕獲階段調(diào)用事件處理程序;如果是false,表示在冒泡階段調(diào)用事件處理程序;不寫的情況下默認(rèn)為false。
在按鈕上為click事件添加事件處理程序,如下:
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
與DOM0 級(jí)方法一樣,這里添加的事件處理程序也是在其依附的元素的作用域中運(yùn)行。使用DOM2 級(jí)方法添加事件處理程序的主要好處是可以添加多個(gè)事件處理程序。如下所示:
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello World");
}, false);
這里為按鈕添加了兩個(gè)事件處理程序。這兩個(gè)事件處理程序會(huì)按照添加它們的順序觸發(fā),因此首先會(huì)顯示元素的ID,其次會(huì)顯示“Hello World”消息。
通過addEventListener()添加的事件處理程序只能使用removeEventListener()來移除;移除時(shí)傳入的參數(shù)與添加處理程序時(shí)使用的參數(shù)相同。這也意味著通過addEventListener()添加的匿名函數(shù)將無法移除。因?yàn)槟涿瘮?shù)體雖然方法體一樣,但是句柄卻不同是,是完全不同的函數(shù),所以應(yīng)寫成:
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
btn.addEventListener("click", handler, false);
IE 事件處理程序
IE 實(shí)現(xiàn)了與DOM中類似的兩個(gè)方法:attachEvent()和detachEvent()。這兩個(gè)方法接受相同的兩個(gè)參數(shù):事件處理程序名稱與事件處理程序函數(shù)。由于IE8及更早版本只支持事件冒泡,所以通過attachEvent()添加的事件處理程序都會(huì)被添加到冒泡階段。
使用attachEvent()添加的事件可以通過detachEvent()來移除,條件是必須提供相同的參數(shù)。與DOM方法一樣,這也意味著添加的匿名將不能被移除。不過,只要能夠?qū)⑾嗤瘮?shù)的引用傳給detachEvent(),就可以移除相應(yīng)的事件處理程序。
使用attachEvent()為按鈕添加一個(gè)事件處理程序,如下所示:
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
attachEvent()與addEventListener()的區(qū)別:
參數(shù)個(gè)數(shù)不同。
attachEvent()的有兩個(gè)參數(shù),添加的事件只能發(fā)生在冒泡階段;而addEventListener()有三個(gè)參數(shù),都最后一個(gè)參數(shù)決定添加的事件處理程序是在冒泡階段還是捕獲階段(一般為了兼容瀏覽器都設(shè)置為冒泡階段)第一個(gè)參數(shù)意義不同。
attachEvent()的第一個(gè)參數(shù)是事件處理程序名稱,例如onclick;而addEventListener()方法中第一個(gè)參數(shù)是事件處理類型,例如click
3.事件處理程序的作用域不同。在使用DOM2級(jí)方法的情況下,事件處理程序會(huì)在其所屬元素的作用域內(nèi)運(yùn)行;在使用attachEvent()方法的情況下,事件處理程序會(huì)在全局作用域中運(yùn)行,因此this等于window。如下:
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert(this === window); // true
});
- 為一個(gè)事件添加多個(gè)事件處理程序時(shí),執(zhí)行順序不同。與
addEventListener()類似,attachEvent()也可以為一個(gè)元素添加多個(gè)事件處理程序,不過,這些事件處理程序不是以添加它們的順序執(zhí)行的,而是以相反的順序被觸發(fā)。當(dāng)添加的事件處理程序較多時(shí)則順序無規(guī)律。
跨瀏覽器的事件處理程序
就不同瀏覽器的差異,對(duì)事件處理程序封裝,此方法接收三個(gè)參數(shù):要操作的元素、事件名稱、事件處理程序函數(shù)。jQuery創(chuàng)始人John Resig做法如下:
function addEvent(node, type, handler){
if(!node){
return false;
}
if(node.addEventListener){
node.addEventListener(type, handler, false);
return true;
}else if(node.attachEvent){
node['e' + type + handler] = handler;
node[type + handler] = function(){
node['e' + type + handler](window.event);
};
node.attachEvent('on' + type, node[type + handler]);
return true;
}
return false;
}
在取消事件處理程序的時(shí)候,如下:
function removeEvent(node, type, handler){
if(!node){
return false;
}
if(node.removeEventListener){
node.removeEventListener(type, handler, false);
return true;
}else if(node.detachEvent){
node.detachEvent('on' + type, node[type + handler]);
node[type + handler] = null;
return true;
}
return false;
}
事件對(duì)象
在觸發(fā)
DOM上的某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象中包含著所有與事件有關(guān)的信息。包括導(dǎo)致事件的元素、事件的類型以及其他與特定事件相關(guān)的信息。例如,鼠標(biāo)操作導(dǎo)致的事件對(duì)象中,會(huì)包含鼠標(biāo)位置的信息,而鍵盤操作導(dǎo)致的事件對(duì)象中,會(huì)包含與按下的鍵有關(guān)的信息。所有的瀏覽器都支持event對(duì)象,但支持方式不同。
DOM中的事件對(duì)象
兼容DOM的瀏覽器會(huì)將一個(gè)event對(duì)象傳入到事件處理程序中。
event對(duì)象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法。觸發(fā)的事件類型不一樣,可用的屬性和方法也不一樣。不過所有事件都會(huì)有下表列出的成員。

在事件處理程序內(nèi)部,對(duì)象
this始終等于currentTarget的值,而target則只包含事件的實(shí)際目標(biāo)。如果直接將使事件處理程序指定給了目標(biāo)元素,則this、currentTarget和target包含相同的值。
阻止事件的默認(rèn)行為
要阻止特定事件的默認(rèn)行為,可用使用preventDefault()方法。例如,鏈接的默認(rèn)行為就是在被單擊時(shí)會(huì)導(dǎo)航到其href特性指定的URL,若想阻止鏈接導(dǎo)航這一默認(rèn)行為,那么提供鏈接的onclick事件處理程序可以取消它,如下:
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
}
只有cancelable屬性設(shè)置為true的事件,才可以使用preventDefault()取消其默認(rèn)行為
阻止事件傳播
stopPropagation()方法用于立即停止事件在DOM層次中的傳播,即取消進(jìn)一步的事件捕獲或冒泡。例如,直接添加到一個(gè)按鈕的事件處理程序可以調(diào)用stopPropagation(),從而避免觸發(fā)注冊(cè)在document.body上面的事件處理程序,如下所示:
var btn = document.getElementById("myBtn");
btn.onclick = function(e){
alert('Clicked');
e.stopPropagation()
};
document.body.onclick = function(e){
alert('Body clicked');
};
調(diào)用stopPropagation()后事件不會(huì)傳到document.body,因此不會(huì)觸發(fā)注冊(cè)在document.body上的onclick事件處理程序。
只有在事件處理程序執(zhí)行期間,
event對(duì)象才會(huì)存在;一旦事件處理程序執(zhí)行完成,event對(duì)象就會(huì)被銷毀。
IE 中的事件對(duì)象
IE中的event對(duì)象有幾種不同的方式,取決于指定事件處理程序的方法。直接為DOM元素添加事件處理程序時(shí),event對(duì)象作為window對(duì)象的一個(gè)屬性存在,如下所示:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var e = window.event;
alert(e.type); // "click"
};
可是,如果事件處理程序是使用attachEvent()添加的,那么就會(huì)有一個(gè)event對(duì)象作為參數(shù)被傳入事件處理程序函數(shù)中,如下所示:
var btn = document.getElementById("myBtn");
btn.attachEvent('onclick', function(event){
alert(event.type); // "click"
});
像這樣使用attachEvent()的情況下,也可以提供window對(duì)象來訪問event對(duì)象,就像使用DOM0級(jí)方法一樣。不過為方便起見,同一個(gè)對(duì)象也會(huì)作為參數(shù)傳遞。
如果是通過HTML特性指定的事件處理程序,那么還可以通過一個(gè)名叫event的變量來訪問event對(duì)象(與DOM中的 事件模型相同),如下所示:
<input type="button" value="Click Me" onclick="alert(event.type)">
IE的event對(duì)象同樣也包含與創(chuàng)建它的事件相關(guān)的屬性和方法。其中很多屬性和方法都有對(duì)應(yīng)的或者相關(guān)的DOM屬性和方法。與DOM的event對(duì)象一樣,這些屬性和方法也會(huì)因?yàn)槭录愋偷牟煌煌?,但所有事件?duì)象都會(huì)包含下表所列的屬性和方法。

注:使用returnValue達(dá)到了阻止默認(rèn)行為的目的,cancelBubble設(shè)為true可阻止事件冒泡。
跨瀏覽器的事件對(duì)象
雖然DOM和IE的event對(duì)象不同,但基于它們的相似性,我們還是可以寫出跨瀏覽器的事件對(duì)象方案
function getEvent(e){
return e || window.event;
}
function getTarget(e){
return e.target || e.srcElement;
}
function preventDefault(e){
if(e.preventDefault){
e.preventDefault();
}else {
e.returnValue = false;
}
}
function stopPropagation(e){
if(e.stopPropagation){
e.stopPropagation();
}else {
e.cancelBubble = true;
}
}
事件冒泡的應(yīng)用
事件代理,又稱為事件委托。
例如:要求當(dāng)點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容。不考慮兼容,如下所示
<ul class="ct">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ct = document.querySelector('.ct');
ct.addEventListener('click', function(e){
var target = e.target;
if(target.tagName.toLowerCase() === 'li'){
console.log(target.innerText);
}
})
</script>
事件類型
Web瀏覽器中可能發(fā)生的事件有很多類型,DOM3級(jí)事件規(guī)定了以下幾類事件:
-
UI(User Interface,用戶界面)事件,當(dāng)用戶與頁面上的元素交互時(shí)觸發(fā) - 焦點(diǎn)事件,當(dāng)元素獲得或失去焦點(diǎn)時(shí)觸發(fā)
- 鼠標(biāo)事件,當(dāng)用戶通過鼠標(biāo)在頁面上執(zhí)行操作時(shí)觸發(fā)
- 滾輪事件,當(dāng)使用鼠標(biāo)滾輪(或類似設(shè)備)時(shí)觸發(fā)
- 文本事件,當(dāng)在文檔中輸入文本時(shí)觸發(fā)
- 鍵盤事件,當(dāng)用戶通過鍵盤在頁面上執(zhí)行操作時(shí)觸發(fā)
- 合成事件,當(dāng)為
IME(Input Method Editor,輸入法編輯器)輸入字符時(shí)觸發(fā) - 變動(dòng)(
mutation)事件,當(dāng)?shù)讓?code>DOM結(jié)構(gòu)發(fā)生變化時(shí)觸發(fā)
此處不想述,具體的可以查看JavaScript程序設(shè)計(jì)
參考:
- W3C DOM
- DOM 分類
- DOM 規(guī)范
- JavaScript 高級(jí)程序設(shè)計(jì)
- JavaScript 事件流
- DOM 事件結(jié)構(gòu)