你真的理解 事件冒泡 和 事件捕獲 嗎?

1. 事件冒泡與事件捕獲

事件冒泡和事件捕獲分別由微軟和網景公司提出,這兩個概念都是爲了解決頁面中事件流(事件發生順序)的問題。html

<div id="outer">
    <p id="inner">Click me!</p>
</div>複製代碼

上面的代碼當中一個div元素當中有一個p子元素,若是兩個元素都有一個click的處理函數,那麼咱們怎麼才能知道哪個函數會首先被觸發呢?node

爲了解決這個問題微軟和網景提出了兩種幾乎徹底相反的概念。web

事件冒泡

微軟提出了名爲事件冒泡(event bubbling)的事件流。事件冒泡能夠形象地比喻爲把一顆石頭投入水中,泡泡會一直從水底冒出水面。也就是說,事件會從最內層的元素開始發生,一直向上傳播,直到document對象。瀏覽器

所以上面的例子在事件冒泡的概念下發生click事件的順序應該是bash

p -> div -> body -> html -> document

事件捕獲

網景提出另外一種事件流名爲事件捕獲(event capturing)。與事件冒泡相反,事件會從最外層開始發生,直到最具體的元素。dom

上面的例子在事件捕獲的概念下發生click事件的順序應該是函數

document -> html -> body -> div -> p

事件冒泡和事件捕獲過程圖:性能


1-5是捕獲過程,5-6是目標階段,6-10是冒泡階段;
flex

2. addEventListener 的第三個參數

DOM2級事件」中規定的事件流同時支持了事件捕獲階段和事件冒泡階段,而做爲開發者,咱們能夠選擇事件處理函數在哪個階段被調用。優化

addEventListener方法用來爲一個特定的元素綁定一個事件處理函數,是JavaScript中的經常使用方法。addEventListener有三個參數:

element.addEventListener(event, function, useCapture)複製代碼
參數 描述
event
必須。字符串,指定事件名。

注意: 不要使用 "on" 前綴。 例如,使用 "click" ,而不是使用 "onclick"。

提示: 全部 HTML DOM 事件,能夠查看咱們完整的 HTML DOM Event 對象參考手冊
function
必須。指定要事件觸發時執行的函數。

當事件對象會做爲第一個參數傳入函數。 事件對象的類型取決於特定的事件。例如, "click" 事件屬於 MouseEvent(鼠標事件) 對象。
useCapture
可選。布爾值,指定事件是否在捕獲或冒泡階段執行。

可能值:
  • true - 事件句柄在捕獲階段執行(即在事件捕獲階段調用處理函數)
  • false- false- 默認。事件句柄在冒泡階段執行(即表示在事件冒泡的階段調用事件處理函數)


3. 事件代理

在實際的開發當中,利用事件流的特性,咱們可使用一種叫作事件代理的方法。

<ul class="color_list">        
    <li>red</li>        
    <li>orange</li>        
    <li>yellow</li>        
    <li>green</li>        
    <li>blue</li>        
    <li>purple</li>    
</ul>
<div class="box"></div>複製代碼

.color_list{            
    display: flex;            
    display: -webkit-flex;        
}        
.color_list li{            
    width: 100px;            
    height: 100px;            
    list-style: none;            
    text-align: center;            
    line-height: 100px;        
}
//每一個li加上對應的顏色,此處省略
.box{            
    width: 600px;            
    height: 150px;            
    background-color: #cccccc; 
    line-height: 150px;            
    text-align: center;        
}
複製代碼

咱們想要在點擊每一個 li 標籤時,輸出li當中的顏色(innerHTML) 。常規作法是遍歷每一個 li ,而後在每一個 li 上綁定一個點擊事件:

var color_list=document.querySelector(".color_list");            
var colors=color_list.getElementsByTagName("li");            
var box=document.querySelector(".box");            
for(var n=0;n<colors.length;n++){                
    colors[n].addEventListener("click",function(){                    
        console.log(this.innerHTML)                    
        box.innerHTML="該顏色爲 "+this.innerHTML;                
    })            
}複製代碼

這種作法在 li 較少的時候可使用,但若是有一萬個 li ,那就會致使性能下降(少了遍歷全部 li 節點的操做,性能上確定更加優化)。

這時就須要事件代理出場了,利用事件流的特性,咱們只綁定一個事件處理函數也能夠完成:

function colorChange(e){                
    var e=e||window.event;//兼容性的處理         
    if(e.target.nodeName.toLowerCase()==="li"){                    
        box.innerHTML="該顏色爲 "+e.target.innerHTML;                
    }                            
}            
color_list.addEventListener("click",colorChange,false)複製代碼

因爲事件冒泡機制,點擊了 li 後會冒泡到 ul ,此時就會觸發綁定在 ul 上的點擊事件,再利用 target 找到事件實際發生的元素,就能夠達到預期的效果。

使用事件代理的好處不只在於將多個事件處理函數減爲一個,並且對於不一樣的元素能夠有不一樣的處理方法。假如上述列表元素當中添加了其餘的元素節點(如:a、span等),咱們沒必要再一次循環給每個元素綁定事件,直接修改事件代理的事件處理函數便可。

(1)toLowerCase() 方法用於把字符串轉換爲小寫。語法:stringObject.toLowerCase()

返回值:一個新的字符串,在其中 stringObject 的全部大寫字符所有被轉換爲了小寫字符。

(2)nodeName 屬性指定節點的節點名稱。若是節點是元素節點,則 nodeName 屬性返回標籤名。若是節點是屬性節點,則 nodeName 屬性返回屬性的名稱。對於其餘節點類型,nodeName 屬性返回不一樣節點類型的不一樣名稱。

全部主流瀏覽器均支持 nodeName 屬性。


冒泡仍是捕獲?

對於事件代理來講,在事件捕獲或者事件冒泡階段處理並無明顯的優劣之分,可是因爲事件冒泡的事件流模型被全部主流的瀏覽器兼容,從兼容性角度來講仍是建議你們使用事件冒泡模型。

IE瀏覽器兼容

IE瀏覽器對addEventListener兼容性並不算太好,只有IE9以上可使用。


要兼容舊版本的IE瀏覽器,可使用IE的attachEvent函數

object.attachEvent(event, function)

兩個參數與addEventListener類似,分別是事件和處理函數,默認是事件冒泡階段調用處理函數,要注意的是,寫事件名時候要加上"on"前綴("onload"、"onclick"等)。

阻止事件冒泡

1. 給子級加 event.stopPropagation( )

$("#div1").mousedown(function(e){
    var e=event||window.event;
    event.stopPropagation();
});複製代碼

2. 在事件處理函數中返回 false

$("#div1").mousedown(function(event){
    var e=e||window.event;
    return false;
});複製代碼

可是這兩種方式是有區別的。return false 不只阻止了事件往上冒泡,並且阻止了事件自己(默認事件)。event.stopPropagation()則只阻止事件往上冒泡,不阻止事件自己。

3.  event.target==event.currentTarget,讓觸發事件的元素等於綁定事件的元素,也能夠阻止事件冒泡;



阻止默認事件

(1)event.preventDefault( )

(2)return false

感謝您的閱讀,有不足之處請爲我指出!

相關文章
相關標籤/搜索