Web/04-JavaScript基础/40-事件的传播和事件冒泡.md
2022-01-02 17:38:44 +08:00

311 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 40-事件的传播和事件冒泡
publish: true
---
<ArticleTopAd></ArticleTopAd>
## DOM事件流
事件传播的三个阶段是:事件捕获、事件冒泡和目标。
- 事件捕获阶段事件从祖先元素往子元素查找DOM树结构直到捕获到事件目标 target。在这个过程中默认情况下事件相应的监听函数是不会被触发的。
- 事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
- 事件冒泡阶段:事件从事件目标 target 开始,从子元素往冒泡祖先元素冒泡,直到页面的最上一级标签。
如下图所示:
![](http://img.smyhvae.com/20180204_1218.jpg)
PS这个概念类似于 Android 里的 **touch 事件传递**
### 事件捕获
addEventListener可以捕获事件
```javascript
box1.addEventListener("click", function () {
alert("捕获 box3");
}, true);
```
上面的方法中参数为true代表事件在捕获阶段执行。
代码演示:
```javascript
//参数为true代表事件在「捕获」阶段触发参数为false或者不写参数代表事件在「冒泡」阶段触发
box3.addEventListener("click", function () {
alert("捕获 child");
}, true);
box2.addEventListener("click", function () {
alert("捕获 father");
}, true);
box1.addEventListener("click", function () {
alert("捕获 grandfather");
}, true);
document.addEventListener("click", function () {
alert("捕获 body");
}, true);
```
效果演示:
![](http://img.smyhvae.com/20180204_1101.gif)
(如果上面的图片打不开,请点击:<http://img.smyhvae.com/20180204_1101.gif>
**重点**捕获阶段事件依次传递的顺序是window --> document --> html--> body --> 父元素、子元素、目标元素。
这几个元素在事件捕获阶段的完整写法是:
```javascript
window.addEventListener("click", function () {
alert("捕获 window");
}, true);
document.addEventListener("click", function () {
alert("捕获 document");
}, true);
document.documentElement.addEventListener("click", function () {
alert("捕获 html");
}, true);
document.body.addEventListener("click", function () {
alert("捕获 body");
}, true);
fatherBox.addEventListener("click", function () {
alert("捕获 father");
}, true);
childBox.addEventListener("click", function () {
alert("捕获 child");
}, true);
```
说明:
1第一个接收到事件的对象是 **window**有人会说body有人会说html这都是错误的
2JS中涉及到DOM对象时有两个对象最常用window、doucument。它们俩是最先获取到事件的。
**补充一个知识点:**
在 js中
- 如果想获取 `html`节点,方法是`document.documentElement`。
- 如果想获取 `body` 节点,方法是:`document.body`。
二者不要混淆了。
### 事件冒泡
**事件冒泡**: 当一个元素上的事件被触发的时候(比如说鼠标点击了一个按钮),同样的事件将会在那个元素的所有**祖先元素**中被触发。这一过程被称为事件冒泡这个事件从原始元素开始一直冒泡到DOM树的最上层。
通俗来讲,冒泡指的是:**子元素的事件被触发时,父元素的同样的事件也会被触发**。取消冒泡就是取消这种机制。
代码演示:
```javascript
//事件冒泡
box3.onclick = function () {
alert("child");
}
box2.onclick = function () {
alert("father");
}
box1.onclick = function () {
alert("grandfather");
}
document.onclick = function () {
alert("body");
}
```
![](http://img.smyhvae.com/20180204_1028.gif)
(如果上面的图片打不开,请点击:<http://img.smyhvae.com/20180204_1028.gif>
上图显示,当我点击子元素 box3 的时候它的父元素box2、box1、body都依次被触发了。即使我改变代码的顺序也不会影响效果的顺序。
当然,上面的代码中,我们用 addEventListener 这种 DOM2的写法也是可以的但是第三个参数要写 false或者不写。
**冒泡顺序**
一般的浏览器: 除IE6.0之外的浏览器)
- div -> body -> html -> document -> window
IE6.0
- div -> body -> html -> document
### 不是所有的事件都能冒泡
以下事件不冒泡blur、focus、load、unload、onmouseenter、onmouseleave。意思是事件不会往父元素那里传递。
我们检查一个元素是否会冒泡,可以通过事件的以下参数:
```javascript
event.bubbles
```
如果返回值为true说明该事件会冒泡反之则相反。
举例:
```javascript
box1.onclick = function (event) {
alert("冒泡 child");
event = event || window.event;
console.log(event.bubbles); //打印结果true。说明 onclick 事件是可以冒泡的
}
```
## 阻止冒泡
大部分情况下,冒泡都是有益的。当然,如果你想阻止冒泡,也是可以的。可以按下面的方法阻止冒泡。
### 阻止冒泡的方法
w3c的方法火狐、谷歌、IE11
```javascript
event.stopPropagation();
```
IE10以下则是
```javascript
event.cancelBubble = true
```
兼容代码如下:
```javascript
box3.onclick = function (event) {
alert("child");
//阻止冒泡
event = event || window.event;
if (event && event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
```
上方代码中我们对box3进行了阻止冒泡产生的效果是事件不会继续传递到 father、grandfather、body了。
### 阻止冒泡的举例
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
<style type="text/css">
#box1 {
width: 100px;
height: 100px;
background-color: red;
/*
* 开启box1的绝对定位
*/
position: absolute;
}
</style>
<script type="text/javascript">
window.onload = function() {
/*
* 使div可以跟随鼠标移动
*/
//获取box1
var box1 = document.getElementById('box1');
//给整个页面绑定:鼠标移动事件
document.onmousemove = function(event) {
//兼容的方式获取event对象
event = event || window.event;
// 鼠标在页面的位置 = 滚动条滚动的距离 + 可视区域的坐标。
var pagex = event.pageX || scroll().left + event.clientX;
var pagey = event.pageY || scroll().top + event.clientY;
// 设置div的偏移量相对于整个页面
// 注意,如果想通过 style.left 来设置属性,一定要给 box1 开启绝对定位。
box1.style.left = pagex + 'px';
box1.style.top = pagey + 'px';
};
// 【重要注释】
// 当 document.onmousemove 和 box2.onmousemove 同时触发时,通过 box2 阻止事件向 document 冒泡。
// 也就是说,只要是在 box2 的区域,就只触发 document.onmousemove 事件
var box2 = document.getElementById('box2');
box2.onmousemove = function(event) {
//阻止冒泡
event = event || window.event;
if (event && event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
};
};
// scroll 函数封装
function scroll() {
return {
//此函数的返回值是对象
left: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
right: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft,
};
}
</script>
</head>
<body style="height: 1000px;width: 2000px;">
<div id="box2" style="width: 300px; height: 300px; background-color: #bfa;"></div>
<div id="box1"></div>
</body>
</html>
```
关键地方可以看代码中的注释。
效果演示:
![](http://img.smyhvae.com/20191112_1650.gif)
## 我的公众号
想学习<font color=#0000ff>**更多技能**</font>?不妨关注我的微信公众号:**千古壹号**id`qianguyihao`)。
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
![](http://img.smyhvae.com/20190101.png)