JS事件冒泡与事件捕获
如果给每个标签都定义事件,当我们点击其中的<body>
<div id="wrap">
<p class="hint">
<a href="#">Click Me</a>
</p>
</div>
</body>
<a> 标签时,会发现绑定在 <div> 和 <p> 标签上的事件也被触发了,这到底是为什么呢?为了解答这一问题,微软和网景两公司提出了两种不同的概念,事件捕获与事件冒泡:事件捕获:由微软公司提出,事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直到达事件的目标节点;
事件冒泡:由网景公司提出,与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动。
提示:上面提到的目标节点指的是触发事件的节点。
后来,W3C 为了统一标准,采用了一个折中的方式,即将事件捕获与事件冒泡并,也就是现在的“先捕获后冒泡”,如下图所示:
图:事件捕获与事件冒泡
事件捕获
在事件捕获阶段,事件会从 DOM 树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直到达事件的目标节点。以上图中的代码为例,如果单击其中的 <a> 标签,则该事件将通过 document -> div -> p -> a的顺序传递到 <a> 标签。示例代码如下:
运行上面的代码,单击最内层的<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<style type="text/css">
div, p, a {
padding: 15px 30px;
display: block;
border: 2px solid #000;
background: #fff;
}
</style>
</head>
<body>
<div id="wrap">DIV
<p class="hint">P
<a href="#">A</a>
</p>
</div>
<script>
function showTagName() {
alert("事件捕获: " + this.tagName);
}
var elems = document.querySelectorAll("div, p, a");
for (let elem of elems) {
elem.addEventListener("click", showTagName, true);
}
</script>
</body>
</html>
<a> 标签,运行结果如下图所示:
图:事件捕获演示
事件冒泡
事件冒泡正好与事件捕获相反,事件冒泡是从目标节点开始,沿父节点依次向上,并触发父节点上的事件,直文档根节点,就像水底的气泡一样,会一直向上。示例代码如下:
运行上面的代码,单击最内层的<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<style type="text/css">
div, p, a {
padding: 15px 30px;
display: block;
border: 2px solid #000;
background: #fff;
}
</style>
</head>
<body>
<div onclick="alert('事件冒泡: ' + this.tagName)">DIV
<p onclick="alert('事件冒泡: ' + this.tagName)">P
<a href="#" onclick="alert('事件冒泡: ' + this.tagName)">A</a>
</p>
</div>
</body>
</html>
<a> 标签,运行结果如下图所示:
图:事件冒泡演示
阻止事件捕获和冒泡
了解了事件捕获和事件冒泡后会发现,这个特性并不友好,例如我们在某个节点上绑定了事件,本想在点击时触发这个事件,结果由于事件冒泡,这个节点的事件被它的子元素给触发了。我们要如何阻止这样的事情发生呢?JavaScript 中提供了 stopPropagation() 方法来阻止事件捕获和事件冒泡的发生,语法格式如下:
event.stopPropagation();
注意:stopPropagation() 会阻止事件捕获和事件冒泡,但是无法阻止标签的默认行为,例如点击链接任然可以打开对应网页。
示例代码如下:此外,您也可以使用 stopImmediatePropagation() 方法来阻止同一节点的同一事件的其它事件处理程序,例如为某个节点定义了多个点击事件,当事件触发时,这些事件会按定义顺序依次执行,如果其中一个事件处理程序中使用了 stopImmediatePropagation() 方法,那么剩下的事件处理程序将不再执行。<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<style type="text/css">
div, p, a {
padding: 15px 30px;
display: block;
border: 2px solid #000;
background: #fff;
}
</style>
</head>
<body>
<div id="wrap">DIV
<p class="hint">P
<a href="#">A</a>
</p>
</div>
<script>
function showAlert(event) {
alert("您点击了 "+ this.tagName + " 标签");
event.stopPropagation();
}
var elems = document.querySelectorAll("div, p, a");
for(let elem of elems) {
elem.addEventListener("click", showAlert);
}
</script>
</body>
</html>
stopImmediatePropagation() 方法的语法格式如下:
event.stopImmediatePropagation();
示例代码如下:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<style type="text/css">
div, p, a {
padding: 15px 30px;
display: block;
border: 2px solid #000;
background: #fff;
}
</style>
</head>
<body>
<div onclick="alert('您点击了 ' + this.tagName + ' 标签')">DIV
<p onclick="alert('您点击了 ' + this.tagName + ' 标签')">P
<a href="#" id="link">A</a>
</p>
</div>
<script>
function sayHi() {
alert("事件处理程序 1");
event.stopImmediatePropagation();
}
function sayHello() {
alert("事件处理程序 2");
}
// 为 id 为 link 的标签定义多个点击事件
var link = document.getElementById("link");
link.addEventListener("click", sayHi);
link.addEventListener("click", sayHello);
</script>
</body>
</html>
阻止默认操作
某些事件具有与之关联的默认操作,例如当您单击某个链接时,会自动跳转到指定的页面,当您单击提交按钮时,会将数据提交到服务器等。如果不想这样的默认操作发生,可以使用 preventDefault() 方法来阻止,其语法格式如下:event.preventDefault();
示例代码如下:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<a href="http://c.biancheng网站站点" rel="nofollow" />
注意:IE9 及以下的版本不支持 preventDefault() 方法,对于 IE9 及以下的浏览器您可以使用 event.returnValue = false;。
- 随机文章
- 香港-马尔代夫(香港与马尔代夫加强关系,牵手未来)
- 了解马尔代夫历史(马尔代夫历史:从国家独立到旅游天堂)
- 上海到马尔代夫要(上海始发到马尔代夫的航线及预订攻略)
- 中国女星马尔代夫(中国女演员赴马尔代夫度假引网友热议)
- 渣女 马尔代夫(重写标题: 善变女子在马尔代夫的冒险经历)
- 国足马尔代夫爆冷(国足客场负马尔代夫错失晋级夺冠良机)
- 小马尔代夫厦门湾(厦门湾:像小马尔代夫一样的度假胜地)
- 北方 马尔代夫(马尔代夫北部地区旅游重启)
- 惠来有马尔代夫吗(惠来是否有前往马尔代夫的旅游线路?)
- 日本空袭马尔代夫(日本空袭马尔代夫岛,造成数十人死伤)
- 云南马尔代夫现状(云南一马尔代夫景点的发展及现状分析)
- 旅游书籍马尔代夫(美丽的马尔代夫:热带天堂的度假胜地)
- 生活继续马尔代夫(马尔代夫——宛如世外桃源的度假胜地)
- 想去马尔代夫骑行(马尔代夫自行车探险,你准备好了吗?)
- 沙特 马尔代夫(沙特与马尔代夫就合作项目达成协议)
- 电脑屏保马尔代夫(美丽马尔代夫,屏保带你领略海岛风光)
- 洛阳马尔代夫现状(洛阳市伊川县水库变成马尔代夫风景区)
- 杨幂 马尔代夫(杨幂游马尔代夫:放松身心享受浪漫假期)
- 邯郸马尔代夫入口(邯郸新增海外旅游目的地:马尔代夫!)
- 长岛马尔代夫风景(重现马尔代夫风情,探索长岛度假胜地)
- 鄂州马尔代夫旅游(鄂州市民疫情后再次踏上马尔代夫海滩)
- 英国 马尔代夫(英国游客能否重新启动马尔代夫旅游?)
- 马尔代夫中国债权(中国向马尔代夫借款成争议,舆论关注)
- 马尔代夫专业代理(马尔代夫旅游专家提供专业代订服务!)
- 父母 马尔代夫(家庭度假:畅游马尔代夫)
- 重庆巴南马尔代夫(重庆巴南有望成为马尔代夫“小巴南”)
- 郑州包机马尔代夫(直飞马尔代夫!郑州再次开通包机航班)
- 马尔代夫主题农庄(马尔代夫推出独特度假体验:主题农庄)
- 马尔代夫印度美国(马尔代夫联手印度美国,强化地区合作)
- 马尔代夫唯逸酒店(唯逸无与伦比!探索马尔代夫唯逸酒店)
