# DOM- 事件高级
# 1. 事件对象
# 1. 获取事件对象
- 事件对象是个对象, 这个对象里有事件触发时的相关信息
- 例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
- 获取: 在事件绑定的回调函数的第一个参数就是事件对象, 一般命名为event、ev、e
// 1. 事件对象
let num1 = document.querySelector('button')
num1.addEventListener('click', function (e) {
console.log(e)
})
# 2. 事件对象常用属性
- 部分常用属性: type 获取当前的事件类型
- clientX/clientY: 获取光标相对于浏览器可见窗口左上角的位置
- offsetX/offsetY: 获取光标相对于当前DOM元素左上角的位置
- key: 用户按下的键盘键的值, 现在不提倡使用keyCode
// 2. 事件对象常用属性
document.addEventListener('click', function (e) {
// 当前视口坐标
console.log(e.clientX, e.clientY)
// 整个页面坐标
console.log(e.pageX, e.pageY)
// 当前元素坐标
console.log(e.offsetX, e.offsetY)
})
# 跟随鼠标案例:
需求:一张图片一直跟着鼠标移动
// 3. 跟随鼠标案例
let img = document.querySelector('img')
document.addEventListener('mousemove', function (e) {
// 不断获取当前鼠标坐标 把坐标给图片
img.style.top = e.pageY + -30 + 'px'
img.style.left = e.pageX + -30 +'px'
})
# 按下回车发布微博案例:
①:用到按下键盘事件 keydown 或者 keyup 都可以
②:如果用户按下的是回车键盘,则发布信息
③:按下键盘发布新闻,其实和点击发布按钮效果一致 send.click()
// 1.事件侦听三要素 键盘事件 发布信息
// 2.keyup 按下键盘松开后触发
// 3.keydown 按下键盘就触发 场景: 拖拽使用
text.addEventListener('keyup', function (e) {
// console.log(e) // e.keyCode(废弃)
// 4.现使用key
console.log(e.key)
if (e.key == 'Enter') {
// 5.自动触发点击按钮
send.click()
}
})
# 2. 事件流
# 1. 事件流和两个阶段说明
- 事件流指的是事件完整执行过程中的流动路径
- 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
- 简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

# 2. 事件捕获和事件冒泡
# 事件冒泡概念:
- 当一个元素事件被触发时, 同样的事件将会在该元素的所有祖先元素中依次被触发, 这一过程被称为事件冒泡
- 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
- 事件冒泡是默认存在的
# 事件捕获概念:
- 从DOM的根元素开始去执行对应的事件 (从外到里)
- 事件捕获需要写对应代码才能看到效果
# 说明:
- addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
- 若传入false代表冒泡阶段触发,默认就是false
- 若是用 L0 事件监听,则只有冒泡阶段,没有捕获
// 1. 冒泡事件阶段(理解)
let num1 = document.querySelector('.box')
let num2 = document.querySelector('.box1')
num1.addEventListener('click', function (e) {
alert('我是爸爸')
e.stopPropagation()
// 2. 捕获事件阶段(了解)
}, true)
num2.addEventListener('click', function (e) {
alert('我是儿子')
})
# 3. 阻止事件流动
- 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
- 若想把事件就限制在当前元素内,就需要阻止事件流动
- 阻止事件流动需要拿到事件对象
- 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
// 3. 阻止事件流动 Propagation(传播)
e.stopPropagation()
}, false)
# 鼠标经过事件:
- mouseover 和 mouseout 会有冒泡效果
- mouseenter 和 mouseleave 没有冒泡效果(推荐)
// mouseover/out会有冒泡 enter/leave没有(推荐)
num1.addEventListener('mouseover', function () {
console.log(11)
})
# 阻止默认行为,比如链接点击不跳转,表单域的跳转
// 5. 阻止默认行为 方法 链接不跳转/表单并提交
let num3 = document.querySelector('a')
num3.addEventListener('click', function (e) {
e.preventDefault()
e.stopPropagation()
})
# 两种注册事件的区别:
传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑, 都是冒泡阶段执行的
let num4 = document.querySelector('button')
// 多次相同事件只执行最后一次
num4.onclick = function () {
alert('你好')
}
num4.onclick = function () {
alert('你好1')
}
// 解除绑定事件
num4.onclick = null
事件监听注册(L2)
- 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
let num5 = document.querySelector('.btn')
num5.addEventListener('click', function () {
alert('你好')
})
// 2. 事件监听注册L2
// 不会覆盖前面事件
num5.addEventListener('click', add)
function add() {
alert('你好1')
}
// 解除绑定事件
num5.removeEventListener('click', add)
# 3. 事件委托
- 事件委托是利用事件流的特征解决一些开发需求的知识技巧
- 优点:给父级元素加事件(可以提高性能)
- 原理:事件委托其实是利用事件冒泡的特点
- 实现:事件对象.target 可以获得真正触发事件的元素
// 不是给每个li注册事件 而是委托给父级
let num6 = document.querySelector('ul')
num6.addEventListener('click', function (e) {
e.target.style.color = 'red'
})
# 4. 综合案例
- 需求:点击录入按钮,可以增加学生信息
- 本次案例主要目的是为了后面学习Vue做铺垫(数据驱动视图)
// 数据后端数据
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: '北京' },
]
// 1.渲染函数
let tbody = document.querySelector('tbody')
let btn = 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 fn() {
// 去掉以前数据 让点击的时候tr都没有
tbody.innerHTML = ''
for (let num1 = 0; num1 < arr.length; num1++) {
// 创建tr标签 然后追加给tbody
let tr = document.createElement('tr')
// 给a添加一个序号 才能知道删除操作删除哪一个
tr.innerHTML = `
<tr>
<td>${arr[num1].stuid}</td>
<td>${arr[num1].uname}</td>
<td>${arr[num1].age}</td>
<td>${arr[num1].gender}</td>
<td>${arr[num1].salary}</td>
<td>${arr[num1].city}</td>
<td><a href="javascript:" id="${num1}">删除</a></td>
</tr>
`
// 复原所有表单数据
tbody.appendChild(tr)
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
}
}
// 页面加载调用函数
fn()
// 2. 添加数据按钮
// 获得表单里的值 追加给数组 用push方法
btn.addEventListener('click', function () {
// 得到数组最后一条数据学号 1003 + 1
arr.push({
stuid: arr[arr.length - 1].stuid + 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
})
// console.log(arr)
fn()
})
// 3. 删除操作 使用事件委托来删除数组数据
tbody.addEventListener('click', function (e) {
// 点击了A才能删除 用if判断tagNmae是否为A来删除
// console.log(e.target.tagName);
if (e.target.tagName == 'A') {
// 删除数组里的数据
// 给a添加一个序号 才能知道删除操作删除哪一个
arr.splice(e.target.id, 1)
}
fn()
})
# 本节单词:
- keyup
- keydown
- enter
- stopPropagation
- mouseover
- preventDefault
- onclick
- removeEventListener
- target
- tagName
- page