JavaScript事件冒泡

                     

JavaSciprt事件中有兩個很重要的特性:事件冒泡以及目標元素。this

      當一個元素上的事件被觸發的時候,好比說鼠標點擊了一個按鈕,一樣的事件將會在那個元素的全部祖先元素中被觸發。這一過程被稱爲事件冒泡;這個事件從原始元素開始一直冒泡到DOM樹的最上層。spa

      任何一個事件的目標元素都是最開始的那個元素,在咱們的這個例子中也就是按鈕,而且它在咱們的元素對象中以屬性的形式出現。使用事件代理的話咱們能夠把事 件處理器添加到一個元素上,等待一個事件從它的子級元素裏冒泡上來,而且能夠很方便地得知這個事件是從哪一個元素開始的。.net

事件的冒泡和捕獲

捕獲是從上級元素到下級元素,冒泡是從下級元素到上級元素。firefox

      在IE中,每一個元素和window對象都有兩個方法:attachEvent()和detachEvent()。attachEvent()用來給一個事件附加事件處理函數。而detachEvent()用來將事件處理函數分離出來。

?

1
2
3
4
5
6
var fnClick = function () {
alert( "Clicked!" );
}
var oDiv = document.getElementById( "div1" );
oDiv.attachEvent( "onclick" , fnClick);
oDiv.detachEvent( "onclick" , fnClick);

事件的冒泡有什麼好處

     想象一下如今咱們有一個10列、100行的HTML表格,你但願在用戶點擊表格中的某一單元格的時候作點什麼。好比說我有一次就須要讓表格中的每個單 元格在被點擊的時候變成可編輯狀態。若是把事件處理器加到這1000個單元格會產生一個很大的性能問題,而且有可能致使內存泄露甚至是瀏覽器的崩潰。相反 地,使用事件代理的話,你只須要把一個事件處理器添加到table元素上就能夠了,這個函數能夠把點擊事件給截下來,而且判斷出是哪一個單元格被點擊了。

      代碼很簡單,咱們所要關心的只是如何檢測目標元素而已。比方說咱們有一個 table元素,ID是「report」,咱們爲這個表格添加一個事件處理器以調用editCell函數。editCell函數須要判斷出傳到table 來的事件的目標元素。考慮到咱們要寫的幾個函數中都有可能用到這一功能,因此咱們把它單獨放到一個名爲getEventTarget的函數中:

?

1
2
3
4
function getEventTarget(e) {
    e = e || window.event;
    return e.target || e.srcElement;
}

e這個變量表示的是一個事件對象,咱們只須要寫一點點跨瀏覽器的代碼來返回目標元素,在IE裏目標元素放在srcElemtn屬性或event.toElement屬性中,而在其它瀏覽器裏則是target或event.relatedTarget屬性。

接下來就是editCell函數了,這個函數調用到了 getEventTarget函數。一旦咱們獲得了目標元素以後,剩下的事情就是看看它是不是咱們所須要的那個元素了。

?

1
2
3
4
5
6
function editCell(e) {
    var target = getEventTarget(e);
    if (target.tagName.toLowerCase() === 'td' ) {
      // DO SOMETHING WITH THE CELL
    }
}

      在editCell函數中,咱們經過檢查目標元素標籤名稱的方法來肯定它是不是一個表格的單元格。這種檢查也許過於簡單了點;若是它是這個目標元素單元格 裏的另外一個元素呢?咱們須要爲代碼作一點小小的修改以便於其找出父級的td 元素。若是說有些單元格不須要被編輯怎麼辦呢?此種狀況下咱們能夠爲那些不可編輯的單元格添加一個指定的樣式名稱,而後在把單元格變成可編輯狀態以前先檢 查它是否不包含那個樣式名稱。選擇老是多樣化的,你只需找到適合你應用程序的那一種。

事件冒泡的優勢和缺點

     那些須要建立的以及駐留在內存中的事件處理器少了。這是很重要的一點,這樣咱們就提升了性能,並下降了崩潰的風險。

      在DOM更新後無須從新綁定事件處理器了。若是你的頁面是動態生成的,好比說經過Ajax,你再也不須要在元素被載入或者卸載的時候來添加或者刪除事件處理器了。

      潛在的問題也許並不那麼明顯,可是一旦你注意到這些問題,你就能夠輕鬆地避免它們:你的事件管理代碼有成爲性能瓶頸的風險,因此儘可能使它可以短小精悍。

不是全部的事件都能冒泡

blur、focus、load和unload不能像其它事件同樣冒泡。事實上blur和focus能夠用事件捕獲而非事件冒泡的方法得到(在IE以外的其它瀏覽器中)。

須要注意的是:若是你的代碼處理mousemove事件的話你趕上性能瓶頸的風險可就大了,由於mousemove事件觸發很是頻繁。而mouseout則由於其怪異的表現而變得很難用事件代理來管理。

阻止事件冒泡

在IE下解決問題很簡單,用onMouseEnter、 onMouseLeave來代替onMouseOver、onMouseOut就好了,他們的做用基本相同,前者不會發生冒泡。可是 firefox下沒有這兩個事件.

?

1
window.event.cancelBubble = true (IE)   event.stopPropagation()  event.preventDefault() (Firefox)

在IE和FF瀏覽器內阻止冒泡行爲是不一樣的。IE中使用cancelBubble,FF中使用stopPropation()。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<html>
<head>
<title>阻止冒泡</title>
<style>
body{ color: #333; font-size:12px; }
</style>
</head>
<body>
<p>什麼是事件冒泡,通俗的來說就是 咱們爲頁面內body添加一個單擊事件,一樣再爲頁面內li元素添加一個單擊事件..當你單擊li的時候.<br>
body的事件也會被觸發.....由於li被包含在body元素內...你單擊了li同時也單擊了body...這就是事件冒泡....<br />
在有些時候.咱們要阻止這種狀況發生...也就是單擊li事件.不會觸發body的事件.... 這就叫阻止冒泡!
<div id= "a" >
     <ul id= "lia" >請單擊下面的列表.會觸發body事件
         <li>項目1</li>
         <li>項目2</li>
         <li>項目3</li>
     </ul>
 
     <ul id= "lib" >請單下面的列表.不會觸發body事件.
         <li>項目1</li>
         <li>項目2</li>
         <li>項目3</li>
     </ul>
</div>
<script language= "javascript" >
document.body.onclick = function (){ //首先爲body元素綁定一個單擊事件
     alert( "BODY事件" ); //單擊頁面即彈出對話框
}
 
function att_Event(){ //爲第一組li元素綁定onclick事件
     var li = document.getElementById( "lia" ).getElementsByTagName( "li" );
     for ( var i=0;i<li.length;i++){
         li[i].onclick = function (){
             alert( "Li事件" );
         }
     }
}
 
function att_Event_b(){ //爲第二組li元素綁定onclick事件
     var li = document.getElementById( "lib" ).getElementsByTagName( "li" );
     for ( var i=0;i<li.length;i++){
         li[i].onclick = function (e){
             alert( "Li事件" );
             stopBubble(e); //運行阻止冒泡的函數
         }
     }
}
//該函數的功能用來阻止事件冒泡.併兼容多瀏覽器
function stopBubble(e){
     //若是傳入了事件對象.那麼就是非IE瀏覽器
     if (e && e.stopPropagation){
         //所以它支持W3C的stopPropation()方法
         e.stopPropagation();
     }
     else
     {
         //不然,咱們得使用IE的方式來取消事件冒泡
         window.event.cancelBubble = true ;
     }
}
 
window.onload = function (){
     att_Event();
     att_Event_b();
}
</script>
</body>
</html>

阻止jQuery事件冒泡

jQuery對DOM的事件觸發具備冒泡特性。有時利用這一特性能夠減小重複代碼,但有時候咱們又不但願事件冒泡。這個時候就要阻止 jQuery.Event冒泡。

在jQuery.Event的文檔中的開頭得知,jQuery.Event對象是符合W3C標準的一個事件對象,同時jQuery.Event免去了檢查兼容IE的步驟。

jQuery.Event提供了一個很是簡單的方法來阻止事件冒泡:event.stopPropagation();

?

1
2
3
4
$( "p" ).click( function (event){
      event.stopPropagation();
      // do something
})

可是這個方法對使用live綁定的事件沒有做用,須要一個更簡單的方法阻止事件冒泡:return false;

?

1
2
3
4
$( this ).live( "click" , function (){
      "Another paragraph!" ;
      return false