淺談事件冒泡與事件捕獲
1. 事件冒泡與事件捕獲
事件冒泡和事件捕獲分別由微軟和網景公司提出,這兩個概念都是為了解決頁面中 事件流 (事件發生順序)的問題。
<div id="outer"> <p id="inner">Click me!</p> </div>複製程式碼
上面的程式碼當中一個div元素當中有一個p子元素,如果兩個元素都有一個click的處理函式,那麼我們怎麼才能知道哪一個函式會首先被觸發呢?
為了解決這個問題微軟和網景提出了兩種幾乎完全相反的概念。
事件冒泡
微軟提出了名為 事件冒泡 (event bubbling)的事件流。事件冒泡可以形象地比喻為把一顆石頭投入水中,泡泡會一直從水底冒出水面。也就是說,事件會從最內層的元素開始發生,一直向上傳播,直到document物件。
因此上面的例子在事件冒泡的概念下發生click事件的順序應該是
p -> div -> body -> html -> document
事件捕獲
網景提出另一種事件流名為 事件捕獲 (event capturing)。與事件冒泡相反,事件會從最外層開始發生,直到最具體的元素。
上面的例子在事件捕獲的概念下發生click事件的順序應該是
document -> html -> body -> div -> p
事件冒泡和事件捕獲過程圖:
1-5是捕獲過程,5-6是目標階段,6-10是冒泡階段;
2. addEventListener 的第三個引數
DOM2級事件”中規定的事件流同時支援了事件捕獲階段和事件冒泡階段,而作為開發者,我們可以選擇事件處理函式在哪一個階段被呼叫。
addEventListener方法用來為一個特定的元素繫結一個事件處理函式,是JavaScript中的常用方法。addEventListener有三個引數:
element.addEventListener(event, function, useCapture)複製程式碼
引數 | 描述 |
---|---|
event |
必須。字串,指定事件名。 注意: 不要使用 "on" 字首。 例如,使用 "click" ,而不是使用 "onclick"。 提示: 所有 HTML DOM 事件,可以檢視我們完整的 HTML DOM Event 物件參考手冊 。 |
function |
必須。指定要事件觸發時執行的函式。 當事件物件會作為第一個引數傳入函式。 事件物件的型別取決於特定的事件。例如, "click" 事件屬於 MouseEvent(滑鼠事件) 物件。 |
useCapture |
可選。布林值,指定事件是否在捕獲或冒泡階段執行。 可能值:
|
3. 事件代理
在實際的開發當中,利用事件流的特性,我們可以使用一種叫做事件代理的方法。
<ul class="color_list"> <li>red</li> <li>orange</li> <li>yellow</li> <li>green</li> <li>blue</li> <li>purple</li> </ul>複製程式碼
.color_list{ display: flex; display: -webkit-flex; } .color_list li{ width: 100px; height: 100px; list-style: none; text-align: center; line-height: 100px; } //每個li加上對應的顏色, .box{ width: 600px; height: 150px; background-color: #cccccc; line-height: 150px; text-align: center; } 複製程式碼
我們想要在點選每個 li 標籤時,輸出li當中的顏色 (innerHTML)
。常規做法是遍歷每個 li ,然後在每個 li 上繫結一個點選事件:
var color_list=document.querySelector(".color_list"); var colors=color_list.getElementsByTagName("li"); var box=document.querySelector(".box"); for(var n=0;n<colors.length;n++){ colors[n].addEventListener("click",function(){ console.log(this.innerHTML) box.innerHTML="該顏色為 "+this.innerHTML; }) }複製程式碼
這種做法在 li 較少的時候可以使用,但如果有一萬個 li ,那就會導致效能降低(少了遍歷所有 li 節點的操作,效能上肯定更加優化)。
這時就需要事件代理出場了,利用事件流的特性,我們只繫結一個事件處理函式也可以完成:
function colorChange(e){ var e=e||window.event; if(e.target.nodeName.toLowerCase()==="li"){ box.innerHTML="該顏色為 "+x.innerHTML; } } color_list.addEventListener("click",colorChange,false)複製程式碼
由於事件冒泡機制,點選了 li 後會冒泡到 ul ,此時就會觸發繫結在 ul 上的點選事件,再利用 target 找到事件實際發生的元素,就可以達到預期的效果。
使用事件代理的好處不僅在於將多個事件處理函式減為一個,而且對於不同的元素可以有不同的處理方法。假如上述列表元素當中添加了其他的元素節點(如:a、span等),我們不必再一次迴圈給每一個元素繫結事件,直接修改事件代理的事件處理函式即可。
(1)toLowerCase() 方法用於把字串轉換為小寫。 語法: stringObject.toLowerCase()
返回值: 一個新的字串,在其中 stringObject 的所有大寫字元全部被轉換為了小寫字元。
(2)nodeName 屬性指定節點的節點名稱。如果節點是元素節點,則 nodeName 屬性返回標籤名。如果節點是屬性節點,則 nodeName 屬性返回屬性的名稱。對於其他節點型別,nodeName 屬性返回不同節點型別的不同名稱。
所有主流瀏覽器均支援 nodeName 屬性。
冒泡還是捕獲?
對於事件代理來說,在事件捕獲或者事件冒泡階段處理並沒有明顯的優劣之分,但是由於事件冒泡的事件流模型被所有主流的瀏覽器相容,從相容性角度來說還是建議大家使用事件冒泡模型。
IE瀏覽器相容
IE瀏覽器對addEventListener相容性並不算太好,只有IE9以上可以使用。
要相容舊版本的IE瀏覽器,可以使用IE的attachEvent函式
object.attachEvent(event, function)
兩個引數與addEventListener相似,分別是事件和處理函式,預設是事件冒泡階段呼叫處理函式,要注意的是,寫事件名時候要加上"on"字首("onload"、"onclick"等)。
阻止事件冒泡
1. 給子級加 event.stopPropagation( )
$("#div1").mousedown(function(e){ var e=event||window.event; event.stopPropagation(); });複製程式碼
2. 在事件處理函式中返回 false
$("#div1").mousedown(function(event){ var e=e||window.event; return false; });複製程式碼
但是這兩種方式是有區別的。 return false
不僅阻止了事件往上冒泡,而且阻止了事件本身(預設事件)。 event.stopPropagation()
則只阻止事件往上冒泡,不阻止事件本身。
阻止預設事件
(1)event.preventDefault( )
(2)return false
感謝您的閱讀,有不足之處請為我指出!