?在瀏覽器中,事件的傳播方式分為:事件冒泡和事件捕獲蓉冈。那么事件冒泡和事件捕獲分別是什么城舞?為什么會出現(xiàn)兩種傳播方式呢轩触?
一、前提
- 在瀏覽器中用戶點擊鼠標家夺,操作系統(tǒng)最先知道脱柱,瀏覽器次之。
- 頁面中子元素被點擊了拉馋,意味著父元素也被點擊榨为。
- 如果子元素和父元素同時監(jiān)聽了點擊事件,那么點擊子元素后煌茴,是子元素先知道随闺,還是父元素先知道。而這個知道的前后順序景馁,就是由事件傳播方式?jīng)Q定的板壮。
二逗鸣、冒泡模式和捕獲模式
?事件的傳播途徑是通過文檔節(jié)點組成的有序列表合住。在DOM里面,這個事件傳播路徑的最后一點就是事件對象(Event Target)本身撒璧,其之前的元素就是事件對象的祖先元素透葛。
- 觸發(fā)事件后:
- 對于冒泡模式,事件首先被事件對象本身知道卿樱,然后讓其父元素知道僚害,就這樣在祖先元素中一層層向外傳播,直至window 對象繁调,如果中間有節(jié)點設(shè)置監(jiān)聽這個事件萨蚕,就調(diào)用其監(jiān)聽函數(shù)。
-
對于捕獲模式蹄胰,事件首先被window 對象知道岳遥,然后到document 節(jié)點,最后才被事件對象本身知道裕寨,如果中間有節(jié)點監(jiān)聽此事件浩蓉,就調(diào)用監(jiān)聽函數(shù)。
例子:
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta charset="utf-8">
<title>example</title>
</head>
<body>
<ul>
<li>item1</li>
<li>item2</li>
<li class="clicked">item3</li>
</ul>
</body>
</html>
- 在上面例子的結(jié)構(gòu)中宾袜,假設(shè)我在
item3
用鼠標點擊了一下捻艳。- 在捕獲模式中,事件是按以下順序傳播的:
window
-->document
--><html>
--><body>
--><ul>
--><li class="clicked">
,如果有節(jié)點監(jiān)聽點擊事件庆猫,那么就在傳播到那個節(jié)點的時候調(diào)用事件函數(shù)认轨。 - 在冒泡模式中,事件的傳播順序是同捕獲模式相反的:
<li class="clicked">
--><ul>
--><body>
--><html>
-->document
-->window
,如果有節(jié)點監(jiān)聽點擊事件月培,那么就在傳播到那個節(jié)點的時候調(diào)用事件函數(shù)嘁字。
- 在捕獲模式中,事件是按以下順序傳播的:
三昨稼、設(shè)置冒泡模式或捕獲模式
?在十幾年前,那時候瀏覽器各自為政拳锚,其中一個巨頭網(wǎng)景公司主張捕獲模式假栓,所以那時它的瀏覽器只支持捕獲模式;而另一巨頭微軟公司支持冒泡模式霍掺,所以IE 9 以前的版本只支持冒泡模式匾荆。在2000 年的時候,w3c 將兩種模式都寫入標準里面杆烁,所以現(xiàn)在大部分瀏覽器都支持兩種模式牙丽。
?我們可以使用addEventListener(type, listener, useCapture)
方法來設(shè)置事件的傳播模式,如果想要捕獲模式兔魂,就將第三個參數(shù)useCapture
設(shè)為true
烤芦。如果想要冒泡模式,可以將第三個參數(shù)設(shè)為false
或者直接省略第三個參數(shù)析校,因為不傳入?yún)?shù)的時候构罗,參數(shù)值為undefined
。
? 例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<section id="alog"></section>
<script>
var divs = document.getElementsByTagName('div');
function capture() {
log('capture: ' + this.firstChild.nodeValue.trim())
}
function bubble() {
log('bubble: ' + this.firstChild.nodeValue.trim())
}
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
function log(msg) {
var p = document.createElement('p');
p.textContent = msg;
alog.appendChild(p);
}
</script>
</body>
</html>
下圖是我點擊div 5
的結(jié)果
?
?在兩種模式混用的情況下智玻,如果是事件對象(Event Target)的父元素遂唧,按先捕獲后冒泡的順序進行。如果是事件對像自身吊奢,那么誰先監(jiān)聽就先執(zhí)行誰盖彭。在實際工作中不推薦混用兩種模式。
?例子:對上面代碼進行小小的改動
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<section id="alog"></section>
<script>
var divs = document.getElementsByTagName('div');
function capture() {
log('capture: ' + this.firstChild.nodeValue.trim())
}
function bubble() {
log('bubble: ' + this.firstChild.nodeValue.trim())
}
for (var i = 0; i < divs.length; i++) {
/* 只是調(diào)換了下面兩句語句的位置*/
divs[i].addEventListener('click', bubble, false);
divs[i].addEventListener('click', capture, true);
// 其他語句不變
}
function log(msg) {
var p = document.createElement('p');
p.textContent = msg;
alog.appendChild(p);
}
</script>
</body>
</html>
下圖是我點擊div 5
的結(jié)果
鏈接
本文部分借鑒自: https://stackoverflow.com/a/4616720/7309659
部分來自饑人谷老師方方的觀點: http://www.reibang.com/users/b4fd9acca45c/timeline
饑人谷鏈接:https://jirengu.com/