JavaScript與HTML之間的交互是通過事件實現的。
事件是文檔或浏覽器窗口中發生的一些特定的交互瞬間,在事件上可以注冊處理程序,以便當事件發生時,處理程序中的代碼得到執行(這種模型對應設計模式中的觀察者模式)。
事件流描述的是從頁面接受事件的順序。
IE事件流叫做事件冒泡,即事件開始由最具體的元素接受,然後逐漸向上傳播到較為不具體的節點。
事件冒泡能夠被所有浏覽器支持。注意IE5.5及更早版本的事件冒泡會跳過<html>元素(從<body>直接跳到document)。IE9,Firefox,Chrome和Safari則將事件一直冒泡到window對象。
事件捕獲是由不太具體的節點最早接受到事件,而最具體的元素最後接受到事件。事件捕獲的用意在於在事件到達預定的目標之前捕獲它。
事件捕獲不支持老版本的浏覽器,但IE9,Firefox,Chrome,Opera和Safari目前都支持這種事件流模型。
"DOM2事件流"包括三個階段:事件捕獲階段,處於目標階段和事件冒泡階段;在DOM事件流中,實際的目標在捕獲階段不會接受到事件(但IE9、Safari、Chrome、Firefox和Opera 9.5及更高版本都會在捕獲階段觸發事件對象上的事件,即有兩個機會在目標對象上面操作事件)。
IE9,Firefox,Chrome,Opera和Safari都支持DOM事件流;IE8及更早版本不支持DOM事件流。
事件是用戶或浏覽器自身執行的某種動作,常見的事件有click,focus,load;而響應事件的函數叫做事件處理程序(或事件偵聽器)。事件處理程序的名字以"on"開頭,例如click事件對應的事件處理程序就是onclick,focus事件對應的事件處理程序就是onfocus,load事件對應的事件處理程序就是onload。
HTML元素支持的每種事件,都可以用一個與相應事件處理程序同名的HTML特性來指定。這個特性的值應該是能夠執行的JavaScript代碼。例如:
<input type="button" value="click me" onclick="showMessage()"/>
HTML中指定事件處理程序有兩個缺點:
首先存在時差問題。如果在HTML元素一出現在頁面就觸發相應的事件,但當時的事件處理程序有可能尚不具備執行條件。具體說來,上例中如果事件處理程序showMessage是在按鈕下方,頁面底部定義的,當用戶在頁面解析該函數之前就單擊了按鈕,就會引發錯誤;
另一個缺點是,這樣擴展事件處理程序的作用鏈在不同浏覽器中會導致不同結果。
通過HTML指定事件處理程序的另一個缺點是HTML和JavaScript代碼緊密耦合。如果要更換事件處理程序,需要同時改動兩個地方:HTML代碼和JavaScript代碼。
指定事件處理程序的傳統方式就是將一個函數賦值給一個事件處理程序屬性。這種方式優點:1.簡單,2.具有跨浏覽器的優勢。為元素指定事件處理程序分兩步:
(1)取得要操作對象的引用;
(2)將該元素的事件處理程序屬性如onclick設置為一個函數。示例如下:
<input type="button" value="click me" id="myBtn"/> <script> var btn = document.getElementById('myBtn'); btn.onclick = function(){ alert("I am clicked!"); } </script>
使用DOM0級方法指定的事件處理程序被認定為是元素的方法,即事件處理程序是在元素的作用域中運行,此時this引用當前元素。
刪除DOM0級事件處理程序:btn.onclick = null;
DOM2級事件定義了兩個方法,用於處理指定和刪除事件處理程序的操作:addEventListener()和removeEventListener()。這兩個方法都接受三個參數:要處理的事件名稱,事件處理程序函數,布爾值。其中布爾值為true表示在捕獲階段調用事件處理程序,否則表示在冒泡階段調用事件處理程序。
與DOM0級事件處理程序一樣,DOM2級事件處理程序也是在依附的元素的作用域中運行。使用DOM2級方法添加事件處理程序的好處是可以添加多個事件處理程序。示例如下:
<input type="button" value="click me" id="myBtn"/> <script> var btn = document.getElementById('myBtn'); btn.addEventListener("click", function(){ alert("first"); }, false); btn.addEventListener("click", function(){ alert("second"); }, false); </script>
運行後,先後彈內容為"first"和"second"的對話框,說明可以通過addEventListener為元素添加多個事件處理程序。
通過addEventListener()添加的事件處理程序只能通過removeEventListener()來移除;移除時傳入的參數與添加時傳入的參數相同。注意通過addEventListener()添加的匿名函數將無法移除。
<input type="button" value="click me" id="myBtn"/> <script> var btn = document.getElementById('myBtn'); btn.addEventListener("click", function(){ alert("first"); }, false); btn.removeEventListener("click", function(){ //失效:刪除匿名函數並不是同一個函數 alert("first"); }, false); var secondFunc = function(){ alert("second"); }; btn.addEventListener("click", secondFunc, false); btn.removeEventListener("click", secondFunc, false);//成功:刪除同一個處理函數 </script>
運行後只有一個彈窗,內容為"first",說明在使用removeEventListener()刪除addEventListener()添加的事件處理函數時,必須保證第二個參數非匿名函數。
DOM2級事件處理程序的添加大多數都是在事件的冒泡階段(第三個參數為false),這樣可以最大限度地兼容各種浏覽器。
IE9,Firefox,Chrome,Opera和Safari支持DOM2級事件處理程序。
IE實現了與DOM中類似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的兩個參數:事件處理程序名稱和事件處理程序函數。由於IE8及更早的版本只支持事件冒泡,所以通過attachEvent()添加的事件處理程序都會被添加到冒泡階段。
在IE中使用attachEvent()與使用DOM0級方法的主要區別是事件處理程序的作用域,使用attachEvent()方法的情況下,事件處理程序會在全局作用域運行,因此this === window。
attachEvent()添加多個事件處理程序,執行順序與添加順序相反。
使用attachEvent()添加的事件可以通過detachEvent()來移除,條件是必須提供相同的參數。與DOM方法一樣,添加的匿名函數不能被移除。
支持IE事件處理程序的浏覽器有IE和Opera。
要保證事件處理程序在大多數浏覽器中一致地運行,只需關注冒泡階段。
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){ element.removeEventListener(type, handler, false); } else if(element.detachEvent){ element.detachEvent("on" + type, handler); } else{ element["on" + type] = null; } } };
兼容DOM(DOM0級和DOM2級)的浏覽器會將一個event對象傳入到事件處理程序中。
要阻止特定的默認行為用preventDefault()方法,要立即停止事件在DOM層次中的傳播用stopPropagation()方法。
在事件處理程序中,對象this始終等於currentTarget的值,而target則只包含事件的實際目標。
與訪問DOM中的event對象不同,IE中的event對象有幾種不同的方式,取決於指定事件處理程序的方法。
使用DOM0級方法添加事件處理程序時,event作為window對象的一個屬性存在;如果使用attachEvent()添加事件處理程序,那麼就會有一個event對象作為參數被傳入事件處理函數中(也可以通過window對象來訪問event對象)。
IE的event對象包含於創建它的事件相關的屬性和方法。
其中cancelBubble設置為true與DOM中stopPropagation()方法的作用相同;
returnValue設置為false與DOM中preventDefault()方法的作用相同。
srcElement表示事件的目標,與DOM中target屬性相同。
type被觸發的事件類型與DOM中type屬性相同。
在前面的EventUtil中添加方法來處理DOM和IE中event對象的差異(以下只展示添加的代碼):
getEvent: function(event){ return event ? event : window.event; }, getTarget: function(event){ return event.target || event.srcElement; }, preventDefalut: function(event){ if(event.preventDefalut){ event.preventDefalut(); } else{ event.returnValue = false; } }, stopPropagation: function(event){ if(event.stopPropagation){ event.stopPropagation(); } else{ event.cancelBubble = true; } }