文檔對象模型是一種與編程語言及平臺無關的API(Application programming Interface),藉助於它,程序可以動態地訪問和修改文檔內容、結構或顯示樣式。javascript
W3C協會早在1988年就開始了DOM標準的制定,W3C DOM標準能夠分爲DOM1,DOM2,DOM3三個版本。html
DOM1級主要定義的是HTML和XML文檔的底層結構。java
DOM2和DOM3級別則在這個結構的基礎上引入了更多的交互能力,也支持了更高級的XML特性。爲此DOM2和DOM3級分爲許多模塊(模塊之間具備某種關聯),分別描述了DOM的某個很是具體的子集。這些模塊以下:編程
DOM2級和3級的目的在於擴展DOM API,以知足操做XML的全部需求,同時提供更好的錯誤處理及特性檢測能力。 DOM0就是直接經過 onclick寫在html裏面的事件;設計模式
DOM2是經過addEventListener綁定的事件, 還有IE下的DOM2事件經過attachEvent綁定; DOM3是一些新的事件。瀏覽器
一開始瀏覽器處理事件的時候只有原始事件模型,事件處理程序被設置爲js代碼串做爲html的性質值,例如:bash
<input id="myButton" type="button" value="Press Me" onclick="alert('thanks');">複製代碼
在js中html元素都有一個對應的對象,這個對象的屬性對應那個html元素的性質,因此能夠用js代碼添加事件監聽函數
服務器
document.getElementById("myButton").onclick = function () {
alert('thanks');
}複製代碼
一般狀況下事件監聽函數若是返回一個值而且是false,則會阻止瀏覽器執行默認的動做。閉包
不管用html仍是js,都是把一個函數賦值給文檔元素,在事件監聽函數被調用時候它是做爲產生事件的元素的放法調用的,因此this引用的是那個目標元素(例子中的Input對象)。
從技術上來講,W3C的DOM標準並不支持上述最原始的添加事件監聽函數的方式,這些都是在DOM標準造成前的事件模型。儘管沒有正式的W3C標準,但這種事件模型仍然獲得普遍應用,這就是咱們一般所說的0級DOM。框架
1級DOM標準中並無定義事件相關的內容,因此沒有所謂的1級DOM事件模型。
在2級DOM中除了定義了一些DOM相關的操做以外還定義了一個事件模型 ,這個標準下的事件模型就是咱們所說的2級DOM事件模型
2級DOM的事件傳播
在2級DOM中,當事件發生在節點時,目標元素的事件處理函數就被觸發,並且目標的每一個祖先節點也有機會處理那個事件。由於2級DOM的事件傳播分三個階段進行。
在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象中包含着全部與事件有關的信息。
包括事件的元素、事件的類型,以及其餘與特定事件相關的信息。
例如,鼠標操做事件,包含鼠標的位置信息,鍵盤操做事件包含按下的鍵的信息。全部瀏覽器都支持event對象,但支持方式不一樣。
IE的事件流是冒泡, 從裏面往上面冒, netscape是從外部元素往內部元素捕獲; 而DOM2級的事件規定了事件流包含三個階段包括: 1:事件捕獲, 2:處於目標階段, 3:事件冒泡階段(IE8以及更早版本不支持DOM事件流);
不管在DOM0仍是DOM2仍是DOM3中都會在事件函數中傳入事件對象;
<script type="text/javascript">
var p = document.getElementById('p');
p.addEventListener("click",function(){
console.log(arguments[0]);
})
</script>複製代碼
點擊a後capturing階段事件傳播會從document-> span->a,而後發生在a,最後bubbling階段事件傳播會從a->span->document 。
2級事件模型中,能夠調用對象的addEventListener()方法爲元素設置事件監聽函數,也就是說經過2級DOM的這個API註冊的函數纔有可能在上述事件傳播三個階段中任意一個階段捕捉到事件的發生(若是用0級DOM的2個方法賦值的事件監聽函數不能在capturing階段捕捉到事件)。
2級DOM中監聽函數中的this
經過addEventListener添加的函數中的this,標準中並無規定this必須指向目標元素, 儘管大多數瀏覽器都是這麼實現的,但最終仍是取決於瀏覽器的實現,咱們須要用到目標元素的時候請調用:event.currentTarget.
用addEventListener添加的事件監聽函數,在被調用的時候js會傳給他一個Event對象,下面就是這個Event對象的經常使用屬性
(1)type:
發生的事件的類型,例如"click", "mouseover"
(2)target:
發生事件的節點,可能與currentTarget不一樣
(3)currentTarget:
正在處理事件的節點,若是在capturing階段和冒泡階段處理事件,這個屬性就與target屬性不一樣。在事件監聽函數中應該用這個屬性而不是this
(4)stopPropagation():
能夠阻止事件從當前正在處理他的節點傳播
(5)preventDefault():
阻止瀏覽器執行與事件相關的默認動做,與0級DOM中返回false同樣
(6)clientX, clientY:
鼠標相對於瀏覽器的x座標y座標
(7)screenX, screenY:
鼠標相對於顯示器左上角的x座標y座標
3. 事件監聽函數註冊
沒有addEventListener,只有attachEvent。2個參數,同addEventListener前兩個,只是事件名帶前綴on。
IE事件模型沒有capturing階段因此調用attachEvent至關於調用addEvetnListener且第三個參數爲false:
document.getElementById("myTest").attachEvent(" function(){alert(1)});
至關於
document.getElementById("myTest").addEventListener("click", function(){alert(1)}, false);
4. 用attachEvent註冊的函數將被做爲全局函數調用,而不是做爲發生事件的文檔元素的方法,
也就是說this引用的是window對象,而不是事件的目標元素。
由於各個瀏覽器的事件對象不同, 把主要的事件對象的屬性和方法列出來;
(1) bubble : 代表事件是否冒泡
(2) cancelable : 代表是否能夠取消冒泡
(3) currentTarget : 當前事件程序正在處理的元素, 和this同樣的;
(4) defaultPrevented: false ,若是調用了preventDefualt這個就爲真了;
(5) detail: 與事件有關的信息(滾動事件等等)
(6) eventPhase:
(7) type : 事件的類型
(8) view : 與元素關聯的window, 咱們可能跨iframe;
(9) preventDefault() 取消默認事件;
(10) stopPropagation() 取消冒泡或者捕獲;
(11) stopImmediatePropagation() (DOM3)阻止任何事件的運行;
//stopImmediatePropagation阻止 綁定在事件觸發元素的 其餘同類事件的callback的運行 IE下的事件對象是在window下的,而標準應該做爲一個參數, 傳爲函數第一個參數;
IE的事件對象定義的屬性跟標準的不一樣,如:
舊版本的 IE(IE 9 以前) 在執行 Javascript 時與幾乎全部其它瀏覽器不一樣,在 IE 9 以前的版本中,你須要使用 attachEvent 模塊,就像這樣:
element.attachEvent(' function() { /* do stuff here*/ });複製代碼
在大部分其它瀏覽器(包括 IE 9 以及更新的版本)中,你可使用 addEventListener,就像這樣:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
內聯事件 (Inline Events, 即 HTML 中的onclick=""屬性和element.onclick)
在全部支持 Javascript 的瀏覽器中,你能夠將一個事件監聽內聯,也就是像下面的 HTML 代碼那樣:
內聯事件示例:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>複製代碼
雖然它的確是能夠完成任務的,並且簡單直接,但絕大部分有經驗的開發者都會盡可能避開使用這樣的方法。
同時,你不能在這裏使用閉包或者匿名函數(雖然處理程序自己就是一個匿名函數),並且你的控制範圍是有限的。
另外一個方法是這樣的
element.onclick = funtion () { /* do stuff here */ }複製代碼
實際上這等價於內聯 Javascript (也就是上面那種在 HTML 標籤屬性中添加的方法),不過這樣能夠擁有更大的控制範圍,同時可使用匿名函數、函數表達式或閉包。
內聯事件有個重大的缺點就是,不像上面提到的事件監聽器那樣,你只能夠指定一個內聯事件。內聯事件會轉化元元素的屬性,那意味着當指定多個的內聯事件時,它以前所指定的內聯事件會被覆蓋掉。
使用上面 HTML 代碼中的 標籤來舉個例子:
var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };複製代碼
當你點擊這個元素後,你只能夠看到 "Did stuff #2",緣由是第二個值覆蓋了第一個指定的 onclick 屬性,同時,會把 HTML 中 onclick 屬性也覆蓋掉。
兩種寫法誰更好呢……
主要的問題是瀏覽器兼容性和必要性。你目前是否須要添加一個以上的事件到一個元素上?將來是否須要?大部分時候,你是須要的。因此,使用 attachEvent 和 addEventListener 是很是有必要的,否則用內聯事件就行了。
JQuery 以及不少其它的 Javascript 框架都爲不一樣的瀏覽器封裝了通用的處理 DOM2 事件的通用模型(Models),這樣你能夠在作跨瀏覽器兼容時不須要爲 IE 的歷史遺留問題而煩惱了。一樣的代碼在 jQuery 中作跨瀏覽器兼容,只須要這樣:
$(element).on('click', function () { /* do stuff */ });複製代碼
固然了,不要由於這麼一件事而使用一個框架。你能夠很容易地寫出一個小工具來兼容舊版本的瀏覽器:
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on' + evnt, funct);
else
return elemt.addEventListener(evnt, funct, false);
}
//example
addEvent(
document.getElementById('myElement'),
'click',
function () { aler('hi!'); }
);複製代碼
一、HTML事件處理程序
某個元素支持的每種事件,均可以使用一個與相應事件處理程序同名的HTML特性來指定。這個特性的值應該是可以執行的JavaScript代碼。
HTML中指定事件處理程序有兩個缺點。一個是時差問題。二個是HTML與JavaScript代碼緊密耦合(若是要更換事件處理程序,就要改動兩個地方:HTML代碼和JavaScript代碼)。
二、DOM0級事件處理程序
要使用JavaScript指定事件處理程序,首先必須取得一個要操做的對象的引用。
每一個元素(包括window和document)都有本身的事件處理程序屬性,這些屬性一般所有小寫,例如onclick。將這種屬性的值設置爲一個函數,就能夠指定事件處理程序:
var btn = document.getElementByIdx_x("myBtn");btn.onclick = function(){ alert("Clicked"); }
使用DOM0級方法指定的事件處理程序被認爲是元素的方法。換句話說,程序中的this引用當前元素。例如:
var btn = document.getElementByIdx_x("myBtn");btn.onclick = function(){ alert("this.id"); }
刪除DOM0級事件處理程序,只需這樣:
btn.onclick = null; //刪除事件處理程序
將事件處理程序設置爲null後,單擊按鈕將不會有任何動做發生。
若是你使用HTML指定事件處理程序,那麼onclick屬性的值就是一個包含着在同名HTML特性中的指定的代碼的函數。而將相應的屬性設置爲null,也能夠刪除以這種方式指定的事件處理函數。
三、DOM2級事件處理程序(IE不支持)
DOM2級事件定義了兩個方法,用於處理指定和刪除事件處理程序的操做:
addEventListener()和removeEventListener()。var btn = document.getElementByIdx_x("myBtn");btn.addEventListener("click",function(){alert("this.id");},false);
使用DOM2級方法添加事件處理程序的主要好處是能夠添加多個事件處理程序。例如:
var btn = document.getElementByIdx_x("myBtn");btn.addEventListener("click",function(){alert("this.id");},false);btn.addEventListener("click",function(){alert("hello world");},false);
刪除事件應用程序。例如:
var btn = document.getElementByIdx_x("myBtn"); var handler = function(){ alert(this.id); }btn.addEventListener("click",handler,false); //省略了其餘代碼btn.removeEventListener("click",handler,false);//有效!
注:匿名函數沒法刪除。
四、IE事件處理程序
IE實現了與DOM中相似的方法:attachEvent()和detachEvent()
例如:
var btn = document.getElementByIdx_x("myBtn");btn.attachEvent("onclick",function(){alert("this.id");});
注意:attachEvent()的第一個參數是 同理,刪除事件處理函數用detachEvent()函數。
一、DOM2級事件類型
(1)UI(user interface,用戶界面)事件,在用戶與頁面上的元素交互時觸發。
(2)鼠標事件,當用戶經過鼠標在頁面上執行操做是觸發。
(3)鍵盤事件,當用戶經過鍵盤在頁面上執行操做是觸發。
(4)HTML事件,當瀏覽器窗口發生變化或發生特定的客戶端/服務器交互時觸發。
(5)變更(mutation)事件,當底層DOM結構發生變化時觸發。
二、UI事件
(1)DOMActive:表示元素已經被用戶操做(經過鼠標或鍵盤)激活。
(2)DOMFocusIn:表示元素已經得到了焦點;
(3)DOMFocousOut:表示元素已經失去了焦點。
支持這幾個UI事件的瀏覽器不多,所以咱們不推薦使用。
三、鼠標事件
(1)鼠標事件觸發順序:
mousedown-》mouseup-》click-》mousedown-》mouseup-》click-》dbclick。
(2)客戶區座標位置var div = docment.getElementById("myDiv");EventUtil.addHandler(div,"click",function(event){event = EventUtil.getEvent(event);alert("Client coordinates:"+event.clientX+","+event.clientY);});
(3)屏幕座標位置
var div = document.getElementByIdx_x("myDiv");EventUtil.addHandler(div, "click", function(event){event = EventUtil.getEvent(event);alert("Screen coordinates: " + event.screenX + "," + event.screenY);});
});複製代碼
四、鍵盤事件(DOM0級事件支持)(1)鍵盤事件包括三個:keydown 、 keypress 、 keyup。(2)鍵碼,event對象的keyCode屬性中會包含一個代碼,與鍵盤上一個特定的鍵對應。對應的鍵碼請查表。五、HTML事件load、unload、abort、error、select、change、submit、reset、resize、scroll、focus、blur。多數HTML事件都與window對象或表單控件相關。六、變更事件(略)