事件流
事件流通俗来讲就是事件执行的过程. 任意事件被触发时都会有冒泡阶段和捕获阶段. 捕获阶段就是从父类一直执行到子类, 冒泡阶段就是从子类也就是当前元素执行到父类.
事件捕获
概念: 从DOM的根元素去执行对应的事件(从父到子)
语法:
DOM.addEventListener(事件类型,时间处理函数,是否使用捕获机制)
注意: addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)若传入false, 则代表冒泡阶段 默认就是false 下面代码点击阮秀 会先从平安开始依次打印去寻找,直到打印出阮秀为止.
特别的: L0事件监听中只有冒泡 没有捕获
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
// 平安 宁姚 阮秀 目标(阮秀) 捕获阶段
document.addEventListener('click', function () {
alert('平安')
}, true)
fa.addEventListener('click', function () {
alert('宁姚')
}, true)
son.addEventListener('click', function () {
alert('阮秀')
}, true)
</script>
</body>
事件冒泡
当某个元素事件被触发时,相同事件会在这个元素的所有祖父元素中依次被触发,代码如下: 点击阮秀 宁姚和平安会依次被触发.
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
// 阮秀 宁姚 平安 冒泡阶段
document.addEventListener('click', function () {
alert('我是平安')
})
fa.addEventListener('click', function () {
alert('我是宁姚')
})
son.addEventListener('click', function () {
alert('我是阮秀')
})
</script>
</body>
事件冒泡是默认存在的,特别的L2事件监听第三个参数是false, 即冒泡.
组织冒泡
因为默认就有冒泡模式的存在 很容易导致某些事件影响到父级元素,所以在某些情况下,要组织冒泡.
语法:事件对象.stopPropagation()
注意: 这个方法在冒泡阶段和捕获阶段都能阻断事件流动传播.
某些情况下我们要阻止一些默认行为的发生,比如链接的挑战 表单域跳转等 采用下面语法:
语法:e.preventDefault()
解绑事件
L0事件解绑方式
btn.onclick = function () {
alert('点击了')
// L0 事件移除解绑
btn.onclick = null
}
L2事件解绑方式
<body>
<button>点击</button>
<script>
const btn = document.querySelector('button')
function fn() {
alert('点击了')
}
btn.addEventListener('click', fn)
// L2 事件移除解绑
btn.removeEventListener('click', fn)
</script>
</body>
鼠标经过事件的区别
mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果 (推荐)
两种注册方式的区别:
传统on注册(L0)
-
同一个对象,后面注册的事件会覆盖前面注册的(同一个事件)
-
直接使用null覆盖可以实现事件的解绑
-
都是冒泡阶段执行的
事件监听注册(L2)
语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
后面注册的事件不会覆盖前面注册的事件(同一个事件)
可以通过第三个参数去确定是在冒泡或者捕获阶段执行
必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
注意: 匿名函数无法被解绑
事件委托
优点: 减少注册次数, 提高程序性能
原理: 事件冒泡 即给父元素注册事件,当触发子元素时,会冒泡到父元素身上.
实现:事件对象.target.tagName 获取真正触发事件的元素
其他事件扩展
页面加载事件1:
外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件.
用处:
-
有时候需要等页面资源全部处理完了再做一些事情
-
老代码喜欢把script写在head中,这个时候找dom元素再找不到
-
时间名: load
监听页面所有资源加载完毕: 给 window 添加 load 事件
window.addEventListener('load', function() {})
注意: 不光可以监听整个页面资源加载完毕, 也可以针对某个资源绑定load事件
页面加载事件2:
-
当初始的HTML文档被完全加载和解析完成后, DOMContentLoaded事件被触发, 无需等待样式表、图像等完全加载
-
事件名: DOMContentLoaded
监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件
document.addEventListener('DOMContentLoaded', function() {})
页面滚动事件
滚动条在滚动的时候持续触发的事件
应用场景: 某些网页需要监测用户把页面滚动到某一区域后做一些处理, 比如固定导航栏, 返回顶部等.
事件名: scroll
监听整个页面滚动
window.addEventListener('scroll', function () {})
页面滚动事件之获取位置:
scrollLeft和scrollTop(属性):
-
获取被卷去的大小
-
获取元素内容往左、往上滚出去看不到的距离
-
这两个值是可读写的 即可以设置值 也可以读取值
检测页面滚出去的距离用
document.documentElement.scrollTop
页面滚动事件之滚动到指定坐标:
scrollTo() 方法可把内容滚动到指定的坐标
语法:元素.scrollTo(x, y)
页面尺寸事件
会在窗口尺寸改变的时候触发事件:
事件名: resize
window.addEventListener('resize', function () {})
可用于检测屏幕宽度:
<script type="text/javascript">
// resize:
window.addEventListener('resize', function () {
// console.log('尺寸改变了......');
// 获取页面
html = document.documentElement.clientWidth;
});
</script>
元素尺寸与位置
1、clientWidth和clientHeight
特点:
获取元素的可见部分宽高(不包含边框, margin, 滚动条等)
2.1 offsetWidth和offsetHeight
获取宽高:
获取元素的自身宽高(包含padding、border)
注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
2.2 offsetLeft和offsetTop
获取位置:
获取元素距离自己定位父级元素的左、上距离(没有定位,就以document文档的左上距离为准)
注意: offsetLeft和offsetTop 是只读属性, 不可赋值.
3、 element.getBoundingClientRect()
获取位置: 方法返回元素的大小及其相对视口的位置,
注意: 这是个方法,我们可以调用这个方法里的属性 比如:
相对于视口(可视区)的左距离:
element.getBoundingClientRect().left
相对于视口(可视区)的上距离:
element.getBoundingClientRect().top
4、scrollHeight
元素内容的高度,包括溢出的不可见内容
总结:
| 属性 | 作用 | 说明 |
|---|---|---|
| clientWidth;clientHeight | 获得元素宽度和、高度 | 不包含border,margin,滚动条, 只读属性 |
| scrollLeft;scrollTop | 被卷去的上部和左侧 | 配合页面滚动来用,可读写 |
| offsetWidth;offsetHeight | 获得元素宽度和高度 | 包含padding,border,滚动条,只读属性 |
| offsetLeft;offsetTop | 获取元素距离自己定位父级元素的左、上距离 | 只读属性 |
| getBoundingClientRect() | 返回元素的大小及其相对视口的位置 | 调用方法里的属性求位置 |
| scrollTo(x, y) | scrollTo() 方法可把内容滚动到指定的坐标 | ~~~ |
扩展一下知识:
属性选择器: 在css里会自动匹配带有属性的样式, 在js里会自动选择带有属性的元素.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 被勾选的复选框样式 */
.ck:checked {
width: 20px;
height: 20px;
}
</style>
</head>
<body>
<input type="checkbox" name="" id="" class="ck">
<input type="checkbox" name="" id="" class="ck">
<input type="checkbox" name="" id="" class="ck">
<input type="checkbox" name="" id="" class="ck">
<script>
const cks = document.querySelectorAll('.ck')
for (let i = 0; i < cks.length; i++) {
cks[i].addEventListener('click', function () {
const len2 = document.querySelectorAll('.ck:checked').length;
console.log(len2);
})
}
</script>
</body>
</html>
当我们点击复选框的时候,会自动触发下面的样式, 同时根据console.log(len2)打印效果可知: 系统会记录你选了几个复选框元素
<style>
/* 被勾选的复选框样式 */
.ck:checked {
width: 20px;
height: 20px;
}
</style>