1. 事件对象
事件对象 也是个对象,在这个对象里面有触发事件时的相关信息,例如鼠标点击事件中,事件对象就会记录鼠标点在哪个位置等信息。
事件绑定的回调函数的第一个参数就是事件对象,一般命名为 event 或 e
事件对象部分常用 属性 :
图片随着鼠标移动 实例 :
- 一张图片一直跟着鼠标移动
- 鼠标在页面移动用 mousemove 事件
- 不断把鼠标在页面中的坐标位置给图片
<!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>
img {
position: absolute;
top: 0;
left: 0;
width: 60px;
height: 60px;
}
</style>
</head>
<body>
<img src="./code.png" alt="" />
<script>
let img = document.querySelector('img')
img.addEventListener('mousemove', e => {
// 位置值减图片大小的一半,让鼠标在图片中间
img.style.left = e.clientX - 30 + 'px'
img.style.top = e.clientY - 30 + 'px'
})
</script>
</body>
</html>
按下回车键,可以发布消息:
- 用到按下键盘事件,用 keydown 或 keyup 都可以,但弹起调用 send 点击事件时,使用 keydown 会有一个空格,因此建议使用 keyup
- 如果用户按下的是回车键盘,则发布消息
- 按键发布和点击发布按钮效果一样
2. 事件流
事件流 指的是事件完整执行过程中的流动路径
例如页面里面有一个 div 元素,当事件被触发时,就会经历两个阶段,即 捕获阶段 和 冒泡阶段 ,简单来说,捕获阶段就是 从父到子 ,冒泡阶段则是 从子到父
2.1 事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有的祖先元素中依次被触发,这一过程就是 事件冒泡
简单来说就是当一个元素触发事件后,会依次向上调用 所有父级 元素的同名事件,事件冒泡是默认存在的
例如:触发点击事件
<!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>
<style>
.s {
width: 100px;
height: 50px;
background-color: rgb(159, 251, 222);
}
.fa {
width: 200px;
height: 100px;
background-color: rgb(245, 165, 216);
}
</style>
<body>
<div class="fa">
<div class="s"></div>
</div>
<script>
let fa = document.querySelector('.fa')
let s = document.querySelector('.s')
document.body.addEventListener('click',()=>{
alert('body 的点击事件')
})
fa.addEventListener('click', () => {
alert('父元素的点击事件')
})
s.addEventListener('click', () => {
alert('触发了子元素的 click 事件')
})
</script>
</body>
</html>
冒泡阶段:点击 “ s ” , 依次弹出 s ---> fa ---> body
2.2 事件捕获
从 DOM 的根元素开始去执行对应的事件(从外到里)称为 事件捕获
事件捕获需要写对应的代码才能看到效果:
监听事件传入的第三个参数 true ,表示 捕获阶段 触发,这一事件使用较少
如果传入 false 则代表是冒泡阶段,默认 就是 false
如果用 L0 事件监听,只有冒泡阶段,没有捕获阶段
例如:同一点击事件
<!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>
<style>
.s {
width: 100px;
height: 50px;
background-color: rgb(159, 251, 222);
}
.fa {
width: 200px;
height: 100px;
background-color: rgb(245, 165, 216);
}
</style>
<body>
<div class="fa">
<div class="s"></div>
</div>
<script>
let fa = document.querySelector('.fa')
let s = document.querySelector('.s')
document.body.addEventListener('click',()=>{
alert('body 的点击事件')
},true)
fa.addEventListener('click', () => {
alert('父元素的点击事件')
},true)
s.addEventListener('click', () => {
alert('触发了子元素的 click 事件')
},true)
</script>
</body>
</html>
捕获阶段:点击 “ s ” , 依次弹出 body ---> fa ---> s
2.3 阻止事件流动
因为事件冒泡默认就存在,因此容易导致事件影响到父级元素,如果想把事件就限制在当前元素内,不向外冒泡,就需要阻止事件流动
阻止事件流动 需要拿到事件对象:
此方法可以阻断事件流动传播,不止在冒泡阶段有效,捕获阶段也有效
示例1:冒泡阶段阻止事件流动
示例2:捕获阶段阻止事件流动
鼠标经过事件推荐使用 mouseenter 和 mouseleave ,没有冒泡效果
2.4 阻止默认行为
阻止默认行为 preventDefault() ,例如表单域的跳转、点击连接不跳转等
<body>
<a href="www.baidu.com">百度</a>
<script>
let a = document.querySelector('a')
a.addEventListener('click', e => {
e.preventDefault()
})
</script>
</body>
3. 事件委托
事件委托 是利用事件流的特征解决一些开发需求的知识技巧
- 优点 :给父级元素添加事件,可以提高性能;
- 原理 :事件委托其实是利用事件冒泡的特点;
- 实现 :事件
对象.target
可以获得真正触发事件的元素
<body>
<div>123456</div>
<script>
let div = document.querySelector('div')
div.addEventListener('click', e => {
console.log(e.target)
e.target.style.color = 'red'
})
</script>
</body>
4. 实例:点击按钮,录入学生信息
删除时,使用事件委托来实现:
用 ID 来记录点击的按钮下标:
用事件委托来实现删除
完整代码:
* {
margin: 0;
padding: 0;
}
tbody button {
width: 66px;
height: 36px;
font-size: 18px;
background-color: rgb(37, 192, 244);
border: none;
border-radius: 3px;
color: white;
}
h1 {
text-align: center;
color:#333;
margin: 20px 0;
}
table {
margin:0 auto;
width: 800px;
border-collapse: collapse;
color:#004085;
}
th {
padding: 10px;
background: #cfe5ff;
font-size: 20px;
font-weight: 400;
}
td,th {
border:1px solid #b8daff;
}
td {
padding:10px;
color:#666;
text-align: center;
font-size: 16px;
}
tbody tr {
background: #fff;
}
tbody tr:hover {
background: #e1ecf8;
}
.info {
width: 900px;
margin: 50px auto;
text-align: center;
}
.info input {
width: 80px;
height: 25px;
outline: none;
border-radius: 5px;
border:1px solid #b8daff;
padding-left: 5px;
}
.info button {
width: 60px;
height: 25px;
background-color: #004085;
outline: none;
border: 0;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
.info .age {
width: 50px;
}
<!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">
<link rel="stylesheet" href="css/user.css">
<title>添加学生信息</title>
</head>
<body>
<h1>新增学生信息</h1>
<div class="info">
姓名:<input type="text" class="uname">
年龄:<input type="text" class="age">
性别: <select name="gender" id="" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary">
就业城市:<select name="city" id="" class="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="陕西">陕西</option>
</option>
</select>
<button class="add">录入</button>
</div>
<h1>就业榜</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>1001</td>
<td>张三</td>
<td>19</td>
<td>男</td>
<td>15000</td>
<td>上海</td>
<td>
<button>删除(不允许删除)</button>
</td>
</tr> -->
</tbody>
</table>
<script>
// 1. 准备好数据后端的数据
let arr = [
{ stuId: 1001, uname: '张三', age: 19, gender: '男', salary: '20000', city: '上海' },
{ stuId: 1002, uname: '李四', age: 29, gender: '男', salary: '30000', city: '北京' },
{ stuId: 1003, uname: '小明', age: 39, gender: '男', salary: '2000', city: '北京' },
]
// 获取元素
let tbody = document.querySelector('table tbody')
let add = document.querySelector('.add')
let uname = document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
// 初始化函数
function ini() {
// 先清空原来的数据,再新增
tbody.innerHTML = ''
for (let i = 0; i < arr.length; i++) {
let tr = document.createElement('tr')
tr.innerHTML = `
<td>${arr[i].stuId}</td>
<td>${arr[i].uname}</td>
<td>${arr[i].age}</td>
<td>${arr[i].gender}</td>
<td>${arr[i].salary}</td>
<td>${arr[i].city}</td>
<td>
<button id='${i}'>删除</button>
</td>
`
tbody.appendChild(tr)
// 复原表单的值
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
}
}
ini() // 函数不调用不执行
// 添加信息
add.addEventListener('click', () => {
// console.log(uname.value)
if (!uname.value.trim()) {
alert("学生姓名不允许为空,请重新添加")
return
}
let stuId = arr[arr.length - 1].stuId + 1
let arrNew = {
stuId: stuId,
uname: uname.value.trim(),
age: age.value.trim(),
gender: gender.value.trim(),
salary: salary.value.trim(),
city: city.value.trim()
}
arr.push(arrNew)
ini()
})
// 事件委托 实现删除
tbody.addEventListener('click', (e) => {
if (arr.length === 1) {
alert("必须保留一条数据")
return
}
// console.dir(e.target)
// 点击了删除按钮
if (e.target.tagName === 'BUTTON') {
// console.log(e.target)
// console.log(e.target.id) e.target.id = 数组下标
arr.splice(e.target.id, 1)
ini()
}
})
</script>
</body>
</html>