本篇文章參考以下博文
https://www.jianshu.com/p/d3e9b653fa95
事件冒泡
我們先象徵性的舉個例子,幫助大家有一個具像的理解,下面有三個盒子,他們嵌套在一起。
<div id="num1" onClick={console.log(1)}>
<div id="num2" onClick='console.log(2)'>
<div id="num3" onClick='console.log(3)'></div>
</div>
</div>
當點擊黃色盒子的時候,控制檯輸出的結果爲3,2,1。
雖然點擊的是黃色盒子,但是瀏覽器不管你點的是誰,只關心你點的位置有幾個元素,點擊黃色區域,有三個元素,所以三個元素都被判定爲點擊,這個類似於你打了別人肚子一拳,雖然是打在他的衣服上面了,但是人家可不管那個,就判定你打他了。
事件冒泡的輸出順序比較好理解,點擊的誰,就從誰開始,一層一層往外擴散,直到擴散到document
,還是上面的例子,當你打別人的時候,力量是先傳遞到衣服上,然後再傳遞到被打人的肉上,一層一層傳遞。
事件捕獲
事件捕獲正好和冒泡相反,修改一下代碼
<div id="num1">
<div id="num2">
<div id="num3"></div>
</div>
</div>
<script>
let div1 = document.getElementById('num1');
div1.addEventListener('click', function () {
console.log(1)
}, true)
let div2 = document.getElementById('num2');
div2.addEventListener('click', function () {
console.log(2)
}, true)
let div3 = document.getElementById('num3');
div3.addEventListener('click', function () {
console.log(3)
}, true)
</script>
上面添加監聽事件的方式是爲了修改事件捕獲,當addEventListener
的第三個參數爲true
的時候,爲事件捕獲,默認爲false
,這次我們還是點擊黃色區域,輸出結果如下:
這次順序完全反了過來,可以理解爲,程序在執行過程中,要從上往下執行代碼,執行到最外層(粉色)div的時候,發現他有一個點擊事件,鼠標點擊的位置還正好在他的範圍之內,那就先把他執行了,別的以後再說。
這樣一層一層往裏執行,找到誰就執行誰,所以執行順序爲1,2,3。
事件委託
事件委託的機制源於冒泡,現在有一個<ul>
,裏面有1000個<li>
需要添加監聽事件,這個時候就不能一個一個的給<li>
添加監聽事件了,累死也加不完。
這時候就需要用到剛學習過的事件冒泡了,我們已經知道點擊一個<li>
的時候,事件會冒泡到<ul>
上,那直接給<ul>
添加上點擊事件就可以了,不管點到哪個<li>
上都能觸發。
當然了,我們還是要區分到底是點擊的哪個<li>
的,比如這個例子,<ul>
裏面有1000個<li>
,當我點擊其中一個<li>
的時候,希望它高亮顯示。我們可以用以下方法實現
let ul = document.getElementById('ul');
ul.addEventListener('click', function (e) {
let id = e.target.getAttribute('id') //獲取元素的id屬性
let li = document.getElementById(id); //這裏id沒有加引號,是在使用上面剛剛獲取到的id屬性
li.classList.add("className1") //添加class屬性,可以在className1中寫上高亮樣式
}, false)
阻止事件冒泡和捕獲
有時候我們不想冒泡,點誰就誰執行,別人別搗亂
let ul = document.getElementById('ul');
ul.addEventListener('click', function (e) {
...
e.stopPropagation() //加上這個就可以阻止冒泡了,同樣也適用於捕獲
})
上面提到了e.target
,這個東西能幫我們獲取到是誰觸發的點擊事件,但當使用一些第三方庫的組件的時候,e.target
可能無法獲取到我們想要的元素,這個時候需要使用e.currentTarget
,具體詳情請查看以下博文