javascript在事件處理機制上經歷了三個階段 html事件處理、dom0級事件處理、dom2級事件處理。
html事件處理:
形如
<div οnclick='alert(this.id)'id="myDiv"></div>
該單擊操作是通過onclick特性並將一些js代碼作爲值來定義。正因爲這個值是js,因此不能使用一些未經轉義的html語法字符,例如""等,要想使用雙引號,上述例子需要修改爲:
<div οnclick="alert("this.id")"></div>
在html中定義事件處理通常都會調用頁面其他地方定義的函數腳本,
<div οnclick="clickme()"></div>
在html中定義事件處理有兩個缺點:
1.時差問題,用戶可能會在html元素一出現在頁面上就觸發相應事件,但有可能在解析函數之前html加載之後用戶單擊該元素就會引發錯誤。
2.html與js代碼耦合性較高,想修改一個事件必須修改兩處。
DOM0級事件處理:
實現該事件處理首先要得到對html元素操作對象的引用,然後爲其指定一個事件處理程序如onclick。
var myBtn=document.getElementById("myDiv");
myBtn.οnclick=function(){alert(this.id)}//其中的this指向元素作用域,即表示當前元素。
dom0級事件處理有一個大的缺陷:
如果我們定義兩個同樣的事件時候,後一個就會把前一個給覆蓋掉,例如onclick事件,後一個該元素的onclick就會覆蓋前一個的onclick事件。
不過雖然有這麼個缺點,開發人員一般都會注意到的,從而很多人還是傾向於應用該處理方式,原因就是寫起來簡單而且沒有瀏覽器兼容問題。
DOM2級處理事件:
DOM2級事件處理主要通過一個方法addEventListener()來實現,所有dom節點都包含該方法,接受三個參數,要處理事件名,事件處理函數和一個布爾值(該布爾值就是下面將要說到的冒泡和捕獲);
var myBtn=document.getElementById("myDiv");
myBtn.addEventListener("click",myFunction,false);
利用該方法可以添加多個事件處理程序,並且可以通過removeEventListener()來移除事件,
需要注意的是第二個參數如果應用匿名函數的話就不能成功將其移除了,所以儘量都寫成這種函數名形式便於移除,
同時在ie低版本瀏覽器中並不是通過該方法實現dom2級事件處理的,而是通過attachEvent和detachEvent()配套完成,
不同的是後者只接受兩個參數,第三個布爾值不會接受(因爲ie只支持冒泡);
接下來我們討論一下事件流捕獲和冒泡事件處理機制,也就是上面addEventListener的第三個參數代表的意思:
假設我們畫一個同心圓,我們點擊圓心時候其實指向的不止是最裏面那個圓,而是這組同心圓的所有圓,如果我們在這組同心圓都定義了相同的事件,那麼是先執行外面的事件還是內部的事件呢?
於是就產生了捕獲與冒泡兩種不同的處理機制;
捕獲:
捕獲的思想是不太具體的節點最先接受到事件,而最具體的節點最後接收到事件,達到在事件到達預定目標之前捕獲該事件的目的。
冒泡:
冒泡正好相反,即事件最先由具體目標接受,然後逐級傳播到最不具體的上級節點文檔,可能這因爲微軟是做操作系統原因,借鑑於其桌面處理方式只支持冒泡。
w3c中和兩種不同機制,規定DOM2級事件流包括三個階段:事件捕獲階段、處於目標階段、和事件冒泡階段,
並且規定在捕獲階段不會觸發對象上的事件,都是在冒泡階段進行觸發的。而瀏覽器都會在捕獲階段觸發對象事件,於是就有兩個機會在目標對象上操作事件。
上面說到addEventListener()方法接受的第三個參數是個布爾值,如果是true時候則在捕獲階段調用事件處理程序,如果是false則是在冒泡階段處理事件。
舉例如下:
<html>
<body>
<div id="div1" style='width: 400px; height: 400px;border: 1px solid #CCCCCC'>
<div id="div2" style='width: 200px; height: 200px;border: 1px solid #CCCCCC'>
</div></div>
</body>
<script>
function $(a){
return document.getElementById(a)
}
function ale(e){alert(this.id)}
</script>
</html>
$('div1').addEventListener('click',ale,false);
$('div2').addEventListener('click',ale,false);
在同時定義情況下點擊div2,會先觸發div2的事件,然後冒泡到div1再執行div1的事件,
$('div1').addEventListener('click',ale,true);
$('div2').addEventListener('click',ale,false);
如果在div1定義爲捕獲事件,則事件傳播同樣會遵從w3c規則,先捕獲,div1是捕獲階段觸發所以先執行div1事件,
然後再執行div2事件,再向上冒泡檢測是否有冒泡事件處理程序,沒檢測到則返回結束。
$('div1').addEventListener('click',ale,fasle);
$('div2').addEventListener('click',ale,true);
如果在div1定義了冒泡處理程序,div2定義了捕獲階段處理程序,點擊div2時候div1沒捕獲到事件,而div2捕獲到了,
於是先觸發div2事件,然後冒泡檢測是否有冒泡事件定義,div1定義的是冒泡事件,隨後就會執行div1事件
$('div1').addEventListener('click',ale,true);
$('div2').addEventListener('click',ale,true);
同時都設置爲捕獲階段處理事件,div1首先捕獲到事件於是觸發div1事件,而後div2捕獲到事件,進而執行div2事件,然後向上冒泡沒有冒泡處理程序,則返回結束。
通常情況下 我們都不想點擊一個元素時候希望它繼續向上或者向下傳播,w3c爲我們提供了取消事件傳播的方法:即event.stopPropagation();
而event.perventDefault()則是取消事件默認行爲(例如a標籤點擊跳轉).
因爲ie的不合作導致瀏覽器兼容性的出現在這就不做評價了,下面附上ie兼容的一段處理dom2級事件方法:
myEvent={
addEvent:function(ele,type,handler){
if(ele.addEventListener){
ele.addEventListener(type,handler,false);
}
else if(ele.attachEvent){
ele.attachEvent("on"+type,handler)
}
else{
ele("on"+type)=handler
}
},
removeEvent:function(ele,type,handler){
if(ele.removeEventListener){
ele.removeEventListener(type,handler,false);
}
else if(ele.datachEvent){
ele.datachEvent('on'+type,handler)
}
else{
ele["on"+type]=null;
}
},
preventDefalut:function(e){
if(e.preventDefault){
e.preventDefault();
}
else{
e.returnValue=false;
}
},stopPropagation:function(e){
if(e.stopPropagation){
e.stopPropagation();
}
else{
e.cancelBubble=true;
}
}
}
使用方法如下:
myEvent.addEvent(ele,"click",handler);
myEvent.removeEvent(ele,"click",handler);
這個很基本,只是讓大家從code層次理解一下事件兼容性的處理