[TOC]
事件流指的是事件完整执行过程中的流动路径
假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
简单来说:捕获阶段是从父到子依次触发事件; 冒泡阶段是从子到父元素依次触发事件
演示代码:
<style>
.outer {
width: 200px;
height: 200px;
background-color: cadetblue;
}
.inner {
width: 100px;
height: 100px;
background-color: chartreuse;
}
</style>
<body>
<div class="outer">
<div class="inner"></div>
</div>
<script>
let outer = document.querySelector('.outer')
let inner = document.querySelector('.inner')
document.addEventListener('click', function () {
console.log('document')
})
outer.addEventListener('click', function () {
console.log('外层div')
})
inner.addEventListener('click', function () {
console.log('内层div~~~~~~')
})
</script>
</body>
事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
简单理解: 当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
事件冒泡是默认存在的
代码参见上面的示例,因为默认 事件就是在冒泡阶段触发的
事件捕获
从DOM的根元素开始去执行对应的事件 (从外到里)
事件捕获需要写对应代码才能看到效果
元素.addEventListener(事件类型, 处理函数, 是否使用捕获机制)
// 元素.addEventListener('事件名称', '处理函数', false) // 默认第三个参数就是false,表示事件在冒泡阶段触发
// 元素.addEventListener('事件名称', '处理函数', true) // 如果第三个参数为true,表示事件在捕获阶段触发
document.addEventListener('click', function () {
console.log('document')
}, true)
outer.addEventListener('click', function () {
console.log('外层div')
}, true)
inner.addEventListener('click', function () {
console.log('内层div~~~~~~')
}, true)
小结
- DOM 2级事件,可以通过传入第三个参数true,来将函数绑定在捕获阶段或者冒泡阶段执行。
- DOM 2级事件若传入false代表冒泡阶段触发,默认就是false
使用 on 的事件都是 DOM 0 级事件,事件绑定函数的执行,都是在冒泡阶段,没有捕获阶段,只要绑定了事件函数,就会按照冒泡顺序执行,单击事件会比双击事件先触发执行。
阻止事件流动
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
若想把事件就限制在当前元素内,就需要阻止事件流动
阻止事件流动需要拿到事件对象
语法:e.stopPropagation()
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效<body> <div class="outer"> <div class="inner"></div> </div> <script> let outer = document.querySelector('.outer') let inner = document.querySelector('.inner') // 元素.addEventListener('事件名称', '处理函数', false) // 默认第三个参数就是false,表示事件在冒泡阶段触发 // 元素.addEventListener('事件名称', '处理函数', true) // 如果第三个参数为true,表示事件在捕获阶段触发 document.addEventListener('click', function (e) { console.log('document') e.stopPropagation() }, true) outer.addEventListener('click', function () { console.log('外层div') }, true) inner.addEventListener('click', function (e) { console.log('内层div~~~~~~') // 当触发了这个事件后,阻止事件继续流动 e.stopPropagation() // 注意加小括号 }, true) </script> </body>为什么人们总喜欢说阻止事件冒泡,而很少说阻止事件的流动? 原因是默认事件是在冒泡阶段触发的,二是因为很少让事件在捕获阶段触发。
对比两组鼠标事件
有两组和事件冒泡相关的鼠标事件如下:
鼠标移入和离开事件:
- mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果(推荐) ```javascript
<a name="yt4cR"></a>
## 阻止标签的默认行为
标签也有默认行为,比如 `<a href="">跳转</a>` ,点击之后,默认就会跳转到指定的页面,这个跳转就是 a 标签的默认行为。<br />还有 form ,当我们点击提交按钮的时候,默认就会将表单数据提交到指定的页面。<br />很多场景,我们并不希望这些默认行为发生,则可以使用 `e.preventDefault()` 来阻止标签的默认行为。
```javascript
<a id="tobaidu" href="https://www.baidu.com">百度一下</a>
<hr>
<!-- href="" 超链接也会跳转,只不过跳转到了当前页面 -->
<!-- <a href="javascript:这里可以写一段js代码">你才我跳转到了哪里</a> -->
<!-- <a href="javascript:">你才我跳转到了哪里</a> -->
<a href="javascript:;">你才我跳转到了哪里</a>
<hr>
<hr>
<!-- form也有默认行为,就是提交数据这种行为 -->
<!-- 只要点击提交按钮,就会提交数据 -->
<!-- 默认会将我们填写的表单数据提交到当前页面 -->
<!-- 如果希望将数据提交到其他页面,可以设置form的action属性 -->
<form action="https://www.baidu.com">
<input type="text" name="username"><br />
<input type="password" name="pwd"><br />
<button>登录</button>
</form>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click', function (e) {
e.preventDefault() // 阻止表单的默认提交行为
console.log(123123123)
})
// a 标签,也可以这样阻止默认行为。
// a 标签还有其他阻止默认行为的方案
document.querySelector('#tobaidu').addEventListener('click', function (e) {
e.preventDefault()
})
</script>
DOM 0级和DOM 2级注册事件的区别
- 传统on注册(L0)
- 语法:
元素.onclick = function () {} - 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖就可以实现事件的解绑
- 都是冒泡阶段执行的
- 语法:
事件监听注册(L2)
- 语法:
元素.addEventListener(事件类型, 事件处理函数, 是否使用捕获) - 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用
元素.removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段),匿名函数无法被解绑,所以要移除事件的话,不能使用匿名函数 ```javascript
```
- 语法:
