原文: Understanding DOM Events and JavaScript Event Listeners

浏览器中的 JavaScript 代码使用事件驱动编程模式。这意味着当浏览器中发生特定的 DOM 事件时,将执行一段代码作为对该操作的响应。

在本文中,我将帮助你理解如何使用 JavaScript 监听和响应 DOM 事件。

如果你需要复习一下 DOM,我已经写了一篇文章来解释什么是 DOM 以及 JavaScript 如何与之交互

什么是 DOM 事件,它们为什么有用

DOM 事件是浏览器暴露的信号,你可以利用这些信号运行一段 JavaScript 代码。

这些 DOM 事件会在用户与我们创建的应用程序进行交互时发生,例如点击按钮或在输入框中输入字母。

作为 web 开发人员,你可以指示 JavaScript 监听特定事件,并对该事件做出响应。

例如:

  • 点击按钮时,更改段落文本。
  • 提交表单时,使用 Fetch API 发送 POST 请求。

如何监听 DOM 事件

要监听事件,需要使用 addEventListener() 方法将事件监听器附加到元素上。

addEventListener() 方法接受两个参数:

  • 要监听的事件 type
  • 事件触发时要运行的函数
Element.addEventListener(type, function);

回到示例,假设你想在点击按钮元素时更改段落文本,你可以这样做:

<body>
  <p id="myParagraph">This is an example paragraph</p>
  <button id="changeText">Change Text</button>
  <script>
    const button = document.querySelector('#changeText');

    function newText(event) {
      const p = document.querySelector('#myParagraph');
      p.innerText = 'The text has been changed';
    }

    button.addEventListener('click', newText);
  </script>
</body>

要在 HTML 文档中插入 JavaScript 代码,我们需要使用 script 标签,如上图所示。

使用 document.querySelector() 方法选择按钮元素,然后在该元素上调用 addEventListener() 方法,为按钮附加一个事件监听器。

首先,指定要监听的事件类型 type,在本例中为 click 事件。然后,指定该事件发生时要运行的函数。

在上面的代码中,当 click 事件被触发时,newText 函数将被执行。

事件监听器还将发送一个 event 对象,其中包含触发事件的相关信息。这就是为什么在上面的 newText 函数中有一个 event 参数。

你可以将事件记录到控制台,查看其详细信息:

function newText(event) {
  console.log(event);
}

如果你再次点击按钮,将得到以下输出结果:

Event object log

根据事件触发时要做的事情,你可能需要使用 event 对象中包含的信息。

在这里,我们要做的只是更改段落文本,因此不需要 event 对象。稍后,我们将在处理键盘事件时看到使用 event 对象的示例。

在浏览器中可以监听的事件有很多。以下是开发 web 应用时可能需要的一些最常见事件:

事件 触发事件的情况
click 当你按下并释放鼠标的主按钮时,用于追踪按钮和可点击的元素
mousemove 当你移动鼠标光标时
mouseover 当你将鼠标光标移动到某个元素上方时,类似于 CSS 的 hover 状态
mouseout 当你的鼠标光标移动出元素的边界时
dblclick 当你点击两次时
DOMContentLoaded 当 DOM 内容完全加载时
keydown 当你在键盘上按下一个键时
keyup 当你在键盘上释放一个键时
submit 当一个表单被提交时

如果你想阅读 DOM 事件类型的完整列表,可以访问此页面

DOM 事件分为多个类别。在此,我们将只介绍两个在项目中最常用的事件:键盘事件和鼠标事件。

键盘事件

对于键盘,你可以跟踪按下和松开键时分别运行的 keydownkeyup 事件。

举例说明,请在控制台运行以下代码:

document.addEventListener('keydown', event => {
  console.log(`A key is pressed: ${event.key}`);
});

document.addEventListener('keyup', event => {
  console.log(`A key is released: ${event.key}`);
});

运行上述代码后,慢慢按下键盘上的一个键,然后慢慢松开。

你应该会看到如下日志输出:

Keyboard events log

请注意 “keydown” 日志是如何在你按下按键时立即显示的,而 “keyup” 日志只有在你松开按键时才会显示。

键盘事件通常会附加到 document 对象而不是特定元素上,因为整个网站都应能监听到该事件。

鼠标事件

除了键盘事件,DOM 还提供了一种跟踪鼠标事件的方法。

最常见的鼠标事件有:

  • mousedown - 鼠标按钮被按下
  • mouseup - 鼠标按钮被释放
  • click - 点击事件
  • dblclick - 双击事件
  • mousemove - 当鼠标移动到元素上时
  • contextmenu - 打开上下文菜单时,例如单击鼠标右键时

同样,你可以直接在 document 对象中添加事件监听器来测试这些事件:

document.addEventListener('mousedown', event => {
  console.log(`The mouse is pressed`);
});

document.addEventListener('mouseup', event => {
  console.log(`The mouse is released`);
});

运行上面的代码,然后点击浏览器中的任意位置。你应该会看到 mousedownmouseup 事件分别被记录下来。

如何移除事件监听器

要移除附加到元素上的事件监听器,需要调用 removeEventListener() 方法,并传递事件的 type 和你传递给 addEventListener() 方法的 function,如下所示:

button.removeEventListener('click', newText);

上述代码可以从 button 元素中移除 “click” 事件监听器。请注意,你需要在元素上调用 removeEventListener() 方法,同时将函数 newText 传递给该方法。

要正确地移除事件监听器,需要为事件附加函数引用。如果向 addEventListener() 方法传递一个无名函数,就无法移除该事件:

button.addEventListener('click', function (event) {
  alert('Button save is clicked');
});

如果没有上面例子中的函数名,就无法移除事件监听器。

如何使用 HTML 属性监听事件

除了使用 addEventListener() 方法外,你还可以通过在 HTML 元素中添加 on[eventname] 属性来监听事件。

例如,假设你想监听按钮点击,你可以为按钮添加 onclick 属性,如下所示:

<body>
  <button onclick="handleClick()">Click Me!</button>
  <script>

    function handleClick(event) {
      alert('The button is clicked!');
    }
  </script>
</body>

在上面的按钮元素中,我们添加了 onclick 属性,并将 handleClick() 函数传递给它。

当我们点击按钮时,`handleClick()` 函数将被执行。

你也可以使用 JavaScript 添加 onclick 属性,如下所示:

<body>
  <button id="myBtn">Click Me!</button>
  <script>
    const myBtn = document.querySelector('#myBtn');
    myBtn.onclick = handleClick;

    function handleClick(event) {
      alert('The button is clicked!');
    }
  </script>
</body>

在这里,我们使用 JavaScript 将 handleClick 函数的引用赋值给 onclick 属性。

要删除 onclick 属性,可以将该属性赋值为空:

const myBtn = document.querySelector('#myBtn');
myBtn.onclick = null;

你应该使用哪一种

如你所见,有两种方法可以监听 DOM 事件:addEventListener() 方法和 on[eventname] HTML 属性。你应该使用哪一种呢?

答案是,当你需要更多扩展性时,可以使用 addEventListener() 方法,而当你希望事情简单化时,可以使用 on[eventname] 方法。

在开发 web 应用程序时,.html 文件只应作为页面的结构,而 .js 文件则应定义 web 应用程序的任何行为。

为了使应用程序更易于维护和扩展,JavaScript 应能访问 HTML 元素,但 HTML 元素不能执行 JavaScript 函数。这就是推荐使用 addEventListener() 方法的原因。

但是,addEventListener() 并不是没有代价的,这使得代码的阅读相当繁琐。

使用 on[eventname] 属性时,你只需在 HTML 元素中指定函数名称:

<body>
  <button onclick="handleClick()">Click Me!</button>
  <script>

    function handleClick(event) {
      alert('The button is clicked!');
    }
  </script>
</body>

但使用 addEventListener() 方法时,需要查询所需的元素,调用该方法,然后指定要运行的事件和回调函数:

<body>
  <button id="myBtn">Click Me!</button>
  <script>
    const myBtn = document.querySelector('#myBtn');
    myBtn.addEventListener('click', handleClick);

    function handleClick(event) {
      alert('The button is clicked!');
    }
  </script>
</body>

如上所示,在使用 on[eventname] 属性时,可以少写一些代码。

虽然这看起来无关紧要,但当你在大型应用程序中使用大量 HTML 和 JS 文件时,这可以是一件重要的事情。

此外,addEventListener() 方法还允许你为同一元素附加多个监听器,如下所示:

<body>
  <button id="myBtn">Click Me!</button>
  <script>
    const myBtn = document.querySelector('#myBtn');

    myBtn.addEventListener('click', handleClick);

    myBtn.addEventListener('click', handleClickTwo);

    function handleClick() {
      console.log('Run from handleClick function');
    }

    function handleClickTwo() {
      console.log('Run from handleClickTwo function');
    }
  </script>
</body>

点击上面的按钮时,JavaScript 将同时执行两个事件监听器。

而使用 onclick 属性则无法做到这一点,因为每次只能指定一个函数作为引用:

<body>
  <button id="myBtn">Click Me!</button>
  <script>
    const myBtn = document.querySelector('#myBtn');

    myBtn.onclick = handleClick;
      
    // 当你把一个新的函数赋给 onclick,
    // 旧的函数被覆盖

    myBtn.onclick = handleClickTwo;

    function handleClick() {
      console.log('Run from handleClick function');
    }

    function handleClickTwo() {
      console.log('Run from handleClickTwo function');
    }
  </script>
</body>

但我从未遇到过需要两次监听同一事件的情况,因此这一优势可能根本没有用处。

结语

通过浏览器暴露的 DOM 事件,你可以以适当的方式响应用户操作。

这种使用事件监听器来完成特定任务的模式被称为事件驱动编程,在使用 JavaScript 开发 web 应用程序时会经常用到这种模式。

有两种方法可以监听事件:使用 addEventListener() JavaScript 方法和 on[eventname] HTML 属性。这两种方法各有利弊,因此你最好都能熟悉。

如果你喜欢这篇文章,并希望将自己的 JavaScript 技能提高到一个新的水平,我建议你点击这里查看我的新书《Beginning Modern JavaScript》

beginning-js-cover

本书旨在让初学者轻松学习 JavaScript,让所有希望学习 JavaScript 的人都能轻松掌握。它以循序渐进的指南帮助你了解如何使用 JavaScript 创建动态 web 应用程序。

我在此承诺:你将真正感觉到自己了解自己在用 JavaScript 做什么。

下次见!