事件就是用戶或者瀏覽器自身執行的某種動作。諸如click、load和mouseover,都是事件的名字。而響應某個事件的函數就叫事件處理程序。事件處理程序的名字以“on”開頭,比如click事件的事件處理程序是onclick。為事件指定事件處理程序的方式有多種方式。
- HTML事件處理程序
元素支持的事件,都可以使用與相應事件處理程序同名的HTML特性來指定。這個特性的值能支持一定的JavaScript代碼。例如,在單擊按鈕的時候執行一些JavaScript代碼。 <div id="aa" onclick="console.log('div')" style="width: 100px;">2222</div> ,當單擊這個div的時候,會在瀏覽器的控制台中輸出div。這個特性是通過JavaScript來實現的,不能在其中使用未經轉義的HTML語法字符,例如和號(&)、雙引號("")、單引號('')、小於號(<)或者大於號(>)。
在HTML中定義的事件處理程序可以包含要執行的具體動作,也可以調用定義在其他頁面的腳本。
1 function divClick(e){ 2 var target=e.target; 3 console.log("div"); 4 //stopBubble(e); 5 }
<div id="aa" onclick="divClick(event);" style="width: 100px;">2222</div>
上面的代碼中,點擊div之后就會調用divClick函數。這個函數是單獨定義的script腳本中的,當然也可以定義在一個外部文件中。事件處理程序中的代碼,可以訪問全局的方法。上面的代碼中,同樣可以傳遞event參數以及this參數。event參數能夠獲取事件的類型參數等,通過this能夠獲取點擊對象本身。
1 function divClick(e){ 2 //var target=e.target; 3 console.log($(e).text());//222 4 //stopBubble(e); 5 }
<div id="aa" onclick="divClick(this);" style="width: 100px;">2222</div>
在HTML中指定事件處理程序有一定的缺點:存在一定的時差,用戶可能在頁面一出現就觸發相應的事件,但是事件處理程序尚不具備執行的條件。上面的例子上,如果divClick函數定義在div的下方,我們在函數尚未解析之前,就點擊div,這樣就會導致報錯。
另一個缺點是,這樣的事件處理程序的作用域鏈在不同的瀏覽器中會導致不同結果。不同JavaScript引擎遵循的標識符解析規則略有差異,很可能在訪問非限定對象時出錯。
通過HTML指定事件處理程序的最后一個缺點是HTML與JavaScript代碼的緊密耦合。如果要更換事件處理程序就需要改動兩個地方:JavaScript和HTML。
- DOM級事件處理程序
通過JavaScript指定事件處理程序的傳統方式是將一個函數賦值給事件處理程序屬性。通過JavaScript指定事件處理程序有兩個優勢:簡單和瀏覽器兼容性好。要使用JavaScript指定事件處理程序,首先必須獲取一個元素的對象引用。每個元素都有自己的事件處理程序,這個屬性通常是全部小寫,比如onclick。
1 <div id="aa" style="width: 100px;">2222</div> 2 <script> 3 var a=document.getElementById("aa"); 4 a.onclick=function(e){ 5 var target=e.target; 6 var text=target.innerHTML; 7 console.log(text);//222 8 } 9 </script>
上面的代碼,通過文檔對象獲取了對象的引用,然后為它指定了onclick事件處理程序。e為點擊事件的參數,通過該參數能夠獲取點擊事件的對象,即target。通過對象可以進一步獲取對象的屬性。
1 <div id="aa" style="width: 100px;">2222</div> 2 <script> 3 $("#aa").click(function(e){ 4 var target=e.target; 5 var text=target.innerHTML; 6 console.log(text);//222 7 }); 8 </script>
這段代碼與上面的例子實現的是一樣的效果,但是這個是通過JQuery來實現的。
DOM2級事件處理程序定義了兩個方法,用於處理指定和刪除事件處理程序的操作:addEventListener和removeEventListener。所有DOM節點都包含這兩個方法,並且他們接收3個參數:要處理的事件名、作為事件處理程序的函數和一個布爾值。布爾值如果為true,表示在捕獲階段執行事件處理程序,如果為false,表示在冒泡階段調用事件處理程序。
1 <div id="aa" style="width: 100px;">2222</div> 2 <script> 3 var a=document.getElementById("aa"); 4 a.addEventListener("click",function(e){ 5 console.log(this.id); 6 },false); 7 </script>
上面的例子通過addEventListener為元素aa添加了一個click事件。通過事件處理程序能夠訪問到元素,this和元素處在同一個作用域鏈。
通過DOM2級可以通過添加多個事件處理程序。事件處理程序會按照添加的順序依次觸發。
1 var a=document.getElementById("aa"); 2 a.addEventListener("click",function(e){ 3 console.log(this.id); 4 },false); 5 a.addEventListener("click",function(e){ 6 console.log(this.innerHTML); 7 },false);
通過addEventListener添加的事件處理程序,可以通過removeEventListener來移除事件處理程序。但是,有時候我們會走入一個誤區。
1 var a=document.getElementById("aa"); 2 a.addEventListener("click",function(e){ 3 console.log(this.id); 4 },false); 5 a.removeEventListener("click",function(e){ 6 console.log(this.id); 7 },false);
上面的代碼我們使用removeEventListener方法移除事件處理程序,但是並沒有起作用。在使用addEventListener和removeEventListener的時候,第二個事件處理程序函數必須是同一個函數才會有作用,我們對上面的代碼作一個修改,就可以了。
1 var a=document.getElementById("aa"); 2 var callback=function(e){ 3 console.log(this.id); 4 } 5 a.addEventListener("click",callback,false); 6 a.removeEventListener("click",callback,false);
上面的代碼中,我們在addEventListener和removeEventListener中調用的是同一個方法,所以元素aa已經沒有點擊事件。
對於IE8以及IE8以下瀏覽器來說,它們並沒有上述的兩個方法,但是提供了attachEvent和detachEvent兩個方法。這兩個只需要傳遞兩個參數:第一參數事件程序名稱,第二個事件處理程序函數。
1 var a=document.getElementById("aa"); 2 var callback=function(e){ 3 console.log(e.target.innerHTML); 4 } 5 //a.addEventListener("click",callback,false); 6 //a.removeEventListener("click",callback,false); 7 a.attachEvent("onclick",callback);
上面的代碼通過attachEvent添加了事件處理程序,但是attachEvent與addEventListener不一樣。在attachEvent的事件處理程序函數中this是指向window的,我們無法獲取元素對象。
1 var a=document.getElementById("aa"); 2 var callback=function(e){ 3 console.log(this.id); 4 } 5 //a.addEventListener("click",callback,false); 6 //a.removeEventListener("click",callback,false); 7 a.attachEvent("onclick",function(e){ 8 callback.call(a,e); 9 });
上面的代碼,我們通過call修改了this的指向,但是這樣會帶來另外一個問題,怎么detachEvent?
- 跨瀏覽器事件處理程序
為了以跨瀏覽器的事件處理程序,開發人員可以封裝適合自己的js庫。
1 var EventUtil={ 2 addEvent:function(element,type,fn){ 3 if(element.addEventListener){ 4 element.addEventListener(type,fn,false); 5 } 6 else if(element.attachEvent){ 7 element.attachEvent("on"+type,fn); 8 } 9 else{ 10 element["on"+type]=fn; 11 } 12 }, 13 removeEvent:function(element,type,fn){ 14 if(element.removeEventListener){ 15 element.removeEventListener(type,fn,false); 16 } 17 else if(element.detachEvent){ 18 element.detachEvent("on"+type,fn); 19 } 20 else{ 21 element["on"+type]=null; 22 } 23 } 24 }; 25 var aa=document.getElementById("aa"); 26 var func=function(e){ 27 console.log(e.type); 28 EventUtil.removeEvent(aa,"click",func); 29 } 30 EventUtil.addEvent(aa,"click",func);
上面的EventUtil封裝了一個跨瀏覽器的對象,包含兩個方法addEvent和removeEvent。在第25行獲取元素對象引用,26行定義了fn函數,30行調用addEvent添加了事件處理程序。這個事件處理程序只能執行一次,因為我們在func函數中又調用了removeEvent函數。