原文: 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
对象中包含的信息。
在这里,我们要做的只是更改段落文本,因此不需要 event
对象。稍后,我们将在处理键盘事件时看到使用 event
对象的示例。
在浏览器中可以监听的事件有很多。以下是开发 web 应用时可能需要的一些最常见事件:
事件 | 触发事件的情况 |
---|---|
click | 当你按下并释放鼠标的主按钮时,用于追踪按钮和可点击的元素 |
mousemove | 当你移动鼠标光标时 |
mouseover | 当你将鼠标光标移动到某个元素上方时,类似于 CSS 的 hover 状态 |
mouseout | 当你的鼠标光标移动出元素的边界时 |
dblclick | 当你点击两次时 |
DOMContentLoaded | 当 DOM 内容完全加载时 |
keydown | 当你在键盘上按下一个键时 |
keyup | 当你在键盘上释放一个键时 |
submit | 当一个表单被提交时 |
如果你想阅读 DOM 事件类型的完整列表,可以访问此页面。
DOM 事件分为多个类别。在此,我们将只介绍两个在项目中最常用的事件:键盘事件和鼠标事件。
键盘事件
对于键盘,你可以跟踪按下和松开键时分别运行的 keydown
和 keyup
事件。
举例说明,请在控制台运行以下代码:
document.addEventListener('keydown', event => {
console.log(`A key is pressed: ${event.key}`);
});
document.addEventListener('keyup', event => {
console.log(`A key is released: ${event.key}`);
});
运行上述代码后,慢慢按下键盘上的一个键,然后慢慢松开。
你应该会看到如下日志输出:
请注意 “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`);
});
运行上面的代码,然后点击浏览器中的任意位置。你应该会看到 mousedown
和 mouseup
事件分别被记录下来。
如何移除事件监听器
要移除附加到元素上的事件监听器,需要调用 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 属性。这两种方法各有利弊,因此你最好都能熟悉。