0. 事件與事件流 事件是瀏覽器與文檔交互的瞬間,如點擊按鈕、填寫表格等操作,它是Javascript與HTML之間溝通的橋梁。DOM是樹狀結構,如果同時給父節點都綁定事件時,當觸發子節點的時候,這兩個事件的發生順序就牽涉到事件流的內容,它描述的是頁面接受時間的順序。事件流描述的是從頁面接收事件的順 ...
0. 事件與事件流
事件是瀏覽器與文檔交互的瞬間,如點擊按鈕、填寫表格等操作,它是Javascript與HTML之間溝通的橋梁。DOM是樹狀結構,如果同時給父節點都綁定事件時,當觸發子節點的時候,這兩個事件的發生順序就牽涉到事件流的內容,它描述的是頁面接受時間的順序。事件流描述的是從頁面接收事件的順序,但比較有意思的是IE和Netscape開發團隊居然提出了差不多完全相反的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕獲流。
從而使得 事件流主要分為兩種:事件冒泡和事件捕獲。
IE的事件流叫做事件冒泡,即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然後逐級向上傳播到較為不具體的節點(文檔)。Netscape團隊提出的另一種事件流叫做事件捕獲(event capturing),事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。捕獲的用意在於在事件到達預定目標前捕獲它。
DOM2級事件規定的事件流包括三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。首先發生的是事件捕獲,為截獲事件提供了機會。然後是實際的目標接收到事件。最後一個階段是冒泡階段,可以在這個階段對事件做出響應。
1. DOM0級事件模型
DOM0級事件模型是早期的事件模型,又稱為原始事件模型,在該模型中,事件不會傳播,即沒有事件流的概念。事件綁定監聽函數較為簡單,要使用Javascript指定事件處理程式,首先必須取得一個要操作的對象的引用。
每個元素(包括window和document)都有自己的事件處理屬性,這些屬性通常全部小寫,如onclick。將這種屬性設置成函數就可以指定事件處理程式:
var btn = document.getElementById("myBtn"); btn.onclick = function() { alert("Clicked!"); };
btn.onclick = null; //刪除事件處理程式
//HTML事件處理程式 <form method="post"> <input type="text" name="username" value=""> <input type="button" value="Username" onclick = "alert(username.value)"> </form>
2. DOM2級事件模型
在這種模型中,分為三個過程:事件捕獲階段、處於目標階段7事件冒泡階段;
-
事件捕獲階段(capturing phase)。事件從document一直向下傳播到目標元素, 依次檢查經過的節點是否綁定了事件監聽函數,如果有則執行。
-
事件處理階段(target phase)。事件到達目標元素, 觸發目標元素的監聽函數。
-
事件冒泡階段(bubbling phase)。事件從目標元素冒泡到document, 依次檢查經過的節點是否綁定了事件監聽函數,如果有則執行。
DOM2級定義了兩個方法,用於處理指定和刪除事件處理程式的操作,addEventListener()和removeEventListener()。所有DOM節點都包含這兩個方法,並且有三個參數,要處理的事件名、作為事件處理程式的函數和一個布爾值。要在click事件添加事件處理程式,可以用:
var btn = document.getElementById("myBtn"); btn.addEventListener("click", functioin() { alert(this.id); }, false); btn.addEventListener("click", function() { alert("Hello Kid"); }, false);
此時執行順序是順序執行:"myBtn" "Hello kid"。IE中的話執行順序正好相反。
移除事件監聽函數的方式如下:
var btn = document.getElementById("myBtn"); var handler = function() { alert(this.id); };
btn.addEventListener("click", handler, false);
//
btn.removeEventListener("click", handler, false);
這裡只能用函數表達式的形式作為事件處理程式,因為removeEventListener()移除的時候要求傳入的參數與添加應用程式時使用的參數相同。而通過匿名函數添加的事件監聽函數將無法被移除。
3. IE中的事件模型
IE中使用與DOM中類似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的兩個參數,事件處理程式名稱和事件處理程式函數。由於IE8及更早的版本只支持事件冒泡,所以通過attachEvent()添加的事件處理程式都會被添加到冒泡階段。如果使用attachEvent()為按鈕添加一個事件處理程式可用:
var btn = document.getElementById("myBtn"); var handler = function() { alert(this.id); }; btn.attachEvent("onclick", handler);//添加事件處理程式 btn.detachEvent("onclick", handler);//刪除事件處理程式
4. 事件對象
DOM中的事件對象
- type表示被觸發的事件類型
- target表示事件的目標
- currentTarget表示事件處理程式當前正在處理事件的那個元素
- cancelable (Boolean) 表明是否可以取消事件的預設行為
- bubbles (Boolean)表明事件是否冒泡
- perventDefault()取消事件的預設行為。如果cancelable為true,則可以使用這個方法
- stopPropagation()取消事件的進一步捕獲或冒泡。如果bubbles為true,則可以使用這個方法。
IE中的事件對象
- type表示被觸發的事件類型
- srcElement表示事件的目標
- cancelBubble (Boolean)預設是false,將其設為true就可以取消事件冒泡
- returnValue (Boolean) 預設是true,將其設置為false就可以取消事件的預設行為
有了上面的事件對象,就可以寫出跨瀏覽器的事件對象封裝成事件包裹了。
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){ //DOM2 element.removeEventListener(type, handler, false); } else if (element.detachEvent){ //IE element.detachEvent("on" + type, handler); } else { element["on" + type] = null; //DOM0 } }, 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; } }, stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; } } };