三种事件监听方式,事件流,事件对象,EventTarget类,事件委托,鼠标事件,键盘事件,表单元素事件,Document事件,CSS事件
1. 认识事件(Event)
Web页面需要经常和用户进行交互,而交互的过程中可能想要捕捉这个交互的过程
- 比如用户点击了某个按钮、用户在输入框里面输入了某个文本、用户鼠标经过了某个位置
- 浏览器需要搭建一条JavaScript代码和事件之间的桥梁
- 当某个事件发生时,让JavaScript可以相应(执行某个函数),所以需要针对事件编写处理程序(handler)
如何进行事件监听呢?
- 事件监听方式一:在script中直接监听
- 事件监听方式二:通过元素的on来监听事件
- 事件监听方式三:通过EventTarget中的addEventListener来监听
2. 事件监听
2.1. 在script中直接监听
1 | <div onclick="console.log('被点击')"></div> |
2.2. 通过元素的on来监听事件
- 事件
唯一:同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
1 | btn.onclick = function(){ |
2.3. 通过addEventListener来监听
- 事件
不唯一:同一个元素同一个事件可以设置多个处理函数 - 调用格式
eventTarget.addEventListener(type,Listener,useCapture)- type:事件类型字符串,比如:click,mouseover
- listener:事件处理函数,事件发生时,会调用该监听函数
- useCapture:可选参数,是一个布尔值,默认是false
1 | btn.addEventListener("click",function(){}) |
3. 解绑事件
- 可以采用两种方法
- btn.onclick=null
- btn.removeEventListener(type,Listener,useCapture)
- 这里的事件处理函数不能使用匿名函数
4. 认识事件流
4.1. 认识事件流的由来
- 事实上对于事件有一个概念叫做事件流,为什么会产生事件流呢?
- 在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身
- 这是因为HTML元素是存在父子元素叠加层级的
- 比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的
- 所以,事件流就是页面中接收事件的顺序
1 | <div class="box"> |
4.2. 事件冒泡和事件捕获
- 默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序称之为
事件冒泡(Event Bubble) - 事实上,还有另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为
事件捕获(Event Capture)- 捕获:document -> html -> body -> span
- 冒泡:span -> body-> html -> document
- 为什么会产生两种不同的处理流呢?
- 这是因为早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题,但是他们采用了完全相反的事件流来对事件进行了传递
- IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式
- addEventListener(type,Listener,
useCapture)- 第三个参数如果是true,表示在捕获阶段调用事件处理程序
- 如果是false,或者不填,表示在冒泡阶段调用事件处理程序
- ⚠️注意
- js代码只能执行捕获或冒泡其中的一个阶段
- 如果同时有事件冒泡和时间捕获的监听,那么会优先监听到事件捕获
- 实际开发中少用事件捕获,更关注事件冒泡
- 有些事件没有冒泡,onblur,onmouseenter,onmouseleave
- js代码只能执行捕获或冒泡其中的一个阶段
4.3. 事件捕获和冒泡的过程

捕获阶段(Capturing phase)
- 事件(从 Window)向下走近元素
目标阶段(Target phase)
- 事件到达目标元素
冒泡阶段(Bubbling phase)
- 事件从元素上开始冒泡
事实上,可以通过event对象来获取当前的阶段
- eventPhase
5. 事件对象event
当一个事件发生时,就会有和这个事件相关的很多信息
- 比如事件的类型是什么,点击的是哪一个元素,点击的位置是哪里等等相关的信息
- 这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象
- 该对象提供了想要的一些属性,以及可以通过该对象进行某些操作
如何获取这个event对象呢?
- event对象会在传入的事件处理(event handler)函数回调时,被系统传入
- 可以在回调函数中拿到这个event对象
1
2
3
4span.addEventListener("click",function(event){
// PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
console.log("span被点击",event);
});
5.1. 常见的属性
1 | btn.onclick=function(e){ |
window.eventie678使用- e对象代表事件的状态,比如键盘按键的状态,鼠标的位置,鼠标按钮的状态
- e变量中存放了跟事件相关的一系列信息数据的集合
| 事件对象属性 | 说明 |
|---|---|
| this | 绑定事件的对象 |
| srcElement | 触发事件的对象,ie678,非标准 |
| type | 对象的类型,click,mouseover,…,不带on |
| target | 触发事件的对象,标准 |
| currentTarget | 当前处理事件的元素 |
| eventPhase | 事件所处的阶段:捕获1,冒泡3,目标2 |
| offsetX、offsetY | 事件发生在元素内的位置 |
| clientX、clientY | 事件发生在客户端内的位置 |
| pageX、pageY | 事件发生在客户端相对于document的位置,滚动情况下和client值不同 |
| screenX、screenY | 事件发生相对于屏幕的位置 |
5.2. 常见的方法
| 事件对象方法 | 说明 |
|---|---|
| preventDefault() | 阻止默认事件,标准 |
| stopPropagation() | 阻止冒泡,标准 |
| cancelBubble | 阻止冒泡,ie678 |
| returnValue | 阻止默认事件,ie678,非标准 |
兼容
1 | var btn = document.querySelector("button"); |
5.3. 事件处理中的this
在函数中,可以通过this来获取当前的发生元素
1
2
3span.addEventListener("click",function(event){
console.log(this === event.currentTarget);
});这是因为在浏览器内部,调用event handler是绑定到当前的target上的
6. EventTarget类
所有的节点、元素都继承自EventTarget
- 事实上Window也继承自EventTarget
那么这个EventTarget是什么呢?
- EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件
Window继承自EventTarget,所以会继承其中的属性和方法
- addEventListener:注册某个事件类型以及事件处理函数
- removeEventListener:移除某个事件类型以及事件处理函数
- dispatchEvent:派发某个事件类型到EventTarget上
1 | const clickHandler = function() { |
7. 事件委托
事件冒泡在某种情况下可以实现强大的事件处理模式 – 事件委托模式(event delegation)
那么这个模式是怎么样的呢?
- 因为当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击
- 并且可以通过event.target获取到当前监听的元素
案例:一个ul中存放多个li,点击某一个li会变成红色
方案一:监听每一个li的点击,并且做出相应
方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理
- 因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<style>
.active {
color: red;
}
</style>
<ul>
li*6{$}
</ul>
<script>
var ul = document.querySelector(".list");
var currentActive = null;
ul.addEventListener("click",function(event){
if (currentActive && event.target !== this) {
currentActive.classList.remove("active");
}
if (event.target !== this) {
event.target.classList.add("active");
currentActive = event.target;
}
});
</script>
事件委托的标记
- 某些事件委托可能需要对具体的子组件进行区分,这个时候可以使用data-*对其进行标记
- 比如多个按钮的点击,区分点击了哪一个按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<div class="list">
<div data-action = "new">新建</div>
<div data-action = "search">搜索</div>
<div data-action = "delete">删除</div>
<div>其他</div>
</div>
<script>
var list = document.querySelector(".list");
list.addEventListener("click",function(event){
var action = event.target.dataset.action;
switch(action) {
case "new":
console.log("新建");
break;
case "search":
console.log("搜索");
break;
case "delete":
console.log("删除");
break;
default:
console.log("action")
}
});
8. 常见的事件类型
8.1. 鼠标事件 MouseEvent
- 常见的鼠标事件(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)
| 属性 | 说明 |
|---|---|
| click | 当用户点击某个对象时调用的事件句柄 |
| contextmenu | 在用户点击鼠标右键打开上下文菜单时触发 |
| dbclick | 当用户双击某个对象时调用的事件句柄 |
| mousedown | 鼠标按下触发 |
| mouseup | 鼠标按键被松开触发 |
| mouseover | 鼠标移到某元素之上触发,会冒泡,影响父盒子 |
| mouseout | 鼠标从某元素移开触发,会冒泡,影响父盒子 |
| mouserenter | 鼠标移到某元素之上触发,不会冒泡,不影响父盒子 |
| mouserleave | 鼠标从某元素移开触发,不会冒泡,不影响父盒子 |
| mousemove | 鼠标移动触发 |
- mouseenter和mouseleave
- 不支持冒泡
- 进入子元素依然属于在该元素内,没有任何反应
- mouseover和mouseout
- 支持冒泡
- 进入元素的子元素时
- 先调用父元素的mouseout
- 再调用子元素的mouseover
- 因为支持冒泡,所以会将mouseover传递到父元素中
8.2. 键盘事件 keyEvent
| 属性 | 说明 |
|---|---|
| keyup | 某个键盘按键被松开时触发 |
| keydown | 某个键盘按键被按下时触发 |
| keypress | 某个键盘按键被按下时触发,不识别功能键,ctrl,shift,箭头等 |
执行顺序:keydown,keypress,keyup
- down事件先发生
- press发生在文本被输入
- up发生在文本输入完成
可以通过key和code来区分按下的键
- code:“按键代码”(“KeyA”,”ArrowLeft” 等),特定于键盘上按键的物理位置
- key:字符(“A”,”a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值)
案例:搜索
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var inputEl = document.querySelector("input");
// 搜索
inputEl.onkeyup = function(event) {
if(event.code === 'Enter') {
console.log("进行搜索功能", inputEl.value);
}
}
// 按键s - 搜索框进行自动聚焦
document.onkeyup = function(event) {
if(event.code === 'KeyS') {
inputEl.focus();
}
}
8.3. 表单元素事件
| 属性 | 说明 |
|---|---|
| change | 该事件在表单元素的内容改变时触发( input,keygen,select,textarea) |
| input | 元素获取用户输入时触发 |
| focus | 元素获取焦点时触发 |
| blur | 元素失去焦点时触发 |
| reset | 表单重置时触发 |
| submit | 表单提交时触发 |
8.4. window事件
| 属性 | 说明 |
|---|---|
| DOMContentLoaded | 浏览器已完全加载 HTML,并构建了 DOM 树,但像 img元素和样式表之类的外部资源可能尚未加载完成。ie9+支持 |
| load | 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等 |
| pageshow | 页面显示时触发,无论页面是否来自缓存 |
| resize | 调整窗口大小 |
| hashchange | 地址栏hash值改变 |
案例
1 | <div class="box">hello world</div> |
8.5. CSS事件
| 属性 | 说明 |
|---|---|
| transitionend | 当一个 CSS 动画完成时 |
| transitionstart | 当一个 CSS 动画开始时 |