一、事件高级内容

1.1、注册事件/绑定事件

1.1.1、概述

1
2
1. 给元素添加事件,称为【注册事件】或者【绑定事件】。
2. 注册事件有两种方式:【传统方式】和【方法监听注册方式】。

0090

1.1.2、传统注册方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button>点击</button>
<script>
// 1. 获取button按钮
var btnObj = document.querySelector("button");

// 2、传统注册方式
btnObj.onclick = function() {
alert('HelloWorld');
}

btnObj.onclick = function() {
alert('JavaScript');
}

// 结论: 同一个元素节点同一个事件只能设置一个处理函数
</script>
</body>

1.1.3、方法监听注册方式

1
2
3
4
5
6
7
8
# 语法
eventTarget.addEventListener(type, listener[, useCapture])
该方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。

# 说明
1. type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
2. listener:事件处理函数,事件发生时,会调用该监听函数
3. useCapture:可选参数,是一个布尔值,默认是 false。以后再说。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button>点击</button>
<script>
// 1. 获取button按钮
var btnObj = document.querySelector("button");

// 2、方法监听注册方式
btnObj.addEventListener('click', function() {
alert('HelloWorld');
});

btnObj.addEventListener('click', function() {
alert('JavaScript');
})

// 结论: 同一个元素节点同一个事件可以指定多个事件处理程序。
</script>
</body>

1.1.4、attachEvent事件监听方式

1
2
3
4
5
6
7
8
9
10
# 语法
eventTarget.attachEvent(eventNameWithOn, callback)
该方法将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,指定的回调函数就会被执行

# 说明
1. eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
2. callback: 事件处理函数,当目标触发事件时回调函数被调用

# 注意
1. IE8 及早期版本支持,新版本不支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button>点击</button>
<script>
// 1. 获取button按钮
var btnObj = document.querySelector("button");

// 2、方法监听注册方式
btnObj.attachEvent("onclick", function() {
alert('HelloWorld');
});

btnObj.attachEvent("onclick", function() {
alert('JavaScript');
});

// 结论: 同一个元素节点同一个事件可以指定多个事件处理程序。不过此种方式是IE8以前提供的方式。
</script>
</body>

0091

1.2、删除事件/解绑事件

1.2.1、概述

1
2
3
4
5
6
# 传统方式
eventTarget.onclick = null;

# 方法监听注册方式
1. eventTarget.removeEventListener(type, listener[, useCapture]);
2. eventTarget.detachEvent(eventNameWithOn, callback); // 存在兼容性问题,I8及之前才有效。

1.2.2、传统方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<button id="btn1">传统方式绑定事件</button>
<button id="btn2">传统方式解绑事件</button>
<script>
// 1. 获取button按钮
var btn1Obj = document.querySelector("#btn1");
btn1Obj.onclick = function() {
alert('HelloWorld');
}

// 2、解绑按钮
var btn2Obj = document.querySelector("#btn2");
btn2Obj.onclick = function() {
btn1Obj.onclick = null;
}
</script>
</body>

1.2.3、方法监听方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<button id="btn1">方法监听方式绑定事件</button>
<button id="btn2">方法监听方式解绑事件</button>
<script>
// 1. 获取button按钮
var btn1Obj = document.querySelector("#btn1");
btn1Obj.addEventListener('click', fn1);

function fn1() {
alert('JavaScript');
}

// 2、解绑按钮
var btn2Obj = document.querySelector("#btn2");
btn2Obj.onclick = function() {
btn1Obj.removeEventListener('click', fn1); // 需要指定事件处理函数名称
}
</script>
</body>

二、DOM事件流

2.1、概述

事件流描述的是从页面中接收事件的顺序。

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。

​ 比如我们给一个div 注册了点击事件,那么这个传播过程是这样的:

    1. 捕获阶段
    1. 当前目标阶段
    1. 冒泡阶段

0092

1
2
1. 事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
2. 事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。

2.2、通俗理解

​ 我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具

体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点( 最具体元素)之后漂浮到水面上,这个过

程相当于事件冒泡。

0093

2.3、注意点

  • JS 代码中只能执行捕获或者冒泡其中的一个阶段。
  • onclick 和 attachEvent 只能得到冒泡阶段。
  • addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序。
  • ==实际开发中我们很少使用事件捕获,我们更关注事件冒泡。==
  • ==有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave==
  • 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件,我们后面讲解。

2.4、案例1

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
<head>
<style>
#div1 {
width: 300px;
height: 300px;
background-color: red;
}

#div2 {
width: 200px;
height: 200px;
background-color: blue;
}

#div3 {
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
// 测试 onclick 只能是得到冒泡阶段

// 1. 获取div1
var div1 = document.querySelector("#div1");
div1.onclick = function() {
alert('div1');
}

// 2. 获取div2
var div2 = document.querySelector("#div2");
div2.onclick = function() {
alert('div2');
}

// 3. 获取div3
var div3 = document.querySelector("#div3");
div3.onclick = function() {
alert('div3');
}
</script>
</body>

2.5、案例2

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
<body>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
// 测试 addEventListener ,第三个参数不写,或者为false,则是冒泡阶段

// 1. 获取div1
var div1 = document.querySelector("#div1");
div1.addEventListener('click', function() {
alert("div1");
});

// 2. 获取div2
var div2 = document.querySelector("#div2");
div2.addEventListener('click', function() {
alert("div2");
});

// 3. 获取div3
var div3 = document.querySelector("#div3");
div3.addEventListener('click', function() {
alert("div3");
});
</script>
</body>

2.6、案例3

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
<body>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
// 测试 addEventListener ,第三个参数为true,则是捕获阶段

// 1. 获取div1
var div1 = document.querySelector("#div1");
div1.addEventListener('click', function() {
alert("div1");
}, true);

// 2. 获取div2
var div2 = document.querySelector("#div2");
div2.addEventListener('click', function() {
alert("div2");
}, true);

// 3. 获取div3
var div3 = document.querySelector("#div3");
div3.addEventListener('click', function() {
alert("div3");
}, true);
</script>
</body>

三、事件对象

3.1、什么是事件对象

1
2
3
4
5
6
7
8
eventTarget.onclick = function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
}

eventTarget.addEventListener('click', function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
})
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 官方解释
event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。

# 通俗理解
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象event,它有很多属性和方法。
例如:
1. 谁绑定了这个事件。
2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。

# 注意点
事件对象本身的获取存在兼容问题:
1. 标准浏览器中是浏览器给方法传递的参数,只需要定义形参 event 就可以获取到。
2. 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。

解决兼容性问题:
event = event || window.event;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div>HelloWorld</div>
<script>
// 获取div
var divObj = document.querySelector('div');

// 传统方式绑定事件
divObj.onclick = function(event) {
event = event || window.event; // 兼容性的写法
console.log(event);
}

// 方法监听方式绑定事件
divObj.addEventListener('click', function(event) {
console.log(event);
});
</script>
</body>

3.2、e.target 和 this 的区别

3.2.1、案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div>HelloWorld</div>
<script>
// 常见事件对象的属性和方法
// >> e.target和this的区别

var divObj = document.querySelector("div");

divObj.addEventListener('click', function(event) {
console.log(this);
console.log(event.target);
})

// 总结:貌似没区别,在看案例2
</script>
</body>

3.2.2、案例2

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
<body>
<ul>
<li>高圆圆</li>
<li>贾静雯</li>
<li>赵敏</li>
</ul>
<script>
// 常见事件对象的属性和方法
// >> e.target和this的区别

var ulObj = document.querySelector("ul");

// 给ul标签节点绑定单击事件
ulObj.addEventListener('click', function(event) {
console.log(this); // ul
console.log(event.target); // li
})

// 总结
// >> e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
// >> this : 我们给ul 绑定了事件 那么this 就指向ul

// 精髓总结
// >> e.target:点击了哪个元素,就返回哪个元素
// > this: 哪个元素绑定了这个点击事件,那么就返回谁
</script>
</body>

3.2.3、案例3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<ul>
<li>高圆圆</li>
<li>贾静雯</li>
<li>赵敏</li>
</ul>
<script>
// 常见事件对象的属性和方法
// >> e.target和this的区别

var ulObj = document.querySelector("ul");

// 给ul标签节点绑定单击事件
ulObj.addEventListener('click', function(event) {
console.log(this); // ul
console.log(event.currentTarget); // ul
})

// 总结
// >> this 有个非常相似的属性 currentTarget,但是ie678不认识,所以this更为通用。
</script>
</body>

3.3、事件对象的常见属性和方法

0094

3.4、阻止事件默认行为

3.4.1、说明

1
# 所谓的阻止事件默认行为,比如说让超链接不跳转,让提交按钮本来要提交表单的事件不发生。

3.4.2、案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<a href="https://www.baidu.com">跳转到百度</a>
<script>
// 阻止超链接的默认行为

var aObj = document.querySelector("a");

// 给ul标签节点绑定单击事件
aObj.addEventListener('click', function(event) {
alert("阻止超链接跳转哦");
event.preventDefault(); // DOM的标准写法
})
</script>
</body>

3.4.3、案例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<a href="https://www.baidu.com">跳转到百度</a>
<script>
// 阻止超链接的默认行为

var aObj = document.querySelector("a");

// 给ul标签节点绑定单击事件
aObj.onclick = function(event) {
alert("阻止超链接的默认行为哦");
event.preventDefault(); // DOM标准写法
}
</script>
</body>

3.4.3、案例3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<a href="https://www.baidu.com">跳转到百度</a>
<script>
// 阻止超链接的默认行为

var aObj = document.querySelector("a");

// 给ul标签节点绑定单击事件
aObj.onclick = function(event) {
alert("阻止超链接的默认行为哦");
event.returnValue; // 低版本浏览器 ie678 returnValue 属性可以阻止
}
</script>
</body>

3.4.4、案例4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<a href="https://www.baidu.com">跳转到百度</a>
<script>
// 阻止超链接的默认行为

var aObj = document.querySelector("a");

// 给ul标签节点绑定单击事件
aObj.onclick = function(event) {
alert("阻止超链接的默认行为哦");
return false; // 没有兼容性问题,但是只限于传统的注册方式
}
</script>
</body>

3.5、阻止事件冒泡

3.5.1、说明

1
2
1. 事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点。
2. 事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。

3.5.2、案例

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
<head>
<style>
#div1 {
width: 300px;
height: 300px;
background-color: red;
}

#div2 {
width: 200px;
height: 200px;
background-color: blue;
}

#div3 {
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
// 1. 获取div1
var div1 = document.querySelector("#div1");
div1.addEventListener('click', function() {
alert("div1");
});

// 2. 获取div2
var div2 = document.querySelector("#div2");
div2.addEventListener('click', function() {
alert("div2");
});

// 3. 获取div3
var div3 = document.querySelector("#div3");
div3.addEventListener('click', function(event) {
alert("div3");
event.stopPropagation(); // 阻止事件冒泡,标准方式
// event.cancelBubble = true; // 也可以阻止事件冒泡
});
</script>
</body>

四、事件委托/代理/委派

4.1、理解

​ 事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。生活中有如下场景:

咱们班有100个学生, 快递员有100个快递, 如果一个个的送花费时间较长。同时每个学生领取的时候,也需

要排队领取,也花费时间较长,何如办呢?

解决方案: 快递员把100个快递,委托给班主任,班主任把这些快递放到办公室,同学们下课自行领取即可。

优势: 快递员省事,委托给班主任就可以走了。 同学们领取也方便,因为相信班主任。

4.2、需求

1
2
3
4
5
<ul>
<li>高圆圆</li>
<li>贾静雯</li>
<li>赵敏</li>
</ul>

​ 点击每个 li 都会弹出对话框弹出每个li里面的内容。

​ 方案:给每个 li 注册点击事件,

​ 问题:麻烦,效率低下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<ul>
<li>高圆圆</li>
<li>贾静雯</li>
<li>赵敏</li>
</ul>

<script>
var liObjs = document.querySelectorAll("li");
for (var i = 0; i < liObjs.length; i++) {
liObjs[i].onclick = function() {
alert(this.innerHTML);
}
}
</script>
</body>

4.3、概述

1
2
3
4
5
6
7
8
# 事件委托
也称为事件代理, 在 jQuery 里面称为事件委派。
# 事件委托的原理
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
# 案例
给ul注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上,ul 有注册事件,就会触发事件监听器。
# 事件委托的作用
我们只操作了一次 DOM ,提高了程序的性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<ul>
<li>高圆圆</li>
<li>贾静雯</li>
<li>赵敏</li>
</ul>

<script>
// 获取ul
var ulObj = document.querySelector("ul");
// 绑定事件
ulObj.addEventListener("click", function(event) {
alert(event.target.innerHTML);
});
</script>
</body>

五、鼠标事件

5.1、常用的鼠标事件

0095

5.2、禁止鼠标右键菜单

1
2
3
4
5
6
7
8
9
10
<body>
<div>我是一段文字</div>

<script>
// 禁用右键菜单
document.addEventListener('contextmenu', function(event) {
event.preventDefault();
})
</script>
</body>

5.3、禁止鼠标选中

1
2
3
4
5
6
7
8
9
10
<body>
<div>我是一段文字</div>

<script>
// 禁止选中文字 selectstart
document.addEventListener('selectstart', function(event) {
event.preventDefault();
})
</script>
</body>

5.4、鼠标事件对象

0096

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<head>
<style>
body {
height: 3000px;
}
</style>
</head>
<body>
<script>
// 禁用右键菜单
document.addEventListener('click', function(event) {
// 1. client 鼠标在可视区的x和y坐标
console.log(event.clientX);
console.log(event.clientY);
console.log('---------------------');

// 2. page 鼠标在页面文档的x和y坐标
console.log(event.pageX);
console.log(event.pageY);
console.log('---------------------');
})
</script>
</body>

5.5、案例

​ 需求:图片一直跟随鼠标移动。

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
<head>
<style>
img {
position: absolute;
top: 2px;
}
</style>
</head>
<body>
<img src="img/angel.gif" alt="">
<script>
// 获取图片节点对象
var imgObj = document.querySelector("img");
// 绑定鼠标在屏幕上移动的事件
document.addEventListener('mousemove', function(event) {
//每次鼠标移动,我们都会获得最新的鼠标坐标,把这个x和y坐标做为图片的top和left值就可以移动图片
var x = event.pageX;
var y = event.pageY;

// 设置图片的top和left
imgObj.style.left = x - 50 + 'px';
imgObj.style.top = y - 40 + 'px';
});
</script>
</body>

六、键盘事件

6.1、常用的键盘事件

0097

注意:

  • 如果使用addEventListener不需要加on
  • onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等
  • 三个事件的执行顺序是: keydown — keypress —- keyup
1
2
3
4
5
6
7
<body>
<script>
document.addEventListener('keyup', function() {
console.log('我弹起了...');
});
</script>
</body>
1
2
3
4
5
6
7
8
<body>
<script>
// 不支持 不可打印字符的按键,比如说 shift,ctrl
document.addEventListener('keypress', function() {
console.log('keypress');
});
</script>
</body>
1
2
3
4
5
6
7
8
<body>
<script>
// 按键按下的时候触发 能识别功能键 比如 ctrl shift 左右箭头等
document.addEventListener('keydown', function() {
console.log('keydown');
});
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<body>
<script>
document.addEventListener('keydown', function() {
console.log('keydown');
});
document.addEventListener('keyup', function() {
console.log('keyup');
});
document.addEventListener('keypress', function() {
console.log('keypress');
});

// 总结: keydown -- keypress --- keyup
</script>
</body>

6.2、键盘事件对象属性

0098

1
# 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
1
2
3
4
5
6
7
8
9
<body>
<script>
document.addEventListener('keyup', function(event) {
if (event.keyCode == 13) {
alert('回车用户提交表单');
}
});
</script>
</body>

6.3、案例

​ 需求:当我们按下 s 键, 光标就定位到搜索框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
搜索: <input type="text" placeholder="请输入你要搜索的内容">
<script>
// 当我们按下 s 键, 光标就定位到搜索框

// 1、获取input
var inputObj = document.querySelector('input');

// 2、绑定按键事件
document.addEventListener('keyup', function(event) {
if (event.keyCode == 83) {
inputObj.focus();
}
});
</script>
</body>