
2025-02-28 日报 Day111

今日的鸡汤
倘若南风知我意,
莫将晚霞落黄昏。
今日学习内容
1、JS 红皮书 P505-522 第十七章:事件
今日笔记
1、事件类型: Web 浏览器中可以发生很多种事件。如前所述,所发生事件的类型决定了事件对象中会保存什么信息。DOM3 Events 定义了如下事件类型。
用户界面事件(UIEvent):涉及与 BOM 交互的通用浏览器事件。
焦点事件(FocusEvent):在元素获得和失去焦点时触发。
鼠标事件(MouseEvent):使用鼠标在页面上执行某些操作时触发。
滚轮事件(WheelEvent):使用鼠标滚轮(或类似设备)时触发。
输入事件(InputEvent):向文档中输入文本时触发。
键盘事件(KeyboardEvent):使用键盘在页面上执行某些操作时触发。
合成事件(CompositionEvent):在使用某种 IME(Input Method Editor,输入法编辑器)输入字符时触发。
- 用户界面事件: 用户界面事件或 UI 事件不一定跟用户操作有关。这类事件在 DOM 规范出现之前就已经以某种形式存在了,保留它们是为了向后兼容。UI 事件主要有以下几种。
load:在 window 上当页面加载完成后触发,在窗套( - load事件: load 事件可能是 JavaScript 中最常用的事件。在 window 对象上,load 事件会在整个页面(包括所有外部资源如图片、JavaScript 文件和 CSS 文件)加载完成后触发。可以通过两种方式指定 load 事件处理程序。第一种是 JavaScript 方式,如下所示:这是使用 addEventListener()方法来指定事件处理程序。与其他事件一样,事件处理程序会接收到一个 event 对象。这个 event 对象并没有提供关于这种类型事件的额外信息,虽然在 DOM 合规的浏览器中,event.target 会被设置为 document
1
2
3window.addEventListener("load", (event) => {
console.log("Loaded!");
});
第二种指定 load 事件处理程序的方式是向元素添加 onload 属性,如下所示:图片上也会触发load事件,包括DOM中的图片和非DOM中的图片。可以在HTML中直接给1
2
3
4
5
6
7
8
<html>
<head>
<title>Load Event Example</title>
</head>
<body onload="console.log('Loaded!')">
</body>
</html>元素的 onload 属性指定事件处理程序,比如:
这个例子会在图片加载完成后输出一条消息。同样,使用 JavaScript 也可以为图片指定事件处理程序:1
<img src="smile.gif" onload="console.log('Image loaded.')">
1
2
3
4let image = document.getElementById("myImage");
image.addEventListener("load", (event) => {
console.log(event.target.src);
}); - unload事件: 与 load 事件相对的是 unload 事件,unload 事件会在文档卸载完成后触发。unload 事件一般是在从一个页面导航到另一个页面时触发,最常用于清理引用,以避免内存泄漏。与 load 事件类似,unload 事件处理程序也有两种指定方式。第一种是 JavaScript 方式,如下所示:
1
2
3window.addEventListener("unload", (event) => {
console.log("Unloaded!");
}); - resize事件: 当浏览器窗口被缩放到新高度或宽度时,会触发 resize 事件。这个事件在 window 上触发,因此可以通过 JavaScript 在 window 上或者为元素添加 onresize 属性来指定事件处理程序。优先使用 JavaScript 方式:注意 浏览器窗口在最大化和最小化时也会触发 resize 事件。
1
2
3window.addEventListener("resize", (event) => {
console.log("Resized");
}); - scroll事件: 虽然 scroll 事件发生在 window 上,但实际上反映的是页面中相应元素的变化。在混杂模式下,可以通过元素检测 scrollLeft 和 scrollTop 属性的变化。而在标准模式下,这些变化在除早期版的 Safari 之外的所有浏览器中都发生在元素上(早期版的 Safari 在上跟踪滚动位置)。下面的代码演示了如何处理这些差异:2、焦点事件: 焦点事件在页面元素获得或失去焦点时触发。这些事件可以与 document.hasFocus()和document.activeElement 一起为开发者提供用户在页面中导航的信息。
1
2
3
4
5
6
7window.addEventListener("scroll", (event) => {
if (document.compatMode == "CSS1Compat") {
console.log(document.documentElement.scrollTop);
} else {
console.log(document.body.scrollTop);
}
});
blur:当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。
DOMFocusIn:当元素获得焦点时触发。这个事件是 focus 的冒泡版。Opera 是唯一支持这个事件的主流浏览器。DOM3 Events 废弃了 DOMFocusIn,推荐 focusin。
DOMFocusOut:当元素失去焦点时触发。这个事件是 blur 的通用版。Opera 是唯一支持这个事件的主流浏览器。DOM3 Events 废弃了 DOMFocusOut,推荐 focusout。
focus:当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持。
focusin:当元素获得焦点时触发。这个事件是 focus 的冒泡版。
focusout:当元素失去焦点时触发。这个事件是 blur 的通用版。
焦点事件中的两个主要事件是 focus 和 blur。IE 新增的这两个事件已经被 DOM3 Events 标准化。
当焦点从页面中的一个元素移到另一个元素上时,会依次发生如下事件。
(1) focuscout 在失去焦点的元素上触发。
(2) focusin 在获得焦点的元素上触发。
(3) blur 在失去焦点的元素上触发。
(4) DOMFocusOut 在失去焦点的元素上触发。
(5) focus 在获得焦点的元素上触发。
(6) DOMFocusIn 在获得焦点的元素上触发。
其中,blur、DOMFocusOut 和 focusout 的事件目标是失去焦点的元素,而 focus、DOMFocusIn和 focusin 的事件目标是获得焦点的元素。
3、鼠标和滚轮事件: 鼠标事件是 Web 开发中最常用的一组事件,这是因为鼠标是用户的主要定位设备。
click:在用户单击鼠标主键(通常是左键)或按键盘回车键时触发。这主要是基于无障碍的考虑,让键盘和鼠标都可以触发 onclick 事件处理程序。
dblclick:在用户双击鼠标主键(通常是左键)时触发。这个事件不是在 DOM2 Events 中定义的,但得到了很好的支持,DOM3 Events 将其进行了标准化。
mousedown:在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。
mouseenter:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseenter 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。
mouseleave:在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseleave 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。
mousemove:在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。
mouseout:在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。
mouseover:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。
mouseup:在用户释放鼠标键时触发。这个事件不能通过键盘触发。
页面中的所有元素都支持鼠标事件。除了 mouseenter 和 mouseleave,所有鼠标事件都会冒泡,都可以被取消,而这会影响浏览器的默认行为。由于事件之间存在关系,因此取消鼠标事件的默认行为也会影响其他事件。
比如,click 事件触发的前提是 mousedown 事件触发后,紧接着又在同一个元素上触发了 mouseup事件。如果 mousedown 和 mouseup 中的任意一个事件被取消,那么 click 事件就不会触发。类似地,两次连续的 click 事件会导致 dblclick 事件触发。只要有任何逻辑阻止了这两个 click 事件发生(比如取消其中一个 click 事件或者取消 mousedown 或 mouseup 事件中的任一个),dblclick 事件就不会发生。这 4 个事件永远会按照如下顺序触发:
(1) mousedown
(2) mouseup
(3) click
(4) mousedown
(5) mouseup
(6) click
(7) dblclick
click 和 dblclick 在触发前都依赖其他事件触发,mousedown 和 mouseup 则不会受其他事件影响。
IE8 及更早版本的实现中有个问题,这会导致双击事件跳过第二次 mousedown 和 click 事件。相应的顺序变成了:
(1) mousedown
(2) mouseup
(3) click
(4) mouseup
(5) dblclick
鼠标事件在 DOM3 Events 中对应的类型是”MouseEvent”,而不是”MouseEvents”。
鼠标事件还有一个名为滚轮事件的子类别。滚轮事件只有一个事件 mousewheel,反映的是鼠标滚轮或带滚轮的类似设备上滚轮的交互。 - 客户端坐标: 鼠标事件都是在浏览器视口中的某个位置上发生的。这些信息被保存在 event 对象的 clientX 和 clientY 属性中。这两个属性表示事件发生时鼠标光标在视口中的坐标,所有浏览器都支持。
可以通过下面的方式获取鼠标事件的客户端坐标:1
2
3
4let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
console.log(`Client coordinates: ${event.clientX}, ${event.clientY}`);
}); - 页面坐标: 客户端坐标是事件发生时鼠标光标在客户端视口中的坐标,而页面坐标是事件发生时鼠标光标在页面上的坐标,通过 event 对象的 pageX 和 pageY 可以获取。这两个属性表示鼠标光标在页面上的位置,因此反映的是光标到页面而非视口左边与上边的距离。
可以像下面这样取得鼠标事件的页面坐标:1
2
3
4let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
console.log(`Page coordinates: ${event.pageX}, ${event.pageY}`);
}); - 屏幕坐标: 屏幕坐标是事件发生时鼠标光标在屏幕上的坐标,通过 event 对象的 screenX 和 screenY 属性可以获取。这两个属性表示鼠标光标到屏幕左边与上边的距离。
可以像下面这样取得鼠标事件的屏幕坐标:1
2
3
4let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
console.log(`Screen coordinates: ${event.screenX}, ${event.screenY}`);
}); - 修饰键: 虽然鼠标事件主要是通过鼠标触发的,但有时候要确定用户想实现的操作,还要考虑键盘按键的状态。键盘上的修饰键 Shift、Ctrl、Alt 和 Meta 经常用于修改鼠标事件的行为。DOM 规定了 4 个属性来表示这几个修饰键的状态:shiftKey、ctrlKey、altKey 和 metaKey。这几属性会在各自对应的修饰键被按下时包含布尔值 true,没有被按下时包含 false。在鼠标事件发生的,可以通过这几个属性来检测修饰键是否被按下。来看下面的例子,其中在 click 事件发生时检测了每个修饰键的状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
let keys = new Array();
if (event.shiftKey) {
keys.push("shift");
}
if (event.ctrlKey) {
keys.push("ctrl");
}
if (event.altKey) {
keys.push("alt");
}
if (event.metaKey) {
keys.push("meta");
}
console.log("Keys: " + keys.join(","));
}); - 相关元素: 对 mouseover 和 mouseout 事件而言,还存在与事件相关的其他元素。这两个事件都涉及从一个元素的边界之内把光标移到另一个元素的边界之内。对 mouseover 事件来说,事件的主要目标是获得光标的元素,相关元素是失去光标的元素。类似地,对 mouseout 事件来说,事件的主要目标是失去光标的元素,而相关元素是获得光标的元素。
- 鼠标按键: 只有在元素上单击鼠标主键(或按下键盘上的回车键)时 click 事件才会触发,因此按键信息并不是必需的。对 mousedown 和 mouseup 事件来说,event 对象上会有一个 button 属性,表示按下或释放的是哪个按键。DOM 为这个 button 属性定义了 3 个值:0 表示鼠标主键、1 表示鼠标中键(通常也是滚轮键)、2 表示鼠标副键。按照惯例,鼠标主键通常是左边的按键,副键通常是右边的按键。
- 额外事件信息: DOM2 Events 规范在 event 对象上提供了 detail 属性,以给出关于事件的更多信息。对鼠标事件来说,detail 包含一个数值,表示在给定位置上发生了多少次单击。
- mousewheel事件: mousewheel事件会在用户使用鼠标滚轮时触发,包括在垂直方向上任意滚动。可以为页面上的任何元素或文档添加 onmousewheel 事件处理程序,以处理所有鼠标滚轮交互,比如:
1
2
3document.addEventListener("mousewheel", (event) => {
console.log(event.wheelDelta);
}); - 触摸屏设备: iOS 和 Android 等触摸屏设备的实现大相径庭,因为触摸屏通常不支持鼠标操作。在为触摸屏设备开发时,要记住以下事项。
不支持 dblclick 事件。双击浏览器窗口可以放大,但没有办法覆盖这个行为。
单指点触屏幕上的可点击元素会触发 mousemove 事件。如果操作会导致内容变化,则不会再触发其他事件。如果屏幕上没有变化,则会相继触发 mousedown、mouseup 和 click 事件。点触不可点击的元素不会触发事件。可点击元素是指点击时有默认动作的元素(如链接)或指定了 onclick 事件处理程序的元素。
mousemove 事件也会触发 mouseover 和 mouseout 事件。
双指点触屏幕并滑动导致页面滚动时会触发 mousewheel 和 scroll 事件。 - 无障碍问题: 如果 Web 应用或网站必须考虑残障人士,特别是使用屏幕阅读器的用户,那么必须小心使用鼠标事件。如前所述,按回车键可以触发 click 事件,但其他鼠标事件不能通过键盘触发。因此,建议不要使用 click 事件之外的其他鼠标事件向用户提示功能或触发代码执行,这是因为其他鼠标事件会严格妨碍盲人或视障用户使用。以下是几条使用鼠标事件时应该遵循的无障碍建议。
使用 click 事件执行代码。有人认为,当使用 onmousedown 执行代码时,应用程序会运行得更快。对视力正常用户来说确实如此。但在屏幕阅读器上,这样会导致代码无法执行,这是因为屏幕阅读器无法触发 mousedown 事件。
不要使用 mouseover 向用户显示新选项。同样,原因是屏幕阅读器无法触发 mousedown 事件。如果必须要通过这种方式显示新选项,那么可以考虑显示相同信息的键盘快捷键。
不要使用 dblclick 执行重要的操作,这是因为键盘不能触发这个事件。
4、键盘与输入事件: 键盘事件是用户操作键盘时触发的。DOM2 Events 最初定义了键盘事件,键盘事件包含 3 个事件:
keydown,用户按下键盘上某个键时触发,而且持续按住会重复触发。
keypress,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会触发这个事件。DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件。
keyup,用户释放键盘上某个键时触发。
输入事件只有一个,即 textInput。这个事件是对 keypress 事件的扩展,用于在文本显示给用户之前更方便地截获文本输入。textInput 会在文本被插入到文本框之前触发。
当用户按下键盘上的某个字符键时,首先会触发 keydown 事件,然后触发 keypress 事件,最后触发 keyup 事件。注意,这里 keydown 和 keypress 事件会在文本框出现变化之前触发,而 keyup事件会在文本框出现变化之后触发。如果一个字符键被按住不放,keydown 和 keypress 就会重复触发,直到这个键被释放。 - 键码: 对于 keydown 和 keyup 事件,event 对象的 keyCode 属性中会保存一个键码,对应键盘上特定的一个键。对于字母和数字键,keyCode 的值与小写字母和数字的 ASCII 编码一致。比如数字 7 键的keyCode 为 55,而字母 A 键的 keyCode 为 65,而且跟是否按了 Shift 键无关。
1
2
3
4let textbox = document.getElementById("myText");
textbox.addEventListener("keyup", (event) => {
console.log(event.keyCode);
}); - 字符编码: 在 keypress 事件发生时,意味着按键会影响屏幕上显示的文本。对插入或移除字符的键,所有浏览器都会触发 keypress 事件,其他键则取决于浏览器。浏览器在 event 对象上支持 charCode 属性,只有发生 keypress 事件时这个属性才会被设置值,包含的是按键字符对应的 ASCII 编码。通常,charCode 属性的值是 0,在 keypress 事件发生时则是对应按键的键码。
1
2
3
4
5
6
7
8
9
10
11var EventUtil = {
// 其他代码
getCharCode: function(event) {
if (typeof event.charCode == "number") {
return event.charCode;
} else {
return event.keyCode;
}
},
// 其他代码
}; - DOM3的变化: 尽管所有浏览器都实现了某种形式的键盘事件,DOM3 Events 还是做了一些修改。比如,DOM3 Events 规范并未规定 charCode 属性,而是定义了 key 和 char 两个新属性。
DOM3 Events 也支持一个名为 location 的属性,该属性是一个数值,表示是在哪里按的键。可能的值为:0 是默认键,1 是左边(如左边的 Alt 键),2 是右边(如右边的 Shift 键),3 是数字键盘,4 是移动设备(即虚拟键盘),5 是游戏手柄(如任天堂 Wii 控制器)。 - textInput事件: DOM3 Events 规范增加了一个名为 textInput 的事件,其在字符被输入到可编辑区域时触发。作为对 keypress 的替代,textInput 事件的行为有些不一样。一个区别是 keypress 会在任何可以获得焦点的元素上触发,而 textInput 只在可编辑区域上触发。另一个区别是 textInput 只在有新字符被插入时才会触发,而 keypress 对任何可能影响文本的键都会触发(包括退格键)。event 对象上还有一个名为 inputMethod 的属性,该属性表示向控件中输入文本的手段。可能的值如下:
1
2
3
4let textbox = document.getElementById("myText");
textbox.addEventListener("textInput", (event) => {
console.log(event.data);
});
0,表示浏览器不能确定是什么输入手段;
1,表示键盘;
2,表示粘贴;
3,表示拖放操作;
4,表示 IME;
5,表示表单选项;
6,表示手写(如使用手写笔);
7,表示语音;
8,表示组合方式;
9,表示脚本。
5、合成事件: 合成事件是 DOM3 Events 中新增的,用于处理通常使用 IME 输入时的复杂输入序列。IME 可以让用户输入物理键盘上没有的字符。
6、变化事件: DOM2 的变化事件(Mutation Events)是为了在 DOM 发生变化时提供通知。
注意 这些事件已经被废弃,浏览器已经在有计划地停止对它们的支持。变化事件已经被Mutation Observers 所取代,可以参考第 14 章中的介绍。