事件
---
title: 事件
updated: 2022-11-15T15:18:03
created: 2022-10-24T21:54:56
---
MDN中事件的详细介绍:事件
事件是您在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,您可以某种方式对事件做出回应。例如:如果用户在网页上单击一个按钮,您可能想通过显示一个信息框来响应这个动作。
事件是您在编程时系统内发生的动作或者发生的事情——系统会在事件出现时产生或触发某种信号,并且会提供一个自动加载某种动作(例如:运行一些代码)的机制。
每个可用的事件都会有一个事件处理器,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们注册了一个事件处理器。注意事件处理器有时候被叫做事件监听器——从我们的用意来看这两个名字是相同的,尽管严格地来说这块代码既监听也处理事件。监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。
网络事件不是 JavaScript 语言的核心——它们被定义成内置于浏览器的 JavaScript APIs。
const btn = document.querySelector('button');
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
// btn.onclick类似于btn.textContent
btn.onclick = function() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
1. 事件处理程序属性(DOM 0级事件处理程序)
有许多的事件处理参数,将 btn.onclick
依次换成其他值,在浏览器中观察效果:
- btn.onfocus及btn.onblur — 颜色将于按钮被置于焦点或解除焦点时改变(尝试使用 Tab 移动至按钮上,然后再移开)。这些通常用于显示有关如何在置于焦点时填写表单字段的信息,或者如果表单字段刚刚填入不正确的值,则显示错误消息。(Tab键触发效果)
- btn.ondblclick — 颜色将仅于按钮被双击时改变。(双击触发效果)
- window.onkeypress, window.onkeydown, window.onkeyup — 当按钮被按下时颜色会发生改变。keypress 指的是通俗意义上的按下按钮 (按下并松开),而 keydown 和 keyup 指的是按键动作的一部分,分别指按下和松开。注意如果你将事件处理器添加到按钮本身,它将不会工作 — 我们只能将它添加到代表整个浏览器窗口的 window对象中。
- btn.onmouseover (鼠标碰到触发)和 btn.onmouseout (鼠标碰到按钮时移开触发) — 颜色将会在鼠标移入按钮上方时发生改变,或者当它从按钮移出时。
如果你需要对多个按钮添加事件处理器,可使用:
const buttons = document.querySelectorAll('button');
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = bgChange;
}
2. DOM Level 2 Events(DOM 2级事件处理程序)
有一种新的事件触发机制,与onclick
类似的,它提供了一个函数,addEventListener()
。
btn.addEventListener('click', bgChange);
与 btn.onclick=bgChange;
效果类似。
addEventListener()
有一个相对应的方法removeEventListener()
,用于移除事件监听器,在大型,复杂的项目中,是非常有用的,以及其他一些场景,比如需要在不同环境下运行不同的事件处理器,您只需要恰当地删除或者添加事件处理器即可。
3. 其他事件处理机制
还有两种处理机制,其中一种为内联事件处理器,在HTML中添加事件处理器,已被淘汰。另一种为IE事件处理程序。
1和2这两种机制是相对可互换的,至少对于简单的用途:
- 事件处理程序属性功能和选项会更少,但是具有更好的跨浏览器兼容性 (在 Internet Explorer 8 的支持下),您应该从这些开始学起。
- DOM Level 2 Events (
addEventListener()
, etc.) 更强大,但也可以变得更加复杂,并且支持不足(只支持到 Internet Explorer 9)。但是您也应该尝试这个方法,并尽可能地使用它们。主要优点是,如果需要的话,可以使用removeEventListener()
删除事件处理程序代码,而且如果有需要,可以向同一类型的元素添加多个监听器。
事件概念
1. 事件对象
有时候在事件处理函数内部,可能会看到一个固定指定名称的参数,例如event
,evt
或简单的e
。这被称为事件对象,它被自动传递给事件处理函数,以提供额外的功能和信息。
function bgChange(e) {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
// e.target指的是按钮本身,事件对象的 e 的 target 属性始终是事件刚刚发生的元素的引用
e.target.style.backgroundColor = rndCol;
console.log(e);
}
btn.addEventListener('click', bgChange);
2. 阻止默认行为
有些事件会具有一些默认的行为,例如自定义注册web表单,当你点击提交按钮时,默认行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种类似于'成功注册'的页面。
当用户没有正确提交数据时,麻烦就来了 - 作为开发人员,你希望停止提交信息给服务器,并给他们一个错误提示,告诉他们什么做错了,以及需要做些什么来修正错误。一些浏览器支持自动的表单数据验证功能,但由于许多浏览器不支持,因此建议你不要依赖这些功能,并实现自己的验证检查,这就需要用到事件了。
3. 事件冒泡及捕获
在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。
捕获阶段
- 浏览器检查元素的最外层祖先
<html>
,是否在捕获阶段中注册了一个onclick
事件处理程序,如果是,则运行它。 - 然后,它移动到
<html>
中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。
document
--> <html>
--> <body>
--> <div>
冒泡阶段
- 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个
onclick
事件处理程序,如果是,则运行它 - 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达
<html>
元素。
<div>
--> <body>
--> <html>
--> document
DOM事件流
按照W3C的标准,先发生捕获事件,后发生冒泡事件。如果一个元素已经执行了有两个同样的事件,但一个是捕获,一个是冒泡,只执行捕获事件。
father.addEventListener('click', function() { console.log('father捕获') }, true);
father.addEventListener('click', function() { console.log('father冒泡') }, false);
son.addEventListener('click', function() { console.log('son捕获') }, true);
son.addEventListener('click', function() { console.log('son冒泡') }, false);
// 输出顺序:father捕获 -> son捕获 -> son冒泡 -> father冒泡
修复冒泡问题
在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。
<button>播放视频</button>
<div class="hidden">
<video>
<source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.mp4" type="video/mp4">
<source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.webm" type="video/webm">
<p>Your browser doesn't support HTML video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
</div>
<script>
const btn = document.querySelector('button');
const videoBox = document.querySelector('div');
function displayVideo() {
if (videoBox.getAttribute('class') === 'hidden') {
videoBox.setAttribute('class', 'showing');
}
}
// 若div class=hidden,点击class=showing
btn.addEventListener('click', displayVideo);
// <div class='hidden'> 停止播放,video被隐藏
videoBox.addEventListener('click', () => videoBox.setAttribute('class', 'hidden'));
const video = document.querySelector('video');
video.addEventListener('click', (e) => video.play());
</script>
沿着这个事件冒泡线路:
- 它发现了
(video) video.onclick...
事件处理器并且运行它,因此这个视频第一次开始播放。 - 接着它发现了(往外冒泡找到的)
(div) videoBox.onclick...
事件处理器并且运行它,因此这个视频<video>
也隐藏起来了。
现在我们要修复它,标准事件对象具有可用的名为 stopPropagation()的函数,当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在冒泡链上进一步扩大,因此将不会有更多事件处理器被运行 (不会向上冒泡)。将上述代码框中第26行改为以下代码:
video.addEventListener('click', (e) => {
e.stopPropagation();
video.play();
});
4. 事件委托
事件冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。
一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将click
单击事件监听器设置在父元素<ul>
上,这样事件就会从列表项冒泡到其父元素<ul>
上。
<body>
<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
</ul>
<script>
const parent = document.getElementById('parent-list');
parent.addEventListener('click', function(e) {
const target = e.target;
if (target.tagName === 'LI') {
console.log(target.id);
}
});
</script>
</body>
事件委托的使用原因/性能内存问题
在JavaScript中,添加到页面的事件处理程序数量会关系到页面的整体运行性能。两个方面:
- 每个函数都是对象,会占用内存,内存中对象越多,性能越差。
- 大量事件处理程序导致的DOM访问次数会延迟整个页面的交互就绪时间。
问题
Q1:在事件处理程序的冒泡阶段和捕获阶段,事件处理程序会执行吗?
A:事件处理程序会在捕获阶段和冒泡阶段执行,只要事件处理程序在该阶段注册。
Q2:我可以在同一个元素上同时注册多个相同类型的事件处理程序吗?
A:可以在同一个元素上同时注册多个相同类型的事件处理程序。如果用addEventListener()
方法注册多个事件处理程序,所有事件处理程序都会被调用,而不是被最后一个覆盖。如果用事件处理程序属性(如onclick
)注册多个事件处理程序,则最后一个覆盖前面的。
Q3:如何在HTML中为一个元素添加事件处理程序?
A:可以使用内联事件处理程序:
<button onclick="alert('Hello World!')">Click me!</button>
Q4:如何使用事件对象来获取点击目标的属性?
A:可以通过事件对象的target
属性获取:
btn.addEventListener('click', function(e) {
console.log(e.target.textContent); // 获取点击目标元素的文本内容
});
Q5:如何停止事件的默认行为?
A:可以使用事件对象的preventDefault()
方法:
btn.addEventListener('click', function(e) {
e.preventDefault(); // 停止事件的默认行为
alert('Default behavior stopped!');
});