JS中DOM0,DOM2,DOM3級事件模型解析

聲明:本文來源于http://www.webzsky.com/?p=731
我只是在這里作為自己的學習筆記整理一下(原文格式看的我眼睛疼)

javascript中的事件

javascript程序使用的是事件驅動的設計模式,為一個元素添加事件監聽函數,當這個元素的相應事件被觸發那么其添加的事件監聽函數就被調用:事件是javascript和HTML交互基礎, 任何文檔或者瀏覽器窗口發生的交互, 都要通過綁定事件進行交互; 所有瀏覽器都支持DOM0級事件處理程序,且使用該方式時,事件處理程序是在元素的作用域中運行,因此程序中的this都是指向元素。

如何理解DOM0,DOM2,DOM3

文檔對象模型是一種與編程語言及平臺無關的API(Application programming Interface),借助于它,程序能夠動態地訪問和修改文檔內容、結構或顯示樣式。
W3C協會早在1988年就開始了DOM標準的制定,W3C DOM標準可以分為DOM1,DOM2,DOM3三個版本。

DOM1級

主要定義的是HTML和XML文檔的底層結構。DOM2和DOM3級別則在這個結構的基礎上引入了更多的交互能力,也支持了更高級的XML特性。為此DOM2和DOM3級分為許多模塊(模塊之間具有某種關聯),分別描述了DOM的某個非常具體的子集。

這些模塊如下:
  1. DOM2級核心(DOM Level 2 Core):在1級核心的基礎上構建,為節點添加了更多方法和屬性;
  2. DOM2級視圖(DOM Level 2 Views):為文檔定義了基于樣式信息的不同視圖;
  3. DOM2級事件(DOM Level 2 Style):定義了如何以編程方式來訪問和改變CSS樣式信息;
  4. DOM2級遍歷和范圍(DOM Level 2 Traversal and Range):引入了遍歷DOM文檔和選擇其特定部分的新接口。
  5. DOM2級HTML(DOM Level 2 HTML):在1級HTML基礎上構建,添加了更多屬性、方法和新接口。
  6. DOM3級又增加了XPath模塊和加載與保存(Load and Save)模塊。
    DOM2級和3級的目的在于擴展DOM API,以滿足操作XML的所有需求,同時提供更好的錯誤處理及特性檢測能力。 DOM0就是直接通過 onclick寫在html里面的事件; DOM2是通過addEventListener綁定的事件, 還有IE下的DOM2事件通過attachEvent綁定; DOM3是一些新的事件。

0級DOM:

一開始瀏覽器處理事件的時候只有原始事件模型,事件處理程序被設置為js代碼串作為html的性質值,例如:
<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。

2級DOM

DOM級別1于1998年10月1日成為W3C推薦標準。1級DOM標準中并沒有定義事件相關的內容,所以沒有所謂的1級DOM事件模型。在2級DOM中除了定義了一些DOM相關的操作之外還定義了一個事件模型 ,這個標準下的事件模型就是我們所說的2級DOM事件模型2級DOM的事件傳播在2級DOM中,當事件發生在節點時,目標元素的事件處理函數就被觸發,而且目標的每個祖先節點也有機會處理那個事件。

因為2級DOM的事件傳播分三個階段進行。

1.capturing階段,事件從Document對象沿著文檔樹向下傳播給節點。如果目標的任何一個祖先專門注冊了事件監聽函數,那么在事件傳播的過程中就會運行這些函數。
2.下一個階段發生在目標節點自身,直接注冊在目標上的適合的事件監聽函數將運行。
3.是bubbling階段,這個階段事件將從目標元素向上傳播回Document對象(與capturing相反的階段)。雖然所有事件都受capturing階段的支配,但并不是所有類型的事件都bubbling。(0級DOM事件模型處理沒有capturing階段)

事件對象

在觸發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級DOM的事件監聽函數注冊
2級事件模型中,可以調用對象的addEventListener()方法為元素設置事件監聽函數,也就是說通過2級DOM的這個API注冊的函數才有可能在上述事件傳播三個階段中任意一個階段捕捉到事件的發生(如果用0級DOM的2個方法賦值的事件監聽函數不能在capturing階段捕捉到事件)。
1 .addEventListener第一個參數是String,事件類型名,沒有前綴on,比如要注冊click事件就傳入“click”,不是“onclick”2 .第二個參數是監聽函數,在調用的時候js會傳給他一個Event對象,這個對象放了有關事件的細節,如果調用的這個對象的stopPropagation()方法,則會阻止事件傳播進一步傳播(比如在第一個階段捕捉到事件并運行事件監聽函數,其中調用了event。stopPropagation則事件就不會再被傳播經歷第二第三階段了)3 .第三個參數是boolean,true表示事件監聽函數能夠在三個階段中的任意一個階段捕捉到事件(符合2級DOM標準),如果是false就表示事件監聽函數不能在capturing階段捕捉到事件(表現同0級DOM)。2級DOM中監聽函數中的this
通過addEventListener添加的函數中的this,標準中并沒有規定this必須指向目標元素, 盡管大多數瀏覽器都是這么實現的,但最終還是取決于瀏覽器的實現,我們需要用到目標元素的時候請調用event.currentTarget.

2級DOM的Event對象

用addEventListener添加的事件監聽函數,在被調用的時候js會傳給他一個Event對象,下面就是這個Event對象的常用屬性type:發生的事件的類型,例如"click", "mouseover"target:發生事件的節點,可能與currentTarget不同currentTarget:正在處理事件的節點,如果在capturing階段和冒泡階段處理事件,這個屬性就與target屬性不同。在事件監聽函數中應該用這個屬性而不是thisstopPropagation():可以阻止事件從當前正在處理他的節點傳播preventDefault():阻止瀏覽器執行與事件相關的默認動作,與0級DOM中返回false一樣clientX, clientY:鼠標相對于瀏覽器的x坐標y坐標screenX, screenY:鼠標相對于顯示器左上角的x坐標y坐標

IE事件模型

1.Event對象不是傳遞給事件監聽函數,而是通過window對象的event屬性訪問Event對象.
2.IE Event對象常用屬性type:
兼容DOM的type屬性srcElement:
兼容DOM的target屬性clientX, clientY:
兼容DOM的clientX, clientY屬性cancelBubble:布爾值,設為true同調用stopPropagation()returnValue:布爾值,設為false同調用preventDefault()3.事件監聽函數注冊沒有addEventListener,只有attachEvent。2個參數,同addEventListener前兩個,只是事件名帶前綴on。 
IE事件模型沒有capturing階段所以調用attachEvent相當于調用addEvetnListener且第三個參數為false:
document.getElementById("myTest").attachEvent("onclick", function(){alert(1)});
相當于
document.getElementById("myTest").addEventListener("click", function(){alert(1)}, false);
4.用attachEvent注冊的函數將被作為全局函數調用,而不是作為發生事件的文檔元素的方法,也就是說this引用的是window對象,而不是事件的目標元素。

事件對象event下的屬性和方法

因為各個瀏覽器的事件對象不一樣, 把主要的事件對象的屬性和方法列出來;

  • bubble : 表明事件是否冒泡
  • cancelable : 表明是否可以取消冒泡
  • currentTarget : 當前事件程序正在處理的元素, 和this一樣的;
  • defaultPrevented: false ,如果調用了preventDefualt這個就為真了;
  • detail: 與事件有關的信息(滾動事件等等)
  • eventPhase: 值為1表示處于捕獲階段, 值為2表示處于目標階段,值為3表示在冒泡階段
  • target || srcElement: 事件的目標
  • trusted: 為ture是瀏覽器生成的,為false是開發人員創建的(DOM3)
  • type : 事件的類型 view : 與元素關聯的window, 我們可能跨iframe;
  • preventDefault() 取消默認事件;
  • stopPropagation() 取消冒泡或者捕獲;
  • stopImmediatePropagation() (DOM3)阻止任何事件的運行; stopImmediatePropagation阻止綁定在事件觸發元素的 其他同類事件的callback的運行 IE下的事件對象是在window下的,而標準應該作為一個參數, 傳為函數第一個參數; IE的事件對象定義的屬性跟標準的不同,如: cancelBubble 默認為false, 如果為true就是取消事件冒泡; returnValue 默認是true,如果為false就取消默認事件; srcElement, 這個指的是target, Firefox下的也是srcElement;

事件監聽** (Event Listeners,包括 addEventListener 以及 IE 的 attacheEvent)**

舊版本的 IE 在執行 Javascript 時與幾乎所有其它瀏覽器不同,在 IE 9 之前的版本中,你需要使用 attachEvent 模塊,就像這樣:
element.attachEvent('onclick', function() { /* do stuff here*/ });
在大部分其它瀏覽器(包括 IE 9 以及更新的版本)中,你可以使用 addEventListener,就像這樣:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
使用這些方法(DOM2 事件),理論上你可以向某個元素加入無數的事件。但實際上,這會受限于客戶端的內存容量以及其它的性能問題,而這對于每一個瀏覽器都是不同的。
上面的例子使用的都是匿名函數,你也可以將一個函數表達式或者一個閉包添加到事件監聽上:
var myFunctionReference = function(){ /* do stuff here */ } element.attachEvent('onclick', myFunctionReference);element.addEventListener('click', myFUnctionReference, false);
addEventListener 還有一個特點就是最后的參數,它會控制監聽在事件冒泡階段時就作出反應。有大約 95% 的可能會像我在例子中那樣使用 false。這個參數在 attachEvent 中或在使用內聯事件(inline Events) 時沒有等效的參數。

內聯事件 (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);elsereturn elemt.addEventListener(evnt, funct, false);} //exampleaddEvent(document.getElementById('myElement'),'click',function () { aler('hi!'); });

事件處理程序

1、HTML事件處理程序某個元素支持的每種事件,都可以使用一個與相應事件處理程序同名的HTML特性來指定。這個特性的值應該是能夠執行的JavaScript代碼。

HTML中指定事件處理程序有兩個缺點。一個是時差問題。二個是HTML與JavaScript代碼緊密耦合(如果要更換事件處理程序,就要改動兩個地方:HTML代碼和JavaScript代碼)。

2、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,也可以刪除以這種方式指定的事件處理函數。

3、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);//有效!
注:匿名函數無法刪除。
4、IE事件處理程序 IE實現了與DOM中類似的方法:attachEvent()和detachEvent()**
例如:
var btn = document.getElementByIdx_x("myBtn"); btn.attachEvent("onclick",function(){alert("this.id");});
注意:attachEvent()的第一個參數是onclick,而非click。同時,這里的this不在是作用域內的元素,而是window對象。 同理,刪除事件處理函數用detachEvent()函數。

事件類型

1、DOM2級事件類型
(1)UI(user interface,用戶界面)事件,在用戶與頁面上的元素交互時觸發。      
(2)鼠標事件,當用戶通過鼠標在頁面上執行操作是觸發。
(3)鍵盤事件,當用戶通過鍵盤在頁面上執行操作是觸發。
(4)HTML事件,當瀏覽器窗口發生變化或發生特定的客戶端/服務器交互時觸發。
(5)變動(mutation)事件,當底層DOM結構發生變化時觸發。

2、UI事件
(1)DOMActive:表示元素已經被用戶操作(通過鼠標或鍵盤)激活。
(2)DOMFocusIn:表示元素已經獲得了焦點;
(3)DOMFocousOut:表示元素已經失去了焦點。支持這幾個UI事件的瀏覽器很少,
因此我們不推薦使用。
3、鼠標事件
(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);});
4、鍵盤事件(DOM0級事件支持)
(1)鍵盤事件包括三個:keydown 、 keypress 、 keyup。
(2)鍵碼,event對象的keyCode屬性中會包含一個代碼,與鍵盤上一個特定的鍵對應。對應的鍵碼請查表。
5、HTML事件load、unload、abort、error、select、change、submit、reset、resize、scroll、focus、blur。多數HTML事件都與window對象或表單控件相關。
6、變動事件(略)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容