DOM- 事件高级
事件对象
1.初识事件对象
事件对象定义
- 也是个对象,这个对象里有事件触发时的相关信息
- 例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
获取事件对象
-
在事件绑定的回调函数的第一个参数就是事件对象
-
一般命名为event、ev、e
代码
<body> <button>点击</button> <script> let btn = document.querySelector('button') btn.addEventListener('click', function (e) { console.log(e); }) </script> </body>
2.事件对象常用属性
部分常用属性
-
type
获取当前的事件类型
-
clientX/clientY
获取光标相对于浏览器可见窗口左上角的位置
-
offsetX/offsetY
获取光标相对于当前DOM元素左上角的位置
-
key
用户按下的键盘键的值
现在不提倡使用keyCode
案例
图片跟随鼠标移动
<style>
img {
position: absolute;
width: 100px;
}
</style>
</head>
<body>
<img src="../images/3.png" alt="">
<script>
// let body = document.querySelector('body')
let pic = document.querySelector('img')
document.addEventListener('mousemove', function (e) {
let x = e.clientX
let y = e.clientY
pic.style.top = y + 'px'
pic.style.left = x + 'px'
})
</script>
</body>
事件流
1.事件流与两个阶段说明
事件流
事件流指的是事件完整执行过程中的流动路径
-
说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
-
简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父
2.事件捕获和事件冒泡
事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
-
简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
-
默认是事件冒泡
事件捕获
从DOM的根元素开始去执行对应的事件 (从外到里)
-
事件捕获需要写对应代码才能看到效果
-
代码
-
说明
- addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
- 若传入false代表冒泡阶段触发,默认就是false
- 若是用 L0 事件监听,则只有冒泡阶段,没有捕获
3.修改事件流动方向
代码展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content=", initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
width: 200px;
height: 200px;
padding: 20px;
overflow: hidden;
}
.a {
background-color: red;
}
.b {
background-color: blue;
}
.c {
background-color: green;
}
</style>
</head>
<body>
<div class="a">
爷
<div class="b">
爸
<div class="c">儿</div>
</div>
</div>
<script>
let a = document.querySelector('.a')
let b = document.querySelector('.b')
let c = document.querySelector('.c')
a.addEventListener('click', function () {
console.log('a');
}, true) //true 将事件流动改为捕获阶段
b.addEventListener('click', function () {
console.log('b');
})
c.addEventListener('click', function () { //默认是事件冒泡
console.log('c');
}, false) //默认为false,冒泡
</script>
</body>
</html>
4.阻止事件流动
原因
- 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
- 若想把事件就限制在当前元素内,就需要阻止事件流动
- 阻止事件流动需要拿到事件对象
语法
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
鼠标经过事件:
- mouseover 和 mouseout 会有冒泡效果
- mouseenter 和 mouseleave 没有冒泡效果(推荐)
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content=", initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
width: 200px;
height: 200px;
padding: 20px;
overflow: hidden;
}
.a {
background-color: red;
}
.b {
background-color: blue;
}
.c {
background-color: green;
}
</style>
</head>
<body>
<div class="a">
爷
<div class="b">
爸
<div class="c">儿</div>
</div>
</div>
<script>
let num = 1
let a = document.querySelector('.a')
let b = document.querySelector('.b')
let c = document.querySelector('.c')
a.addEventListener('click', function (e) {
console.log('a');
e.stopPropagation()
console.log(num += 1);
})
b.addEventListener('click', function (e) {
console.log('b');
console.log(num += 10);
e.stopPropagation()
})
c.addEventListener('click', function (e) { //默认是事件冒泡
console.log('c');
e.stopPropagation()
console.log(num += 100);
})
</script>
</body>
</html>
5.阻止默认行为
阻止默认行为,比如链接点击不跳转,表单域的不提交
语法
e.preventDefault();
案例
form表单 button按钮点击刷新行为--解决方案
案例
<!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>
</head>
<body>
<a href="http://www.baidu.com">百度</a>
<form action="">
<!-- <button type="button">点击刷新</button> //方法三:给button添加type="button" -->
<button>点击刷新</button>
</form>
<script>
// 1.点击链接不跳转
let a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.preventDefault();
console.log('点击链接不跳转');
})
// 2.点击form表单中的按钮不刷新
// 方法一
let form = document.querySelector('form')
let btn = document.querySelector('button')
// form.addEventListener('submit', function (e) {
// console.log('点击不刷新');
// e.preventDefault();
// })
// 方法二
// btn.addEventListener('click', function (e) {
// e.preventDefault();
// console.log('不刷新');
// })
</script>
</body>
</html>
自定义右键菜单
<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
}
.menu {
list-style: none;
padding: 10px;
border-radius: 5px;
border: 1px solid #ccc;
width: 150px;
position: fixed;
display: none;
}
li {
height: 40px;
display: flex;
align-items: center;
padding-left: 10px;
border-bottom: 1px solid #ccc;
}
li:hover {
background-color: skyblue;
color: #fff;
cursor: pointer;
}
li:last-child {
border-bottom: none;
}
</style>
</head>
<body>
<ul class="menu">
<li>添加图标</li>
<li>切换壁纸</li>
<li>下载壁纸</li>
<li>设置</li>
</ul>
<script>
let menu = document.querySelector('.menu')
// 阻止body右键
document.body.addEventListener('contextmenu', function (e) { //contextmenu 右键菜单
e.preventDefault()
let left = e.clientX
let top = e.clientY
menu.style.left = left
menu.style.top = top
menu.style.display = 'block';
menu.style.left = left + 'px';
menu.style.top = top + 'px';
})
document.body.addEventListener('click', function (e) {
menu.style.display = 'none';
})
</script>
</body>
</html>
事件委托
定义
事件委托是利用事件流的特征解决一些开发需求的知识技巧
总结
- 优点:给父级元素加事件(可以提高性能)
- 原理:事件委托其实是利用事件冒泡的特点, 给父元素添加事件,子元素可以触发
- 实现:事件对象.target 可以获得真正触发事件的元素
代码展示
<!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>
ul {
background-color: aqua;
width: 600px;
padding: 50px;
list-style: none;
}
</style>
</head>
<body>
<ul>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
</ul>
<script>
let ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
e.target.style.backgroundColor = 'red'
})
</script>
</body>
</html>
在上方的事件委托案例中,我们可以看出一个缺陷:只要在范围内的标签都可以点击并且发生相应的变化,不方便筛选出指定的标签做变化。
需要使用nodeName
标签来帮助我们筛选出正确的标签,比如e.target.nodeName === 'LI'
代表只有点击li
标签才会发生变化,nodeName
后的标签要大写
代码展示
<!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>
ul {
background-color: aqua;
width: 600px;
padding: 50px;
list-style: none;
}
</style>
</head>
<body>
<ul>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
</ul>
<script>
let ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// e.target.style.backgroundColor = 'red'
console.log(e.target);
// if (e.targe.nodeName === 'LI') {
if (e.target.nodeName === 'LI') {
console.log('大写改标签');
e.target.style.backgroundColor = 'pink'
}
if (e.target.nodeName === 'A') {
console.log('点击链接');
e.target.style.backgroundColor = 'blank'
e.target.style.Color = 'blank'
}
})
</script>
</body>
</html>
两种注册事件的区别
- 传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的
- 事件监听注册(L2)
- 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
const 跟 let 的区别
能使用 const ,就不要使用let
const 声明的变量不能被修改