让我们从一个示例开始。
处理程序(handler)被分配给了
),该处理程序也会运行:
<div onclick="alert('The handler!')">
<em>If you click on <code>EM</code>, the handler on <code>DIV</code> runs.</em>
</div>
这是不是有点奇怪?如果实际上点击的是 ,为什么在
上的处理程序会运行?
冒泡
冒泡(bubbling)原理很简单。
当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
假设我们有 3 层嵌套 FORM > DIV > P,它们各自拥有一个处理程序:
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
点击内部的
会首先运行 onclick:
- 在该
上的。
- 然后是外部 上的。
- 然后是外部
- 以此类推,直到最后的 document 对象。
因此,如果我们点击
,那么我们将看到 3 个 alert:p → div → form。
这个过程被称为“冒泡(bubbling)”,因为事件从内部元素“冒泡”到所有父级,就像在水里的气泡一样。
几乎 所有事件都会冒泡。
这句话中的关键词是“几乎”。
例如,focus 事件不会冒泡。同样,我们以后还会遇到其他例子。但这仍然是例外,而不是规则,大多数事件的确都是冒泡的。
event.target
父元素上的处理程序始终可以获取事件实际发生位置的详细信息。
引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过 event.target 访问。
注意与 this(=event.currentTarget)之间的区别:
- event.target —— 是引发事件的“目标”元素,它在冒泡过程中不会发生变化。
- this —— 是“当前”元素,其中有一个当前正在运行的处理程序。
例如,如果我们有一个处理程序 form.onclick,那么它可以“捕获”表单内的所有点击。无论点击发生在哪里,它都会冒泡到 并运行处理程序。
在 form.onclick 处理程序中:
- this(=event.currentTarget)是 元素,因为处理程序在它上面运行。
- event.target 是表单中实际被点击的元素。
一探究竟:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="example.css">
</head>
<body>
A click shows both <code>event.target</code> and <code>this</code> to compare:
<form id="form">FORM
<div>DIV
<p>P</p>
</div>
</form>
<script src="script.js"></script>
</body>
</html>
form {
background-color: green;
position: relative;
width: 150px;
height: 150px;
text-align: center;
cursor: pointer;
}
div {
background-color: blue;
position: absolute;
top: 25px;
left: 25px;
width: 100px;
height: 100px;
}
p {
background-color: red;
position: absolute;
top: 25px;
left: 25px;
width: 50px;
height: 50px;
line-height: 50px;
margin: 0;
}
body {
line-height: 25px;
font-size: 16px;
}
form.onclick = function(event) {
event.target.style.backgroundColor = 'yellow';
// chrome needs some time to paint yellow
setTimeout(() => {
alert("target = " + event.target.tagName + ", this=" + this.tagName);
event.target.style.backgroundColor = ''
}, 0);
};
点击之后
event.target 可能会等于 this —— 当点击事件发生在 元素上时,就会发生这种情况。
停止冒泡
冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到 ,然后再到 document 对象,有些事件甚至会到达 window,它们会调用路径上所有的处理程序。
但是任意处理程序都可以决定事件已经被完全处理,并停止冒泡。
用于停止冒泡的方法是 event.stopPropagation()。
例如,如果你点击