事件傳播是一種描述在Web瀏覽器中觸發(fā)的事件“堆?!钡姆椒?。
事件冒泡和捕獲是事件傳播的兩種機制,它們描述了在一個元素上激活相同事件類型的兩個處理程序時發(fā)生的情況。
假設您在<div >元素內有一個<p>元素,并且用戶單擊了<p>元素,應該首先處理哪個元素的“ click”事件?
<div id="div1">Capturing <p id="p1">Click me</p> </div> <script> document.querySelector("#div1").addEventListener("click", myFunc, true); document.querySelector("#p1").addEventListener("click", myFunc, true); </script>測試看看?/?
通過capture(捕獲),事件首先被最外面的元素捕獲并傳播到內部元素。
通過冒泡,事件首先由最內層元素捕獲和處理,然后傳播到外層元素。
使用該addEventListener()方法,您可以使用“ useCapture ”參數(shù)指定傳播類型,下面語法項對useCapture進行了詳細說明。
element.addEventListener(event, listener, useCapture)
“useCapture”缺省值為false,它將默認使用冒泡傳播;而將值設置為true時,事件將使用捕獲傳播。
引入事件傳播的概念是為了處理DOM層次結構中具有父子關系的多個元素具有針對同一事件的事件處理程序(例如,鼠標單擊)的情況。現(xiàn)在的問題是,當用戶單擊內部元素時,將首先處理哪個元素的click事件,即外部元素或內部元素的click事件。
當在具有父元素的元素上觸發(fā)事件時(例如本示例中的<p>),瀏覽器將運行兩個不同的階段-捕獲階段和冒泡階段。
在捕獲階段:
瀏覽器將檢查元素的最外層父級(<html>)是否在捕獲階段注冊了onclick事件處理程序,如果是,則運行該事件處理程序。
然后,它移動到<html>中的下一個元素并執(zhí)行相同的操作,然后執(zhí)行下一個,依此類推,直到到達實際單擊的元素。
在冒泡階段,正好相反:
瀏覽器檢查在冒泡階段實際單擊的元素是否在其上注冊了onclick事件處理程序,如果是,則運行該事件處理程序。
然后,它移動到下一個直接父級元素,然后再執(zhí)行下一個,依次類推,直到到達<html>元素為止。
在主流瀏覽器中,默認情況下,所有事件處理程序都在冒泡階段注冊。
在捕獲階段,事件從Window向下傳播通過DOM樹到達目標節(jié)點。
document.querySelector("div").addEventListener("click", myFunc, true); document.querySelector("p").addEventListener("click", myFunc, true); document.querySelector("a").addEventListener("click", myFunc, true);測試看看?/?
僅當addEventListener()第三個參數(shù)設置為true時,事件捕獲才與在時注冊的事件處理程序一起使用。
在起泡階段,恰好相反。在此階段,事件會傳播或冒泡,從目標元素到Window都將DOM樹向上傳播。
document.querySelector("div").addEventListener("click", myFunc); document.querySelector("p").addEventListener("click", myFunc); document.querySelector("a").addEventListener("click", myFunc);測試看看?/?
所有瀏覽器均支持事件冒泡,并且事件冒泡適用于所有處理程序,無論它們如何注冊(例如,使用onclick或addEventListener())。
如果要防止使用該event.stopPropagation()方法通知任何祖先元素的事件處理程序有關事件的信息,也可以在中間停止事件傳播。
在以下示例中,如果單擊子元素,則不會執(zhí)行父元素上的click事件監(jiān)聽器:
document.querySelector("div").addEventListener("click", myFunc); document.querySelector("p").addEventListener("click", myFunc); document.querySelector("a").addEventListener("click", myFunc); function myFunc() { alert("You clicked: "+ this.tagName); event.stopPropagation(); }測試看看?/?
目標元素是已生成事件的DOM節(jié)點。
例如,如果用戶單擊超鏈接,則目標元素是超鏈接。
目標元素的訪問方式為event.target,在事件傳播階段不會更改。
document.querySelector("div").addEventListener("click", myFunc); document.querySelector("p").addEventListener("click", myFunc); document.querySelector("a").addEventListener("click", myFunc); function myFunc() { alert("target = " + event.target.tagName); }測試看看?/?
某些事件具有與之關聯(lián)的默認操作。例如,如果您單擊鏈接瀏覽器,則將您帶到鏈接的目標,當您單擊表單提交按鈕時,瀏覽器將提交表單等等。您可以使用event.preventDefault()事件對象的方法來防止此類默認操作。
function myFunc() { event.preventDefault(); }測試看看?/?
但是,阻止默認操作并不能阻止事件傳播;事件繼續(xù)照常傳播到DOM樹。
冒泡還使我們能夠利用事件委托。
事件委托使您可以避免將事件監(jiān)聽器添加到特定節(jié)點;而是將事件監(jiān)聽器添加到一個父對象。
這個概念基于以下事實:如果您希望在單擊大量子元素中的任何一個元素時運行某些代碼,則可以在其父元素上設置事件監(jiān)聽器,并使發(fā)生在它們上面的事件冒泡到其父元素,不必為每個孩子單獨設置事件監(jiān)聽器。
在這個示例中,如果你想在點擊時彈出一條消息,你可以在父<ul>上設置click事件監(jiān)聽器,它會彈出列表項
<ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul> <script> document.getElementById("parent-list").addEventListener("click", function(event) { if(event.target && event.target.nodeName == "LI") { alert("List item " + event.target.id.replace("post-", "") + " was clicked!"); } }); </script>測試看看?/?