概念
客户端JavaScript程序使用异步事件驱动的编程模型。
浏览器会在文档、浏览器或某些元素或与之关联的对象发生某些事情时生成事件对象。比如文档加载完成、敲击键盘输入等。
JavaScript程序可以给某些对象绑定监听器函数来监听特定的事件,在该对象上发生指定事件时,这些函数会被执行。
在浏览器,事件可以发生在DOM元素对象、Window对象与Document对象等对象上面。
基于事件的编程有以下概念:
- 事件类型: 一个字符串,表示发生了什么事。
- 事件目标: 一个对象,表示事件发生的位置。
- 事件监听器: 也叫事件处理程序,用于响应或处理事件
- 事件对象:与特定事件关联的对象,包含该事件的细节。如type与target属性表示事件的类型与事件目标。
- 事件传播: 是一个过程。有“捕获”与“冒泡”两种方式和"捕获","目标","冒泡"三个阶段。
事件类型
浏览器事件太多太多了,但是可以按照其共性分为几个类别:
- 设备相关的输入事件:
与如键盘、鼠标、触屏等特定设备相关的输入事件。如keydown,mouseup,touchdown等 - 设备无关的输入事件:
可由多种设备触发的事件,与某一特定设备无关。如click由鼠标或触屏触发,input可以由鼠标、键盘与触屏触发。 - 用户界面事件:
UI事件是高级事件,一般有表单元素触发。如focus由输入元素获得焦点时触发,change在输入元素的输入内容改变是触发,submit在表单提交时触发。 - 状态变化事件:
不由用户的主动行为触发,而是由网络或浏览器的活动触发。比如文档加载完毕时,触发Document对象的DOMContentLoaded;网络状态变化时触发Window对象的online与offline。 - API特定事件:
HTML及其相关规范定义的WebAPI包含了自己的事件,比如<video>与<audio>标签有waiting,playing,seeking等事件。XMLHttpRequest与IndexdDB都有自己的事件。
事件传播
两种方式:
- 捕获:从Window对象开始到Document对象,再沿DOM树从上到下,直到目标元素。
- 冒泡:与捕获相反,从目标对象开始向上传播,指到Window对象。
三个阶段:
- 捕获阶段: 事件从Window对象开始向下传播,直到目标元素的父元素上,过程中的对象上注册为
捕获事件监听器
的监听器被触发。该阶段目标对象的监听器不会触发。 - 目标阶段: 事件传播到目标元素,开始执行事件监听器,此阶段不分监听器的类型是捕获还是冒泡,按照它们的注册顺序执行。
- 冒泡阶段: 目标元素执行完自身的处理程序后,事件开始向上传播,过程中的对象上注册为
冒泡事件监听器
的监听器被触发。
注册事件监听器
有三种形式:
- 作为对象的特定属性
element.onclick = function(event) {};
- 作为HTML标签的属性
<!-- 使用js代码 -->
<button onclick="console.log(1)">Click</button>
<!-- 使用js方法名,该方法需要在js代码中有定义 -->
<button onclick="clickHandler">Click</button>
- 使用EventTarget.addEventListener()方法
上面的方法只能注册一个事件处理程序,而addEventListener()方法能注册多个监听器。
它接收三个参数:
- type: 字符串,表示事件类型
- listener: 一个实现了 EventListener 接口的对象,或者是一个函数。
- useCapture / options: 可选的一个布尔值或对象。
- 布尔值: 是否注册为捕获事件监听器,默认为冒泡事件监听器
- 对象: 有以下可选的字段:
- capture: 布尔类型。为true将函数注册为捕获事件监听器,未指定该字段或false为冒泡事件监听器
- once: 布尔类型。为 true,listener 会在其被调用一次之后自动移除。
- passive: 布尔类型。为 true,表示 listener 永远不会调用 preventDefault()来取消事件的默认行为。若仍然调用该方法,会在控制台抛出警告。
监听器函数接收一个Event对象作为唯一的参数。
let btn = document.querySelector('button');
btn.addEventListener('click', function(event) {}, false);
btn.addEventListener('click', function(event) {}, {capture: true});
取消事件与阻止事件传播
Event接口的preventDefault()
方法可以取消事件的默认行为,比如阻止输入元素输出内容。但对于passive为true的事件,该方法不能起作用。
Event 接口的 stopPropagation()
方法阻止捕获和冒泡阶段中当前事件的进一步传播,但是它不能取消事件的默认行为,也不能阻止同一元素上与该事件相关的其他监听器的触发。
Event 接口的 stopImmediatePropagation()
方法除了阻止事件的继续传播,还阻止监听同一事件的其他事件监听器被调用。
事件监听器的上下文
作为事件监听器的函数,若以function关键字定义,则其this值指向注册监听器的目标对象;
若以箭头函数定义,其this值则指向函数被定义时其作用域的this值。
监听器的返回值
监听器不应该有返回值。
虽然返回false,可以告诉浏览器取消事件的默认行为,但是标准的做法是使用preventDefault()
方法。
事件对象
事件对象有以下几个常用的属性:
- type: 字符串,事件类型。
- target: 事件发生的对象
- currentTarget: 注册事件监听器的对象
- timeStamp: 事件发生时间的毫秒级时间戳
- isTrusted: 表示事件是由浏览器(例如用户点击)发起的;false表示由脚本(使用事件创建方法,例如 Event.initEvent)发出的。
- 更多属性: Event#属性
自定义事件
使用EventTarget接口 的 dispatchEvent() 方法将一个由CustomEvent()
构造函数创建的自定义事件派发给目标对象。
CustomEvent()构造函数接收的第一个参数是一个表示事件类型的字符串,第二个参数是一个描述事件的对象。
该对象有以下可选的字段:
- detail: 任意类型的值,默认为null,表示事件的上下文。
- bubble: 为true表示事件可以冒泡。默认情况下事件只能捕获。
- cancelable: 布尔值,表示事件是否能被取消。