一、概述:
冒泡和捕获并不难理解,正如标题所言,他们指子元素和父元素之间的,两个方法的效果截然相反。
捕获指的是父元素默认向子元素传递事件(事件自上向下的传递);
冒泡指的是子元素默认向父元素传递事件(事件自下向上的传递);
二、详细解释:
冒泡
冒泡的原理是当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
举个例子:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>mphbh</title>
</head>
<body>
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form οnclick="alert('form')">FROM
<div οnclick="alert('div')">DIV
<p οnclick="alert('p')">P</p>
</div>
</form>
</body>
</html>
在上述代码运行的网页中点击内部的 <p>
会依次运行不同的 onclick事件
:
- 在该
<p>
上的。 - 然后是外部
<div>
上的。 - 然后是外部
<form>
上的。 - 以此类推,直到最后的
document
对象。
因此,如果我们点击 <p>
,那么我们将看到网页上会依次出现 3 个 alert:p → div
→ form,
这个过程称之为“冒泡”。
注意:
1.几乎所有事件都会冒泡,并不绝对,比如fouces事件不会冒泡等等
2.我们通常称引发事件那个嵌套层级最深的元素(即最底层元素)为目标元素,可以通过event.target 进行访问(如上例中的<p>)
停止冒泡
冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到 <html>
,然后再到 document
对象,有些事件甚至会到达 window
,它们会调用路径上所有的处理程序。
但是任意处理程序都可以决定事件已经被完全处理,并停止冒泡。
用于停止冒泡的方法是 event.stopPropagation()
。
举个简单的例子:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body οnclick="alert(event will spread there)">
<button οnclick="event.stopPropagation">click me</button>
</body>
</html>
这里<body>中的onclick事件不会工作,这就实现了冒泡的阻断
注意:
1.如果一个元素在一个事件上有多个处理程序,即使其中一个停止冒泡,其他处理程序仍会执行。
换句话说:event.stopPropagation()
停止向上移动,但是当前元素上的其他处理程序都会继续运行。
2.event.stopImmediatePropagation()
方法,不仅可以用于停止冒泡,也阻止了当前元素上的处理程序运行。使用该方法之后,其他处理程序就不会被执行。
3.不要在没有需要的情况下停止冒泡!
捕获
捕获的原理是当我们点击目标元素时,会先从最外层元素的事件逐步向下逐步运行,直到进入到目标元素为止。
DOM事件标准描述了事件传播的 3 个阶段:
- 捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。
- 目标阶段(Target phase)—— 事件到达目标元素。
- 冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。
如图所示:
捕获的默认状态是关闭的,在运行程序时,捕获阶段很少被使用。所有通常我们看不到它。
使用 on<event>
属性或使用 HTML 特性(attribute)或使用两个参数的 addEventListener(event, handler)
添加的处理程序,对捕获一无所知,它们仅在第二阶段和第三阶段运行。
为了在捕获阶段捕获事件,我们需要将处理程序的 capture
选项设置为 true:
elem.addEventListener(...,{capture:true})// 或者,用 {capture: true} 的别名 "true"
elem.addEventListener(...,true)
capture
选项有两个可能的值:
- 如果为
false
(默认值),则在冒泡阶段设置处理程序。 - 如果为
true
,则在捕获阶段设置处理程序。
如上述冒泡例子(我们略作修改:激活冒泡):
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>mphbh</title>
</head>
<body>
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form >FROM
<div >DIV
<p >P</p>
</div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`));
}
</script>
</body>
</html>
激活捕获之后,如果你继续点击<p>,那么事件发生的顺序是:
HTML
→BODY
→FORM
→DIV
(捕获阶段,第一个监听器)P
(目标阶段,触发两次,因为我们设置了两个监听器:捕获和冒泡)DIV
→FORM
→BODY
→HTML
(冒泡阶段,第二个监听器)
注意:
1.激活捕获后,要移除处理程序,addEventListener 和removeEventListener需要一一对应,即
addEventListener(. . . , true)和 removeEventListener(. . . , true)要一一对应
2.同一元素的同一阶段的监听器按其设置顺序运行