DOM- 事件高级

1. 事件对象

1. 获取事件对象

  1. 事件对象是个对象, 这个对象里有事件触发时的相关信息
  2. 例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
  3. 获取: 在事件绑定的回调函数的第一个参数就是事件对象, 一般命名为event、ev、e
1
2
3
4
5
// 1. 事件对象
let num1 = document.querySelector('button')
num1.addEventListener('click', function (e) {
console.log(e)
})

2. 事件对象常用属性

  1. 部分常用属性: type 获取当前的事件类型
  2. clientX/clientY: 获取光标相对于浏览器可见窗口左上角的位置
  3. offsetX/offsetY: 获取光标相对于当前DOM元素左上角的位置
  4. key: 用户按下的键盘键的值, 现在不提倡使用keyCode
1
2
3
4
5
6
7
8
9
// 2. 事件对象常用属性
document.addEventListener('click', function (e) {
// 当前视口坐标
console.log(e.clientX, e.clientY)
// 整个页面坐标
console.log(e.pageX, e.pageY)
// 当前元素坐标
console.log(e.offsetX, e.offsetY)
})

跟随鼠标案例:

1
2
3
4
5
6
7
8
需求:一张图片一直跟着鼠标移动
// 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'
})

按下回车发布微博案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
①:用到按下键盘事件 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. 事件流和两个阶段说明

  1. 事件流指的是事件完整执行过程中的流动路径
  2. 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
  3. 简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

2. 事件捕获和事件冒泡

事件冒泡概念:

  1. 当一个元素事件被触发时, 同样的事件将会在该元素的所有祖先元素中依次被触发, 这一过程被称为事件冒泡
  2. 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
  3. 事件冒泡是默认存在的

事件捕获概念:

  1. 从DOM的根元素开始去执行对应的事件 (从外到里)
  2. 事件捕获需要写对应代码才能看到效果

说明:

  1. addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
  2. 若传入false代表冒泡阶段触发,默认就是false
  3. 若是用 L0 事件监听,则只有冒泡阶段,没有捕获
1
2
3
4
5
6
7
8
9
10
11
// 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. 阻止事件流动

  1. 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
  2. 若想把事件就限制在当前元素内,就需要阻止事件流动
  3. 阻止事件流动需要拿到事件对象
  4. 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
1
2
3
// 3. 阻止事件流动 Propagation(传播)
e.stopPropagation()
}, false)

鼠标经过事件:

  1. mouseover 和 mouseout 会有冒泡效果
  2. mouseenter 和 mouseleave 没有冒泡效果(推荐)
1
2
3
4
// mouseover/out会有冒泡 enter/leave没有(推荐)
num1.addEventListener('mouseover', function () {
console.log(11)
})

阻止默认行为,比如链接点击不跳转,表单域的跳转

1
2
3
4
5
6
// 5. 阻止默认行为 方法 链接不跳转/表单并提交
let num3 = document.querySelector('a')
num3.addEventListener('click', function (e) {
e.preventDefault()
e.stopPropagation()
})

两种注册事件的区别:

传统on注册(L0)

  1. 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
  2. 直接使用null覆盖偶就可以实现事件的解绑, 都是冒泡阶段执行的
1
2
3
4
5
6
7
8
9
10
let num4 = document.querySelector('button')
// 多次相同事件只执行最后一次
num4.onclick = function () {
alert('你好')
}
num4.onclick = function () {
alert('你好1')
}
// 解除绑定事件
num4.onclick = null

事件监听注册(L2)

  1. 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
  2. 后面注册的事件不会覆盖前面注册的事件(同一个事件)
  3. 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
  4. 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
  5. 匿名函数无法被解绑
1
2
3
4
5
6
7
8
9
10
11
12
13
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. 事件委托

  1. 事件委托是利用事件流的特征解决一些开发需求的知识技巧
  2. 优点:给父级元素加事件(可以提高性能)
  3. 原理:事件委托其实是利用事件冒泡的特点
  4. 实现:事件对象.target 可以获得真正触发事件的元素
1
2
3
4
5
// 不是给每个li注册事件 而是委托给父级
let num6 = document.querySelector('ul')
num6.addEventListener('click', function (e) {
e.target.style.color = 'red'
})

4. 购物车案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
    <div class="car">
<table>
<thead>
<tr>
<th><input type="checkbox" id="all" />全选</th>
<th>商品</th>
<th>单价</th>
<th>商品数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody id="carBody">
<tr>
<td>
<input class="s_ck" type="checkbox" readonly />
</td>
<td>
<img src="./images/01.jpg" />
<p>牛奶</p>
</td>
<td class="price">5¥</td>
<td>
<div class="count-c clearfix">
<button class="reduce" disabled>-</button>
<input type="text" value="1" />
<button class="add">+</button>
</div>
</td>
<td class="total">5¥</td>
<td><a href="javascript:" class="del">删除</a></td>
</tr>
</tbody>
</table>
<div class="controls clearfix">
<a href="javascript:" class="del-all">删除所选商品</a>
<a href="javascript:" class="clear">清理购物车</a>
<a href="javascript:" class="pay">去结算</a>
<p>已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span></p>
</div>
</div>
let btn = document.querySelectorAll('.reduce')
let btn1 = document.querySelectorAll('.add')
let input = document.querySelectorAll('.count-c input')
let del = document.querySelectorAll('.del')
let price = document.querySelectorAll('.price')
let total = document.querySelectorAll('.total')
let totalCount = document.querySelector('#totalCount')
let totalprice = document.querySelector('.total-price')
let carBody = document.querySelector('#carBody')
for (let num1 = 0; num1 < btn1.length; num1++) {
total[num1].innerHTML = price[num1].innerHTML
// 1.加号操作
btn1[num1].addEventListener('click', function () {
input[num1].value++
btn[num1].disabled = false
// console.log(parseInt(price[num1].innerHTML) * input[num1].value)
total[num1].innerHTML = parseInt(price[num1].innerHTML) * input[num1].value + '¥'
fn()
})
// 2.减号操作
btn[num1].addEventListener('click', function () {
input[num1].value--
total[num1].innerHTML = parseInt(price[num1].innerHTML) * input[num1].value + '¥'
if (input[num1].value <= 1) {
btn[num1].disabled = true
}
fn()
})
// 4.删除操作
del[num1].addEventListener('click', function () {
// carBody.removeChild(carBody.children[num1])
carBody.removeChild(this.parentNode.parentNode)
fn()
})
}
// 3.计算总件/总价函数
function fn() {
let total = document.querySelectorAll('.total')
let input = document.querySelectorAll('.count-c input')
let num2 = 0
let num3 = 0
for (let num1 = 0; num1 < total.length; num1++) {
num2 = num2 + parseInt(input[num1].value)
num3 = num3 + parseInt(total[num1].innerHTML)
}
totalCount.innerHTML = num2
totalprice.innerHTML = num3 + '¥'
}
fn()

5. 综合案例

  1. 需求:点击录入按钮,可以增加学生信息
  2. 本次案例主要目的是为了后面学习Vue做铺垫(数据驱动视图)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 数据后端数据
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()
})

本节单词:

  1. keyup
  2. keydown
  3. enter
  4. stopPropagation
  5. mouseover
  6. preventDefault
  7. onclick
  8. removeEventListener
  9. target
  10. tagName
  11. page