在JavaScript中经常可以看到事件对象的使用,所以打算写一篇博客来记录下学习此知识点的过程。
什么是事件对象?
首先我们需要了解一下事件对象是什么,先来看下MDN给出的定义
Event 接口表示在 DOM 中出现的事件。
一些事件是由用户触发的,例如鼠标或键盘事件;而其他事件常由 API 生成,例如指示动画已经完成运行的事件,视频已被暂停等等。事件也可以通过脚本代码触发,例如对元素调用 HTMLElement.click() 方法,或者定义一些自定义事件,再使用 EventTarget.dispatchEvent() 方法将自定义事件派发往指定的目标(target)。------ MDN-Event
可以看到事件对象是一个对象,而这个对象记录了事件触发的时候的相关信息,比如:
- 触发鼠标点击事件的时候,事件对象记录了鼠标的位置相关的信息
- 触发键盘事件的时候,事件对象会记录下用户按下的对应的按键的相关信息
如何使用(获取)事件对象?
通过DOM LV2的方式添加绑定事件的时候,回调函数的第一个参数就是事件对象,一般可以将其命名为e
、event
javascript
document.querySelector('#test', function(e){
console.log(e);
});
事件流
事件流描述了页面接收事件的顺序。------《JavaScript高级程序设计》
假如页面上有一个元素,该元素触发事件的时候,首先水发生事件捕获 ,然后才是事件冒泡。

事件冒泡
当一个元素触发事件后,会依次向上调用所有父级元素的同名事件

事件冒泡是默认存在的
验证:
比如现在页面上有这几个元素,对应的CSS以及HTML如下
CSS:
css
#test1 {
width: 600px;
height: 600px;
background-color: red;
}
#test2 {
width: 400px;
height: 400px;
background-color: yellow;
}
#test3{
width: 200px;
height: 200px;
background-color: blue;
}
HTML:
html
<div id="test1">
<div id="test2">
<div id="test3"></div>
</div>
</div>

现在给分别给这几个元素添加点击事件
JS:
javascript
let test1 = document.querySelector('#test1');
let test2 = document.querySelector('#test2');
let test3 = document.querySelector('#test3');
test1.addEventListener('click',function(){
console.log('test1');
});
test2.addEventListener('click',function(){
console.log('test2');
});
test3.addEventListener('click',function(){
console.log('test3');
});
此时点击蓝色的部分(div#test3
),就会依次输出test3
,test2
,test1
的消息。
如果点击黄色的部分(div#test2
),就会依次输出test2
,test1
的消息。
如果点击红色的部分(div#test1
),就会只输出test1
的消息。
可以看到,结果符合事件冒泡的顺序div#test3
->div#test2
->div#test1
(从里到外)。
事件捕获
从DOM的根元素开始去执行对应的事件 (从外到里)

注:
实际上,所有浏览器都是从
window
对象开始捕获事件,而 DOM2 Events 规范规定的是从document
开始由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情 况下可以使用事件捕获。
相关内容详细请见------《JavaScript高级程序设计》
事件捕获需要写对应的代码才能开启,如何开启事件捕获如下:
javascript
document.querySelector('#test1').addEventListener(eventType, callBack, useCapture)
说明:
- 参数3
useCapture
接受一个布尔值,代表是否开启事件捕获 - 参数3为
true
的时候代表开启事件捕获(默认状态为false
)
❕注意 :默认DOM 0级,也就是element.onclick = function(){}
的写法,只有冒泡没有捕获。
验证:
针对刚才冒泡的js代码做如下修改,开启事件捕获
javascript
test1.addEventListener('click',function(){
console.log('test1');
}, true);
test2.addEventListener('click',function(){
console.log('test2');
}, true);
test3.addEventListener('click',function(){
console.log('test3');
}, true);
此时点击蓝色的部分(div#test3
),就会依次输出test1
,test2
,test3
的消息。
如果点击黄色的部分(div#test2
),就会依次输出test1
,test2
的消息。
如果点击红色的部分(div#test1
),就会只输出test1
的消息。
可以看到,结果符合事件捕获的顺序div#test1
->div#test2
->div#test3
(从外到里)。
阻止停止冒泡,捕获
如果只想把对应的事件限制在子元素内,不想触发父级的事件,那么旧需要阻止事件流动。
通过e.stopPropagation()
方法,就可以阻止事件冒泡
❕注:不推荐使用
event.cancelBubble
,该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。------ MDN-Event.cancelBubble
比如对于刚才的案例,如果指向要点击事件的触发只限制在子元素内,比如只想要div#test3
点击的时候,只输出test3
,那就可以加上这个方法。
对于事件捕获(不常用):
javascript
test1.addEventListener('click',function(e){
console.log('test1');
e.stopPropagation();
}, true);
test2.addEventListener('click',function(e){
// console.log('test2');
}, true);
test3.addEventListener('click',function(e){
console.log('test3');
// e.stopPropagation();
}, true);
加上这个方法后不管点击哪里,都只会输出test1
了。
对于事件冒泡:
javascript
test1.addEventListener('click',function(e){
console.log('test1');
});
test2.addEventListener('click',function(e){
console.log('test2');
});
test3.addEventListener('click',function(e){
console.log('test3');
e.stopPropagation();
});
现在点击div#test3
就只会输出test3
了。
阻止事件默认行为
如果不想要a标签点击直接跳转,表单点击不直接提交可以使用下面的方法
语法:
e.preventDefault()
验证:
HTML:
html
<form action="https://cn.bing.com/" method="post">
<button>click</button>
</form>
<a href="https://cn.bing.com/" script="preventDefault()">link</a>
JS:
javascript
let btn = document.querySelector('button');
let a = document.querySelector('a');
btn.addEventListener('click', function(e){
// 阻止表单提交
e.preventDefault();
});
a.addEventListener('click', function(e){
// 阻止链接点击默认跳转
e.preventDefault();
})
事件代理(委托)
可以利用事件冒泡的特性,可以只使用一个事件处理程序来管理一种类型的事件。------《JavaScript高级程序设计》
给所有元素共同的祖先节点添加一个事件处理程序(可以提高性能)
使用:通过事件对象的target属性(e.target
),可以得到事件真正的触发者。
使用场景:
html
<style>
ul .active {
color: red;
border: 1px solid rgb(255, 28, 28);
}
</style>
<div class="header">
<ul>
<li>第1个</li>
<li>第2个</li>
<li>第3个</li>
<li>第4个</li>
<li>第5个</li>
<li>第6个</li>
</ul>
</div>
比如页面上有这样一个tag栏,想要点击的时候对应的tag栏变色的效果,这个时候正常情况下可以使用,for循环的形式依次为每个li
元素添加点击事件:
javascript
// 获取所有li元素组成的NodeList
let lis = document.querySelectorAll('li');
// 给每个NodeList添加点击事件
lis.forEach((item) => {
item.addEventListener('click', function(){
// 去除页面上已经激活的li样式
if (document.querySelector('.active')) {
document.querySelector('.active').classList.remove('active');
}
// 激活当前li的样式
this.classList.add('active');
});
});
同样的使用事件代理也可以解决问题,并且可以提高网页性能,不需要在使用循环的形式来给每个元素做绑定事件了,对应的JS代码如下:
javascript
// 事件代理
let ul = document.querySelector('ul');
ul.addEventListener('click', function (e) {
// 如果点击的目标元素是li标签则进行样式修改
if (e.target.tagName == 'LI') {
// 去除页面上已经激活的li样式
if (document.querySelector('.active')) {
document.querySelector('.active').classList.remove('active');
}
// 激活当前li的样式
e.target.classList.add('active');
}
});
🤖了解 :e.currentTarget
返回事件的监听者(事件绑定的对象)。
事件对象中的常用属性
获取事件事件触发类型
type
:获取当前触发的事件的类型(只读)
获取当前坐标的属性(点击事件)
offsetX/offsetY
:返回的是点击时候相对点击的元素左上角的相对位置(包含外边距)的坐标。pageX/pageY
:返回的是整个文档的坐标,如果此时页面比较长可以获取文档中鼠标的坐标。clientX/clientY
:返回的是鼠标点击的时候光标浏览器可视范围相对左上角的坐标。screenX/screenY
:返回的是鼠标点击的时候光标相对屏幕左上角的坐标。layerX/layerY
:往上找有定位属性的父元素的左上角(自身有定位属性的话就是相对于自身),都没有的话,就是相对于body的左上角的坐标。
PS:有关于这块的内容可以参考笔者写的另一篇文章------> 链接在这里
获取按键的属性(键盘事件)
key
:获取用户按下的物理按键的值(只读)keyCode
(不建议使用):返回按键对应的编号。(注:该功能已从Web标准中删除,尽管一些浏览器可能仍然支持它,但它正在被丢弃。)
target属性与currentTarget属性
target
:返回事件真正的触发者currentTarget
:返回事件的监听者(事件绑定的对象)
eventPhase属性
-
eventPhase
:表示事件流当前处于哪一个阶段。返回值 含义 0 此时没有事件在处理 1 事件处于捕获阶段 2 事件已经到达触发者 3 事件处于冒泡阶段
测试:
1、事件冒泡
HTML:
javascript
<div id="test3"></div>
CSS:
css
#test3{
width: 200px;
height: 200px;
background-color: blue;
}
JS:
javascript
let test3 = document.querySelector('#test3');
test3.addEventListener('click',function(e){
console.log('div e.eventPhase:' + e.eventPhase);
});
document.addEventListener('click', function(e){
console.log('doc eventPhase:' + e.eventPhase);
});
点击div#test3
依次输出
div e.eventPhase:2
doc eventPhase:3
2、事件捕获
对上面的代码做一点修改
javascript
let test3 = document.querySelector('#test3');
test3.addEventListener('click',function(e){
console.log('div e.eventPhase:' + e.eventPhase);
});
document.addEventListener('click', function(e){
console.log('doc eventPhase:' + e.eventPhase);
}, true);
点击div#test3
依次输出
-
doc eventPhase: 1
-
div e.eventPhase: 2
button属性
e.button
:MouseEvent.button
是只读属性,它返回一个值,代表用户按下并触发了事件的鼠标按键。
返回值 | 含义 |
---|---|
0 | 鼠标左键 |
1 | 鼠标中键 |
2 | 鼠标右键 |
测试代码:
javascript
let test3 = document.querySelector('#test3');
test3.addEventListener('mouseup',function(e){
if (e.button == 2) {
console.log('click the right button');
} else if (e.button == 0){
console.log('click the left button');
} else if (e.button == 1){
console.log('click the middle button');
} else {
console.log('????');
}
});
参考文献
-
《JavaScript高级程序设计》(第四版)