事件传播是一种描述在Web浏览器中触发的事件“堆栈”的方法。
事件冒泡和捕获是事件传播的两种机制,它们描述了在一个元素上激活相同事件类型的两个处理程序时发生的情况。
假设您在<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 ”参数指定传播类型,下面语法项对useCapture进行了详细说明。
element.addEventListener(event, listener, useCapture)
“useCapture”缺省值为false,它将默认使用冒泡传播;而将值设置为true时,事件将使用捕获传播。
引入事件传播的概念是为了处理DOM层次结构中具有父子关系的多个元素具有针对同一事件的事件处理程序(例如,鼠标单击)的情况。现在的问题是,当用户单击内部元素时,将首先处理哪个元素的click事件,即外部元素或内部元素的click事件。
当在具有父元素的元素上触发事件时(例如本示例中的<p>),浏览器将运行两个不同的阶段-捕获阶段和冒泡阶段。
在捕获阶段:
浏览器将检查元素的最外层父级(<html>)是否在捕获阶段注册了onclick事件处理程序,如果是,则运行该事件处理程序。
然后,它移动到<html>中的下一个元素并执行相同的操作,然后执行下一个,依此类推,直到到达实际单击的元素。
在冒泡阶段,正好相反:
浏览器检查在冒泡阶段实际单击的元素是否在其上注册了onclick事件处理程序,如果是,则运行该事件处理程序。
然后,它移动到下一个直接父级元素,然后再执行下一个,依次类推,直到到达<html>元素为止。
在主流浏览器中,默认情况下,所有事件处理程序都在冒泡阶段注册。
在捕获阶段,事件从Window向下传播通过DOM树到达目标节点。
document.querySelector("div").addEventListener("click", myFunc, true); document.querySelector("p").addEventListener("click", myFunc, true); document.querySelector("a").addEventListener("click", myFunc, true);测试看看‹/›
仅当addEventListener()第三个参数设置为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()方法通知任何祖先元素的事件处理程序有关事件的信息,也可以在中间停止事件传播。
在以下示例中,如果单击子元素,则不会执行父元素上的click事件监听器:
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节点。
例如,如果用户单击超链接,则目标元素是超链接。
目标元素的访问方式为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); }测试看看‹/›
某些事件具有与之关联的默认操作。例如,如果您单击链接浏览器,则将您带到链接的目标,当您单击表单提交按钮时,浏览器将提交表单等等。您可以使用event.preventDefault()事件对象的方法来防止此类默认操作。
function myFunc() { event.preventDefault(); }测试看看‹/›
但是,阻止默认操作并不能阻止事件传播;事件继续照常传播到DOM树。
冒泡还使我们能够利用事件委托。
事件委托使您可以避免将事件监听器添加到特定节点;而是将事件监听器添加到一个父对象。
这个概念基于以下事实:如果您希望在单击大量子元素中的任何一个元素时运行某些代码,则可以在其父元素上设置事件监听器,并使发生在它们上面的事件冒泡到其父元素,不必为每个孩子单独设置事件监听器。
在这个示例中,如果你想在点击时弹出一条消息,你可以在父<ul>上设置click事件监听器,它会弹出列表项
<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>测试看看‹/›