05-事件处理
码路教育 7/19/2022
# 1. 事件类型
事件 + 事件的反馈 = 前端交互(前端的核心)
鼠标事件:
- onclick 点击事件 = onmousedown + onmouseup
- ondbclick 双击事件
- onmousedown 鼠标按下去
- onmouseup 鼠标抬起来
- onmouseover 鼠标移到某元素上面
- onmouseout 鼠标离开某元素上面
- onmousemove 鼠标在元素上面的移动
- ....
键盘事件:
- onkeydown 键盘按下去
- onkeyup 键盘抬起来
- onkeypress 键盘抬住
事件的执行顺序是 onkeydown、onkeypress、onkeyup
- down事件先发生
- press发生在文本被输入
- up发生在文本输入完成
加载事件:
- load 加载 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源 事件源是window
- unload 加载 退出页面 此事件不好捕获到 也没有什么用
- DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 img 和样式表之类的外部资源可能尚未加载 完成。
表单事件:
- focus: 获取焦点
- blur: 失去焦点
- change: 改变输入框中的内容,并失去焦点时触发
- input:内部发生变化,立即触发,不等到失去焦点
- submit: 当点击了提交按钮,会触发submit事件
- reset: 当点击了重置按钮,会触发reset事件
其它事件:
- resize: 改变了浏览器窗口大小时,会触发resize事件
- scroll: 页面滚动时,会触发scroll事件
- ....
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
// 代码执行是从上到下执行,在执行JS代码时,
// HTML结构还没加载完毕,获取DOM节点,是没有
// 办法获取的。
// 所以我们需要等DOM节点加载完毕,再去获取DOM元素,
// 加载事件 load事件 事件源是window
window.onload = function(){
let btn = document.getElementById("btn");
console.log(btn); // null / button
}
</script>
</head>
<body>
<button id="btn">登录</button>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" id="ipt">
<script>
window.onload = function(){
let ipt = document.getElementById("ipt");
// 表示表单获取焦点时触发
ipt.onfocus = function(){
console.log("表单获取了焦点~");
}
ipt.onblur = function(){
console.log("表单失去了焦点~");
}
// 输入框中的数据发生了变化,并失败了焦点就会触发
ipt.onchange = function(){
console.log("表单中的数据发生变化~");
}
ipt.oninput = function(){
console.log("内容变化了~");
}
}
</script>
</body>
</html>
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
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
# 2. 事件的绑定和解绑
# 2.1 HTML级别的事件绑定
可以在HTML开始标签上,去绑定事件,这种绑定基本不用
<body>
<!--
button叫事件源
click叫事件类型 on仅仅是前缀
"alert('点我弄啥~')"事件反馈 事件处理程序 也叫监听器
我们说的事件绑定:指的是绑定监听器
-->
<!-- <button onclick="alert('点我弄啥~')">点我</button> -->
<!-- test() 加了() 这行代码在你眼中就是一个值 -->
<!-- 在test()外面,还包一层函数,这一层函数你看不到 -->
<!-- 只有点击了按钮,外面的函数才会执行,才能执行到里面的test() -->
<!-- ()不加不行 -->
<button onclick="test()">点我</button>
<script>
function test(){
alert("点我弄啥~~")
}
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2.2 DOM0事件绑定
DOM0事件绑定,就是给DOM元素的onxxxx属性赋值,是基于属性赋值的操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// DOM0级别的事件绑定
// 基于属性赋值 如果重复绑定多次,后面的会覆盖前面的
btn.onclick = function(){
// console.log("1");
this.innerHTML = "加载中...."
// this.className = "loading"
this.classList.add("loading")
var self = this;
setTimeout(function(){
// this表示window
// this.innerHTML = "加载更多"
// this.classList.remove("loading")
self.innerHTML = "加载更多"
self.classList.remove("loading")
},2000)
}
// 由于是基于属性赋值,后面赋值的会覆盖掉前面的
// btn.onclick = function(){
// console.log("2");
// }
</script>
</body>
</html>
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
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
现在学习了ES6,可以使用箭头函数,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
btn.onclick = function(){
this.innerHTML = "加载中...."
this.classList.add("loading")
setTimeout(()=>{
this.innerHTML = "加载更多"
this.classList.remove("loading")
},2000)
}
</script>
</body>
</html>
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
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
如果有HTML级别的事件绑定和DOM0的事件绑定,谁的优先给高,演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn" onclick="test()">加载更多</button>
<script>
function test(){
console.log("html级别的事件绑定");
}
// dom0级别的事件绑定优先级是高于HTML级别的事件绑定的
let btn = document.getElementById("btn");
btn.onclick = function(){
console.log("dom0级别的事件绑定");
}
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.3 DOM2的事件绑定
DOM2的事件绑定是基于事件池的,不是基于属性赋值的,所以可以多次绑定,不会覆盖掉,演示代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
// 基于事件池 事件绑定都是异步代码 都是宏任务
btn.addEventListener("click",function(){
this.innerHTML = "加载中..."
this.className = "loading"
setTimeout(()=>{
this.innerHTML = "加载更多"
this.className = ""
},2000)
});
btn.addEventListener("click",function(){
console.log("~我是有底线的~");
})
</script>
</body>
</html>
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
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
特别需要注意,绑定的是事件处理程序,也就是监听器,也就是事件反馈,如果你的监听器是同一个,即使你绑定了多次,也只会执行一次,演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
btn.addEventListener("click",function(){
console.log("111");
});
btn.addEventListener("click",function(){
console.log("222");
})
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// 如果这样绑定,事件池中只有一个Fn
btn.addEventListener("click",fn);
btn.addEventListener("click",fn)
function fn(){
console.log("xxx");
}
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3. 事件解绑
绑定它是绑定的监听器,解绑也是解绑监听器,解绑也分DOM0的解和DOM2的解绑,演示DOM0的解绑代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button{
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got{
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// 事件绑定 绑定的监听器
btn.onclick = function(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// 已领取 需要解绑 解绑的也是监听器
this.onclick = null; // DOM0的解绑
}
</script>
</body>
</html>
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
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
下面的DOM2的事件解绑是不OK的,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button {
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got {
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",fn,false)
// DOM2的解绑 解绑的参数需要和绑定的参数一样
// 不行:因为上,刚把fn扔到池中,下面紧接着,把fn从池中扔出来的
btn.removeEventListener("click", fn,false)
function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
}
</script>
</body>
</html>
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
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
正确的解绑如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button {
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got {
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",fn,false)
function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// DOM2的解绑 解绑的参数需要和绑定的参数一样
btn.removeEventListener("click", fn,false)
}
</script>
</body>
</html>
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
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
其它的解绑方案如下:
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
btn.removeEventListener("click", fn,false)
})
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",function(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// console.log(arguments.callee);
btn.removeEventListener("click", arguments.callee, false)
})
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4. 事件冒泡和事件捕获
- 我们会发现默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序我们称之为事件冒泡(Event Bubble)
- 另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture)
如果我们都监听,那么会按照如下顺序来执行:
- 捕获阶段(Capturing phase):事件(从 Window)向下走近元素
- 目标阶段(Target phase):事件到达目标元素
- 冒泡阶段(Bubbling phase):事件从元素上开始冒泡
开发中通常会使用事件冒泡,所以事件捕获了解即可。
# 5. 事件对象
当一个事件发生时,就会有和这个事件相关的很多信息:
- 比如事件的类型是什么,你点击的是哪一个元素,点击的位置是哪里等等相关的信息
- 那么这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;
- 该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作;
如何获取这个event对象呢?
- event对象会在传入的事件处理(event handler)函数回调时,被系统传入;
- 我们可以在回调函数中拿到这个event对象;
常见的属性:
- type:事件的类型;
- target:当前事件发生的元素;
- currentTarget:当前处理事件的元素;
- eventPhase:事件所处的阶段;
- offsetX、offsetY:事件发生在元素内的位置;
- clientX、clientY:事件发生在客户端内的位置;
- pageX、pageY:事件发生在客户端相对于document的位置;
- screenX、screenY:事件发生相对于屏幕的位置;
常见的方法:
- preventDefault:取消事件的默认行为;
- stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);
# 6. 事件处理中的this
在函数中,我们也可以通过this来获取当前的发生元素:
- 在浏览器内部,调用event handler是绑定到当前的事件源上的
# 7. 事件委托(event delegation)
事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式(也是一种设计模式)
- 当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;
- 可以通过event.target获取到当前监听的元素;