DOM 事件

事件被触发以通知代码可能影响代码执行的“有趣变化”。这些变化可能来自用户交互,例如使用鼠标或调整窗口大小,底层环境状态的变化(例如,低电量或来自操作系统的媒体事件),以及其他原因。

每个事件都由一个基于Event接口的对象表示,并且可能具有额外的自定义字段和/或函数以提供有关发生事件的信息。每个事件的文档都有一个表格(靠近顶部),其中包含指向关联事件接口的链接以及其他相关信息。不同事件类型的完整列表在Event > 基于 Event 的接口中给出。

本主题提供了一个索引,列出了您可能感兴趣的主要类型事件(动画、剪贴板、Worker 等),以及实现这些事件的主要类。

事件索引

事件类型 描述 文档
Animation

Web Animation API相关的事件。

用于响应动画状态的变化(例如,当动画开始或结束时)。

DocumentWindowHTMLElement上触发的动画事件。
异步数据获取

与数据获取相关的事件。

AbortSignalXMLHttpRequestFileReader上触发的事件。
Clipboard

Clipboard API相关的事件。

用于在内容被剪切、复制或粘贴时发出通知。

DocumentElementWindow上触发的事件。
组合

与组合相关的事件;“间接”输入文本(而不是使用常规键盘按键)。

例如,通过语音转文本引擎输入的文本,或者使用特殊的组合键修改键盘按键以表示其他语言中的新字符。

Element上触发的事件。
CSS 过渡

CSS 过渡相关的事件。

在 CSS 过渡开始、停止、取消等时提供通知事件。

DocumentHTMLElementWindow上触发的事件。
数据库

与数据库操作相关的事件:打开、关闭、事务、错误等。

IDBDatabaseIDBOpenDBRequestIDBRequestIDBTransaction上触发的事件。
DOM 变动

与文档对象模型 (DOM) 层次结构和节点修改相关的事件。

警告:变动事件已弃用。应改用变动观察器

拖放,滚轮

与使用HTML 拖放 API滚轮事件相关的事件。

拖放和滚轮事件源自鼠标事件。虽然它们在使用鼠标滚轮或拖放时触发,但也可以与其他适当的硬件一起使用。

Document上触发的拖放事件

Element上触发的滚轮事件

焦点

与元素获得和失去焦点相关的事件。

ElementWindow上触发的事件。
表单

与表单的构建、重置和提交相关的事件。

HTMLFormElement上触发的事件。
全屏

Fullscreen API相关的事件。

用于在全屏和窗口模式之间切换时以及在此转换过程中发生错误时发出通知。

DocumentElement上触发的事件。
Gamepad

Gamepad API相关的事件。

Window上触发的事件。
手势

建议使用触摸事件来实现手势。

DocumentElement上触发的事件。

此外,还有一些非标准手势事件

History

History API相关的事件。

Window上触发的事件。
HTML 元素内容显示管理

与显示或文本元素状态变化相关的事件。

HTMLDetailsElementHTMLDialogElementHTMLSlotElement上触发的事件。
输入

与 HTML 输入元素相关的事件,例如<input><select><textarea>

HTMLElementHTMLInputElement上触发的事件。
键盘

与使用键盘相关的事件。

用于在按键抬起、按下或仅仅被按下时发出通知。

DocumentElement上触发的事件。
文档加载/卸载

与文档加载和卸载相关的事件。

DocumentWindow上触发的事件。

清单

渐进式 Web 应用清单安装相关的事件。

Window上触发的事件。
媒体

与媒体使用相关的事件(包括Media Capture and Streams APIWeb Audio API画中画 API等)。

ScriptProcessorNodeHTMLMediaElementAudioTrackListAudioScheduledSourceNodeMediaRecorderMediaStreamMediaStreamTrackVideoTrackListHTMLTrackElementOfflineAudioContextTextTrackTextTrackListElement/audioElement/video上触发的事件。
消息

与窗口从另一个浏览上下文接收消息相关的事件。

Window上触发的事件。
鼠标

与使用计算机鼠标相关的事件。

用于在鼠标单击、双击、上下事件、右键单击、移入和移出元素、文本选择等时发出通知。

指针事件提供了一种与硬件无关的鼠标事件替代方案。拖放和滚轮事件源自鼠标事件。

Element上触发的鼠标事件
网络/连接

与获得和失去网络连接相关的事件。

Window上触发的事件。

NetworkInformationNetwork Information API)上触发的事件。

支付

Payment Request API相关的事件。

PaymentRequestPaymentResponse上触发的事件。

性能

与任何分组到性能 API中的性能相关规范相关的事件。

Performance上触发的事件。

指针

Pointer Events API相关的事件。

提供来自指向设备(包括鼠标、触摸、笔/手写笔)的硬件无关通知。

DocumentHTMLElement上触发的事件。
打印

与打印相关的事件。

Window上触发的事件。
Promise 拒绝

当任何 JavaScript promise 被拒绝时,发送到全局脚本上下文的事件。

Window上触发的事件。
套接字

WebSockets API相关的事件。

WebSocket上触发的事件。
SVG

与 SVG 图像相关的事件。

SVGElementSVGAnimationElementSVGGraphicsElement上触发的事件。

文本选择

Selection API中与文本选择相关的事件。

HTMLTextAreaElementHTMLInputElement上触发的事件 (selectionchange)。

Touch

Touch Events API相关的事件。

提供与触摸敏感屏幕交互(即使用手指或手写笔)的通知事件。与Force Touch API无关。

DocumentElement上触发的事件。
虚拟现实

WebXR Device API相关的事件。

警告:WebVR API(以及相关的Window事件)已弃用。

XRSystemXRSessionXRReferenceSpace上触发的事件。
RTC(实时通信)

WebRTC API相关的事件。

RTCDataChannelRTCDTMFSenderRTCIceTransportRTCPeerConnection上触发的事件。
服务器发送事件

服务器发送事件 API相关的事件。

EventSource上触发的事件。
语音

Web Speech API相关的事件。

SpeechSynthesisUtterance上触发的事件。
Worker

Web Workers APIService Worker APIBroadcast Channel APIChannel Messaging API相关的事件。

用于响应新消息和消息发送错误。Service worker 还可以接收其他事件通知,包括推送通知、用户点击显示的通知、推送订阅失效、内容索引中项目删除等。

ServiceWorkerGlobalScopeDedicatedWorkerGlobalScopeSharedWorkerGlobalScopeWorkerGlobalScopeWorkerBroadcastChannelMessagePort上触发的事件。

创建和分派事件

除了内置接口触发的事件之外,您还可以自己创建和分派 DOM 事件。此类事件通常被称为合成事件,与浏览器触发的事件相对。

创建自定义事件

事件可以使用Event构造函数创建,如下所示

js
const event = new Event("build");

// Listen for the event.
elem.addEventListener("build", (e) => {
  /* … */
});

// Dispatch the event.
elem.dispatchEvent(event);

此代码示例使用了EventTarget.dispatchEvent()方法。

添加自定义数据 – CustomEvent()

要向事件对象添加更多数据,存在CustomEvent接口,并且可以使用detail属性传递自定义数据。例如,事件可以按如下方式创建

js
const event = new CustomEvent("build", { detail: elem.dataset.time });

这将允许您在事件监听器中访问附加数据

js
function eventHandler(e) {
  console.log(`The time is: ${e.detail}`);
}

添加自定义数据 – 继承 Event

Event接口也可以被子类化。这对于重用、更复杂的自定义数据,甚至向事件添加方法都特别有用。

js
class BuildEvent extends Event {
  #buildTime;

  constructor(buildTime) {
    super("build");
    this.#buildTime = buildTime;
  }

  get buildTime() {
    return this.#buildTime;
  }
}

此代码示例定义了一个具有只读属性和固定事件类型的BuildEvent类。

事件可以按如下方式创建

js
const event = new BuildEvent(elem.dataset.time);

然后可以使用自定义属性在事件监听器中访问附加数据

js
function eventHandler(e) {
  console.log(`The time is: ${e.buildTime}`);
}

事件冒泡

通常希望从子元素触发事件并由祖先元素捕获它;可选地,您可以将数据包含在事件中

html
<form>
  <textarea></textarea>
</form>
js
const form = document.querySelector("form");
const textarea = document.querySelector("textarea");

// Create a new event, allow bubbling, and provide any data you want to pass to the "detail" property
const eventAwesome = new CustomEvent("awesome", {
  bubbles: true,
  detail: { text: () => textarea.value },
});

// The form element listens for the custom "awesome" event and then consoles the output of the passed text() method
form.addEventListener("awesome", (e) => console.log(e.detail.text()));

// As the user types, the textarea inside the form dispatches/triggers the event to fire, using itself as the starting point
textarea.addEventListener("input", (e) => e.target.dispatchEvent(eventAwesome));

动态创建和分派事件

元素可以监听尚未创建的事件

html
<form>
  <textarea></textarea>
</form>
js
const form = document.querySelector("form");
const textarea = document.querySelector("textarea");

form.addEventListener("awesome", (e) => console.log(e.detail.text()));

textarea.addEventListener("input", function () {
  // Create and dispatch/trigger an event on the fly
  // Note: Optionally, we've also leveraged the "function expression" (instead of the "arrow function expression") so "this" will represent the element
  this.dispatchEvent(
    new CustomEvent("awesome", {
      bubbles: true,
      detail: { text: () => textarea.value },
    }),
  );
});

触发内置事件

此示例演示了使用 DOM 方法模拟复选框上的点击(即以编程方式生成点击事件)。查看实际示例。

js
function simulateClick() {
  const event = new MouseEvent("click", {
    view: window,
    bubbles: true,
    cancelable: true,
  });
  const cb = document.getElementById("checkbox");
  const cancelled = !cb.dispatchEvent(event);

  if (cancelled) {
    // A handler called preventDefault.
    alert("cancelled");
  } else {
    // None of the handlers called preventDefault.
    alert("not cancelled");
  }
}

注册事件处理程序

有两种推荐的注册处理程序的方法。事件处理程序代码可以在事件触发时运行,方法是将其分配给目标元素相应的onevent属性,或者使用addEventListener()方法将处理程序注册为元素的监听器。在任何一种情况下,处理程序都将接收一个符合Event接口(或派生接口)的对象。主要区别在于可以使用事件监听器方法添加(或删除)多个事件处理程序。

警告:不建议使用 HTML onevent 属性设置事件处理程序的第三种方法!它们会使标记膨胀,降低可读性并使其更难调试。有关更多信息,请参阅内联事件处理程序

使用 onevent 属性

按照约定,触发事件的 JavaScript 对象具有相应的“onevent”属性(通过在事件名称前加上“on”来命名)。这些属性在事件触发时被调用以运行关联的处理程序代码,也可以由您自己的代码直接调用。

要设置事件处理程序代码,只需将其分配给适当的 onevent 属性即可。每个元素中的每个事件只能分配一个事件处理程序。如果需要,可以通过将另一个函数分配给同一属性来替换处理程序。

以下示例演示了如何使用onclick属性为click事件设置greet()函数。

js
const btn = document.querySelector("button");

function greet(event) {
  console.log("greet:", event);
}

btn.onclick = greet;

请注意,表示事件的对象作为第一个参数传递给事件处理程序。此事件对象实现或派生自Event接口。

EventTarget.addEventListener

在元素上设置事件处理程序最灵活的方法是使用EventTarget.addEventListener方法。这种方法允许为元素分配多个监听器,并允许在需要时使用EventTarget.removeEventListener移除监听器。

注意:添加和移除事件处理程序的能力允许您,例如,让同一个按钮在不同情况下执行不同的操作。此外,在更复杂的程序中,清理旧的/未使用的事件处理程序可以提高效率。

以下示例演示了如何将greet()函数设置为click事件的监听器/事件处理程序(如果需要,您可以使用匿名函数表达式而不是命名函数)。再次注意,事件作为第一个参数传递给事件处理程序。

js
const btn = document.querySelector("button");

function greet(event) {
  console.log("greet:", event);
}

btn.addEventListener("click", greet);

该方法还可以接受附加参数/选项来控制事件捕获和移除的方面。更多信息可以在EventTarget.addEventListener参考页面上找到。

使用 AbortSignal

一个值得注意的事件监听器功能是能够使用中止信号同时清理多个事件处理程序。

这是通过将相同的AbortSignal传递给所有希望一起移除的事件处理程序的addEventListener()调用来完成的。然后,您可以在拥有AbortSignal的控制器上调用abort(),它将移除所有使用该信号添加的事件处理程序。例如,要添加一个我们可以使用AbortSignal移除的事件处理程序

js
const controller = new AbortController();

btn.addEventListener(
  "click",
  (event) => {
    console.log("greet:", event);
  },
  { signal: controller.signal },
); // pass an AbortSignal to this handler

然后可以这样移除此事件处理程序

js
controller.abort(); // removes any/all event handlers associated with this controller

多个事件处理程序的交互

onevent IDL 属性(例如,element.onclick = ...)和 HTML onevent 内容属性(例如,<button onclick="...">)都指向同一个单一处理程序槽。HTML 在 JavaScript 访问同一元素之前加载,因此通常 JavaScript 会替换 HTML 中指定的内容。使用addEventListener()添加的处理程序是独立的。使用onevent不会移除或替换使用addEventListener()添加的监听器,反之亦然。

当事件被分派时,监听器分阶段调用。有两个阶段:捕获冒泡。在捕获阶段,事件从最高的祖先元素开始,沿着 DOM 树向下移动,直到到达目标。在冒泡阶段,事件沿相反方向移动。事件监听器默认在冒泡阶段监听,它们可以通过在addEventListener()中指定capture: true来在捕获阶段监听。在一个阶段内,监听器按照注册的顺序运行。onevent处理程序在第一次变为非空时注册;后来的重新分配只改变其回调,不改变其在顺序中的位置。

调用Event.stopPropagation()可以阻止在传播链中稍后调用其他元素的监听器。Event.stopImmediatePropagation()还可以阻止调用同一元素上剩余的监听器。

规范

规范
DOM
# events
HTML
# events-2

另见