在说到事件委托之前我觉得有必要对事件流进行一个回顾,了解事件的执行过程有助于加深对事件的理解。
事件流
如上图,事件被触发后有两个阶段:'捕获阶段'和'冒泡阶段'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
div {
width: 200px;
height: 200px;
background: orange;
}
div p {
width: 100px;
height: 100px;
background: pink;
}
</style>
</head>
<body>
<div>
<p>鍛煉</p>
</div>
<script type="text/javascript">
// 事件高级:
// 事件流:
const p = document.querySelector('p');
const div = document.querySelector('div');
p.addEventListener('click', function () {
console.log('p');
});
div.addEventListener('click', function () {
console.log('div');
});
document.addEventListener('click', function () {
console.log('document');
});
// 事件源.addEventListener('事件源', 事件处理函数, true/false)
</script>
</body>
</html>
执行上述代码后发现,当单击事件触发时,其祖先元素的单击事件也【相继触发】,这是为啥呢?
当某一个元素被触发了之后,会先从上往下找,是谁干的这事儿,找到之后再从下往上冒泡,往上汇报,是这个元素干的事儿。这个找的过程叫做捕获,往上汇报的过程就是冒泡。
结论
addEventListener第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发addEventListener第3个参数为true表示捕获阶段触发,false表示冒泡阶段触发,默认值为false- 事件流只会在父子元素具有相同事件类型时才会产生影响
- 绝大部分场景都采用默认的冒泡模式
阻止冒泡
阻止冒泡是为了防止事件的流动,保证事件只在当前元素执行,不再去影响上级元素。
<body>
<div>
<p>abc</p>
</div>
<script type="text/javascript">
const p = document.querySelector('p');
const div = document.querySelector('div');
p.addEventListener('click', function (e) {
console.log('p');
// 阻止冒泡:
// 事件对象.stopPropagation();
e.stopPropagation();
});
div.addEventListener('click', function () {
console.log('div')
});
</script>
</body>
结论
事件对象中的ev.stopPropagation方法,专门用来阻止事件冒泡。
- 鼠标经过事件
mouseover和mouseout会有冒泡效果mouseenter和mouseleave没有冒泡效果 (推荐)
事件委托
事件委托是利用事件流的特征解决一些现实开发需求的知识技巧,主要的作用是提升程序效率。大量的事件监听是比较耗费性能的,如下代码所示
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) {
// 为 10000 个 button 元素添加了事件
buttons.addEventListener('click', function () {
// 省略具体执行逻辑...
})
}
</script>
我们可以利用事件流的特征,对代码进行优化,给它的父元素监听点击事件,利用事件的冒泡会向上级元素冒泡这个特征进行优化。为了找到是哪个元素被触发的,需要用到事件对象e.target
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button')
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
const parents = document.querySelector('table')
parents.addEventListener('click', function (e) {
// console.log(e.target);
// 只有 button 元素才会真正去执行逻辑
if(e.target.tagName === 'BUTTON') {
// 执行的逻辑
}
})
</script>
其他事件
- 页面加载事件,事件名:
load
window.addEventListener('load', function() {
// xxxxx
})
- 元素滚动事件, 滚动条滚动触发
window.addEventListener('scroll', function() {
// xxxxx
})
- 页面尺寸事件
window.addEventListener('resize', function() {
// xxxxx
})
元素尺寸与位置
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth和offsetHeight获取出来的是数值,方便计算
注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0