- 說明:該文屬于 大前端全棧架構白寶書專欄,目前階段免費,如需要項目實戰或者是體系化資源,文末名片加V!
- 作者:不渴望力量的哈士奇(哈哥),十余年工作經驗, 從事過全棧研發、產品經理等工作,目前在公司擔任研發部門CTO。
- 榮譽:2022年度博客之星Top4、2023年度超級個體得主、谷歌與亞馬遜開發者大會特約speaker、全棧領域優質創作者。
- 🏆 白寶書系列
- 🏅 啟示錄 - 攻城獅的自我修養
- 🏅 Python全棧白寶書
- 🏅 ChatGPT實踐指南白寶書
- 🏅 產品思維訓練白寶書
- 🏅 全域運營實戰白寶書
- 🏅 大前端全棧架構白寶書

文章目錄
- ? 事件傳播
- ? addEventListener()方法。
? 事件傳播
先來思考一個問題:
當頁面的盒子有嵌套時,如果盒子都設置了同樣的事件監聽,那么事件監聽觸發的順序是什么樣的呢?比如下面圖中,三個盒子都設置了onclick事件監聽,當鼠標點擊最內側的盒子時,會觸發三個盒子的onclick事件,那這三個事件觸發的順序是怎樣的?
我們編寫代碼來驗證一下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#box1 {width: 142px;height: 142px;border: 1px solid #000;padding: 20px;}#box2 {width: 102px;height: 102px;border: 1px solid #000;padding: 20px;}#box3 {width: 100px;height: 100px;border: 1px solid #000;}</style>
</head>
<body><div id="box1"><div id="box2"><div id="box3"></div></div></div><script>var oBox1 = document.getElementById('box1');var oBox2 = document.getElementById('box2');var oBox3 = document.getElementById('box3');oBox1.onclick = function () {console.log('鼠標單擊box1');};oBox2.onclick = function () {console.log('鼠標單擊box2');};oBox3.onclick = function () {console.log('鼠標單擊box3');};</script>
</body>
</html>
這種從內到外觸發事件的過程就是事件傳播的過程。
但事實上,真實的事件傳播并不僅僅是由內到外的,而是先從外到內,然后再從內到外
,那為什么結果只有從內到外的呢?這是因為onxxx事件監聽只能監聽后面的一段過程,即從內到外的過程。
**事件的傳播:**DOM規定事件傳播的方向是先從外到內,再從內到外
從外到內的過程為
捕獲階段(capturing phase)
,從內到外的過程為冒泡階段(Bubbing phase)
那么問題來了,如何才能監聽到捕獲階段呢?這就用到第二種事件監聽方法——addEventListener()方法。
在DOM更新過程中,事件監聽分成兩個級別:DOM0級事件監聽和DOM2級事件監聽。一開始只有DOM0級事件監聽,只能監聽到冒泡階段;在DOM2中新增加了addEventListener()方法,可以監聽到捕獲和冒泡兩個階段。之所以沒有DOM1級,是因為DOM1中沒有對事件監聽的方法進行更新修改。
? addEventListener()方法。
addEventListener()方法的示例代碼:
oBox.addEventListener('click', function(){ //click表示鼠標單擊//事件處理函數代碼
}, true);//true表示監聽捕獲階段,false表示監聽冒泡階段
上面的例子中,用addEventListener()方法監聽整個事件傳播的過程,代碼如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#box1 {width: 142px;height: 142px;border: 1px solid #000;padding: 20px;}#box2 {width: 102px;height: 102px;border: 1px solid #000;padding: 20px;}#box3 {width: 100px;height: 100px;border: 1px solid #000;}</style>
</head>
<body><div id="box1"><div id="box2"><div id="box3"></div></div></div><script>var oBox1 = document.getElementById('box1');var oBox2 = document.getElementById('box2');var oBox3 = document.getElementById('box3');oBox1.addEventListener('click', function () {console.log('我是box1的捕獲階段');}, true);oBox2.addEventListener('click', function () {console.log('我是box2的捕獲階段');}, true);oBox3.addEventListener('click', function () {console.log('我是box3的捕獲階段');}, true);oBox1.addEventListener('click', function () {console.log('我是box1的冒泡階段');}, false);oBox2.addEventListener('click', function () {console.log('我是box2的冒泡階段');}, false);oBox3.addEventListener('click', function () {console.log('我是box3的冒泡階段');}, false);</script>
</body>
</html>
上述代碼中,需要注意的是,觸發事件監聽的順序跟代碼書寫的順序沒有關系的,新手可能會誤以為兩者有關系,我們把捕獲和冒泡的代碼順序調一下也不會影響事件傳播順序:
還需注意的是,如果給元素設置相同的兩個或多個同名事件時,DOM0級的事件監聽后面寫的會覆蓋先寫的;而DOM2級的會按順序執行。示例代碼:
<body><div id="box1"><div id="box2"><div id="box3"></div></div></div><script>var oBox1 = document.getElementById('box1');var oBox2 = document.getElementById('box2');var oBox3 = document.getElementById('box3');oBox2.onclick = function () { //這個事件監聽會被后面的覆蓋console.log('A');};oBox2.onclick = function () {console.log('B');};oBox2.addEventListener('click', function () { //不會被覆蓋console.log('C');},true)oBox2.addEventListener('click', function () { //不會被覆蓋console.log('D');},true)</script>
</body>