# 定义

事件就是用户或浏览器在某一时刻执行的某种动作,JavaScript 与 HTML 的交互是通过事件实现的。是一种内置在浏览器中的 API。

# 事件流

DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获到达目标事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个阶段响应事件。

在 DOM 事件流中,实际的目标( <div>元素)在捕获阶段不会接收到事件。这是因为捕获阶段从 document 到<html>再到<body>就结束了。下一阶段,即会在<div>元素上触发事件的“到达目标”阶段,通常在事件处理时被认为是冒泡阶段的一部分。

# 事件处理程序

为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程序的名字以"on"开头。

事件的三种使用方式:

  • 行内事件处理器
<div onclick="console.log('Clicked')"></div>
1
  • DOM0 级事件处理程序

把一个函数赋值给(DOM 元素的)一个事件处理程序属性。

let btn = document.getElementById("myBtn");
btn.onclick = function() {
  console.log("Clicked");
};
1
2
3
4
  • DOM2 级事件处理程序

DOM2 Events 为事件处理程序的赋值和移除定义了两个方法: addEventListener()removeEventListener()。这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值, true 表示在捕获阶段调用事件处理程序, false(默认值)表示在冒泡阶段调用事件处理程序。

注意:DOM2 级的事件名称不是以 on 开头的!

let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
  console.log(this.id);
}, false);
1
2
3
4

优点:可绑定多个事件。

# 事件对象

在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。event 对象是传给事件处理程序的唯一参数。不管以哪种方式使用事件,都会传入这个 event 对象。

在事件处理程序内部, this 对象始终等于 currentTarget 的值,表示注册事件的元素,而 target 表示事件的实际目标。

举例:我们在 body 元素上绑定一个 click 事件,然后点击文档中的一个按钮 myBtn:

document.body.onclick = function(event) {
  console.log(event.currentTarget === document.body); // true
  console.log(this === document.body); // true
  console.log(event.target === document.getElementById("myBtn")); // true
};
1
2
3
4
5

# 阻止默认行为

preventDefault()方法用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL。如果想阻止这个导航行为,可以在 onclick 事件处理程序中取消。

let link = document.getElementById("myLink");
link.onclick = function(event) {
  event.preventDefault();
};
1
2
3
4

# 阻止冒泡

stopPropagation()方法用于立即阻止事件流在 DOM 结构中传播,取消后续的事件捕获或冒泡。例如,直接添加到按钮的事件处理程序中调用 stopPropagation(),可以阻止 document.body 上注册的事件处理程序执行。

let btn = document.getElementById("myBtn");
btn.onclick = function(event) {
  console.log("Clicked");
event.stopPropagation();
};
document.body.onclick = function(event) {
  console.log("Body clicked"); // 不执行
};
1
2
3
4
5
6
7
8

# 事件委托

冒泡的特点可以让我们使用事件委托来优化性能。

一个很好的例子是一个列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将 click 单击事件绑定在父元素<ul>上,这样事件就会从列表项冒泡到其父元素<ul>上,而不必再为每个子节点单独绑定事件。

# 事件类型

事件的类型决定了事件对象 event 中会保存什么信息。 DOM3 Events 定义了如下事件类型:

  • 用户界面事件 (UIEvent):涉及与 BOM 交互的通用浏览器事件。
  • 焦点事件 (FocusEvent):在元素获得和失去焦点时触发。
  • 鼠标事件 (MouseEvent):使用鼠标在页面上执行某些操作时触发。
  • 滚轮事件 (WheelEvent):使用鼠标滚轮(或类似设备)时触发。
  • 输入事件 (InputEvent):向文档中输入文本时触发。
  • 键盘事件 (KeyboardEvent):使用键盘在页面上执行某些操作时触发。
  • 合成事件 ( CompositionEvent):在使用某种 IME(Input Method Editor,输入法编辑器)输入字符时触发。

不同类型的事件都有一个事件对象,绑定了一些特有的属性和方法,但是都是继承自 Event 对象,下面的列表展示了他们之间的关系:

  • Event
    • UIEvent
      • MouseEvent
      • TouchEvent
      • FocusEvent
      • KeyboardEvent
      • WheelEvent
      • InputEvent
      • CompositionEvent
    • MessageEvent
    • SVGEvent
    • ...(全部事件列表可参考 MDN (opens new window)

我们可以尝试给页面上的一个 input 元素绑定一个 focus 事件,打印一下 event 对象:

bubbles: false
cancelBubble: false
cancelable: false
type: "focus"
...
[[Prototype]]: FocusEvent
  constructor: FocusEvent()
  bubbles: false
  cancelBubble: false
  cancelable: false
  ...
  [[Prototype]]: UIEvent
    constructor: UIEvent()
    view: Window
    which: 0
    ...
    [[Prototype]]: Event
      constructor: Event()
      bubbles: false
      cancelBubble: false
      cancelable: false
      currentTarget: null
      stopPropagation: stopPropagation()
      preventDefault: preventDefault()
      ...
      [[Prototype]]: Object
        constructor: Object()
        hasOwnProperty: hasOwnProperty()
        isPrototypeOf: isPrototypeOf()
        ...
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

# 常见事件类型

todo

# 不常见和非标准事件

todo

# GlobalEventHandlers

事件处理程序这一节我们有讲到 DOM 0 事件处理程序,这里的事件名称都以on开头,跟我们上面讲到的不带on的事件并不是一回事。不带on的 events 是在 DOM 2 规范中确认下来的,而以on开头的事件们是在 DOM 规范出现之前就有的。

现在这些以on开头的事件们被放在一个叫做 GlobalEventHandlers 的集合中,并被混入和实现在 HTMLElement, Document, Window 对象上。它不是一个构造函数,不能直接创建实例,也不在 DOM 对象的原型链中,完全是为了兼容 DOM 0 时候的写法。

下面列举一些常见的on开头的事件:

# onerror

error 事件发生时,就会调用 onerror 属性指定的回调函数。

error 事件分成两种。

一种是 JavaScript 的运行时错误,这会传到 window 对象,导致 window.onerror()

window.onerror = function (message, source, lineno, colno, error) {
  // ...
}
1
2
3

另一种是资源加载错误,比如<img><script>加载的资源出现加载错误。这时,Error 对象会传到对应的元素,导致该元素的 onerror 属性开始执行。

element.onerror = function (event) {
  // ...
}
1
2
3

# onload、onloadstart

元素完成加载时,会触发 load 事件,执行onload()。它的典型使用场景是 window 对象和<img>元素。对于 window 对象来说,只有页面的所有资源加载完成(包括图片、脚本、样式表、字体等所有外部资源),才会触发 load 事件。

以下元素加载完成时都会触发 onload 事件:

<frame>, <frameset>, <iframe>, 
<body>, <link>, <script>, <style>
<img>, <input type="image">, 
1
2
3

对于<img><video>等元素,加载开始时还会触发 loadstart 事件,导致执行 onloadstart。

# onfocus,onblur

元素获得焦点、失去焦点时触发的事件。

注意,如果不是可以接受用户输入的元素,要触发 onfocus,该元素必须有 tabindex 属性。

# onscroll

页面或元素滚动时,会触发 scroll 事件,导致执行onscroll()

# oncontextmenu,onshow

用户在页面上按下鼠标的右键,会触发 contextmenu 事件,导致执行 oncontextmenu()。如果该属性执行后返回 false,就等于禁止了右键菜单。document.oncontextmenu 与 window.oncontextmenu 效果一样。

document.oncontextmenu = function () {
  return false;
};
1
2
3

上面代码中,oncontextmenu 属性执行后返回 false,右键菜单就不会出现。

元素的右键菜单显示时,会触发该元素的 onshow 监听函数。

上次更新: 7/25/2022, 10:16:34 AM