一塊:
事件流描述的是從頁面接收事件的順序。
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>
如上所示,這是一個十分簡單地文檔結構:document > html > body > div > h5
並且分別在obj1
,obj2
上綁定了一個點擊事件,由於addEventListener
的第三個參數爲false
,所以頁面是在冒泡階段處理綁定事件。此時整個頁面可以有三種行爲出現
- 點擊文字welcome時,彈出hello。
此時就只觸發了綁定在obj1上的點擊事件。具體冒泡實現過程如下:welcome 屬於文本節點,點擊後,開始從文本節點查找,當前文本節點沒有綁定點擊事件,繼續向上找,找到父級(id爲obj1的div),有綁定的點擊事件,執行,再向上找,body
,沒有綁定點擊事件,再到html
,document
,都沒再有綁定的點擊事件,好,整個冒泡過程結束。 - 點擊文字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);
<div class="person-messagebox">
<div class="left-message"><a href="https://blog.csdn.net/weixin_41646716">
<img src="https://profile.csdnimg.cn/5/7/E/3_weixin_41646716" class="avatar_pic" username="weixin_41646716">
</a></div>
<div class="middle-message">
<div class="title"><span class="tit "><a href="https://blog.csdn.net/weixin_41646716" data-report-click="{"mod":"popu_379","ab":"new"}" target="_blank">歪歪100</a></span>
<!-- 等級,level -->
<img class="identity-icon" src="https://csdnimg.cn/identity/blog6.png"> </div>
<div class="text"><span>原創文章 49</span><span>獲贊 158</span><span>訪問量 70萬+</span></div>
</div>
<div class="right-message">
<a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379","ab":"new"}">關注</a>
<a href="https://im.csdn.net/im/main.html?userName=weixin_41646716" target="_blank" class="btn btn-sm bt-button personal-letter">私信
</a>
</div>
</div>
</div>