1. 程式人生 > 實用技巧 >js 事件冒泡和事件捕獲

js 事件冒泡和事件捕獲

轉載:https://blog.csdn.net/weixin_41646716/article/details/81099880

一塊:

事件流描述的是從頁面接收事件的順序。
IE的事件是冒泡事件流,
而firefox的事件流是捕獲事件流。
1.事件冒泡
IE的事件流叫做事件冒泡,即事件從最具體的元素到不具體的元素。
好比氣泡從水底下一直向上冒泡,像dom樹一樣,一直到根元素。
2.事件捕獲
即從不具體的元素到具體的元素

事件傳遞有兩種方式:冒泡與捕獲。

事件傳遞定義了元素事件觸發的順序。 如果你將 <p> 元素插入到 <div> 元素中,使用者點選 <p> 元素, 哪個元素的 "click" 事件先被觸發呢?

冒泡中,內部元素的事件會先被觸發,然後再觸發外部元素,即: <p> 元素的點選事件先觸發,然後會觸發 <div> 元素的點選事件。

捕獲中,外部元素的事件會先被觸發,然後才會觸發內部元素的事件,即: <div> 元素的點選事件先觸發 ,然後再觸發 <p> 元素的點選事件。

第一種:事件冒泡

   IE提出的事件流叫做事件冒泡,即事件開始時由最具體的元素接收,然後逐級向上傳播到較為不具體的節點,看一下以下示例:

接下來我們點選一下頁面上的p元素,看看會發生什麼:

正如上面我們所說的,它會從一個最具體的的元素接收,然後逐級向上傳播, p=>button=>div=>body..........事件冒泡可以形象地比喻為把一顆石頭投入水中,泡泡會一直從水底冒出水面。

第二種:事件捕獲

網景公司提出的事件流叫事件捕獲流。

事件捕獲流的思想是不太具體的DOM節點應該更早接收到事件,而最具體的節點應該最後接收到事件,針對上面同樣的例子,點選按鈕,那麼此時click事件會按照這樣傳播:(下面我們就借用addEventListener的第三個引數來模擬事件捕獲流)

同樣我們看一下後臺的列印結果:

react有專屬的阻止事件冒泡方法,e.nativeEvent.stopImmediatePropagation()

handleBarDisplay(e){

e.nativeEvent.stopImmediatePropagation();

this.setState({barDisplay:false})

}

二塊:

事件捕獲和事件冒泡屬於兩個相反的過程,這裡可以有一個我感覺十分恰當的比喻,當你把一個可以漂浮在水面上的物品,使勁向水裡砸下去,它會首先有一個下降的過程,這個過程就可以理解為從最頂層向事件發生的最具體元素(目標點)的捕獲過程;之後由於浮力大於物體自身的重力,物體會在到達最低點( 最具體元素)之後漂浮到水面上,這個過程相對於事件捕獲是一個回溯的過程,即事件冒泡。
好了,對於事件捕獲和事件冒泡有了一個概念上的理解,那我們就可以開始考慮實際的編碼過程中的實際應用了。先貼上本文所需要的程式碼

 
<!DOCTYPE html>
 
<html>
 
<head>
 
<title>event</title>
 
</head>
 
<body>
 
<div id="obj1">
 
welcome
 
<h5 id="obj2">hello</h5>
 
<h5 id="obj3">world</h5>
 
</div>
 
<script type="text/javascript">
 
var obj1=document.getElementById('obj1');
 
var obj2=document.getElementById('obj2');
 
obj1.addEventListener('click',function(){
 
alert('hello');
 
},false);
 
obj2.addEventListener('click',function(){
 
alert('world');
 
})
 
</script>
 
</body>
 
</html>
View Code 如上所示,這是一個十分簡單地文件結構:document > html > body > div > h5

並且分別在obj1,obj2上綁定了一個點選事件,由於addEventListener的第三個引數為false,所以頁面是在冒泡階段處理繫結事件。此時整個頁面可以有三種行為出現

  1. 點選文字welcome時,彈出hello。
    此時就只觸發了繫結在obj1上的點選事件。具體冒泡實現過程如下:welcome 屬於文字節點,點選後,開始從文字節點查詢,當前文字節點沒有繫結點選事件,繼續向上找,找到父級(id為obj1的div),有繫結的點選事件,執行,再向上找,body,沒有繫結點選事件,再到html,document,都沒再有繫結的點選事件,好,整個冒泡過程結束。
  2. 點選文字hello時,先彈出world,再彈出hello。
    具體冒泡的過程如下圖所示


3. 點選world時,彈出hello。
具體冒泡過程和第二種情況類似,如下圖

理解了以上的內容,我們可以接著來討論事件代理機制。
比如上面的程式碼,我們想要在點選每個h5標籤時,彈出對應的innerHTML。常規做法是遍歷每個h5,然後在每個h5上繫結一個點選事件,這種做法在h5較少的時候可以使用,但如果有一萬個h5,那就會導致效能降低。這時就需要事件代理出場了。
先貼程式碼

 
obj1.addEventListener('click',function(e){
 
var e=e||window.event;
 
if(e.target.nodeName.toLowerCase()=='h5'){
 
alert(e.target.innerHTML);
 
}
 
 
 
},false);

由於事件冒泡機制,點選了h5後會冒泡到div,此時就會觸發繫結在div上的點選事件,再利用target找到事件實際發生的元素,就可以達到預期的效果。

三塊:

採用事件代理,為頁面中的所有a標籤繫結click事件。

document.addEventListener("click", function(e) {

if (e.target.nodeName == "A")

console.log("a");

}, false);

問題:若a標籤裡面仍有span、img等其他元素,上述程式碼中,單擊span、img等其他元素不能觸發click事件。

原因:單擊span、img等其他元素時,e.target指向的是觸發click事件的元素(span、img等其他元素),而不是a標籤。

解決方法:從觸發click事件的元素開始,逐級向上查詢,直到找到a標籤為止。

document.addEventListener("click", function(e) {

var node = e.target;

while (node.parentNode.nodeName != "BODY") {

if (node.nodeName == "A") {

console.log("a");

break;

}

node = node.parentNode;

}

}, false);