天天看點

javascript事件處理方式之捕獲冒泡

javascript在事件處理機制上經曆了三個階段 html事件處理、dom0級事件處理、dom2級事件處理。

html事件處理:

形如

<div οnclick='alert(this.id)'id="myDiv"></div>

該單擊操作是通過onclick特性并将一些js代碼作為值來定義。正因為這個值是js,是以不能使用一些未經轉義的html文法字元,例如""等,要想使用雙引号,上述例子需要修改為:

<div οnclick="alert(&quot;this.id&quot;)"></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的第三個參數代表的意思:

javascript事件處理方式之捕獲冒泡

假設我們畫一個同心圓,我們點選圓心時候其實指向的不止是最裡面那個圓,而是這組同心圓的所有圓,如果我們在這組同心圓都定義了相同的事件,那麼是先執行外面的事件還是内部的事件呢?

于是就産生了捕獲與冒泡兩種不同的處理機制;

捕獲:

捕獲的思想是不太具體的節點最先接受到事件,而最具體的節點最後接收到事件,達到在事件到達預定目标之前捕獲該事件的目的。

冒泡:

冒泡正好相反,即事件最先由具體目标接受,然後逐級傳播到最不具體的上級節點文檔,可能這因為微軟是做作業系統原因,借鑒于其桌面處理方式隻支援冒泡。

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層次了解一下事件相容性的處理

繼續閱讀