原文链接:http://javascript.info/keyboard-events,translate with ❤️ by zhangbao.
在深入键盘事件之前,需要注意的是,还有其他的“输入”方式。例如:在手机设备上可以使用语音识别,或者用鼠标复制/粘贴进行输入。
因此,如果我们想要追踪 <input> 输入框,仅使用键盘事件是不够的。有一个叫 input 事件可以观察到任何情境下的 <input> 输入框里值的改变,有时它可能才是更好的选择。我们会在后面的《事件:change、input、cut、copy 和 paste》一章中介绍。
键盘事件用于处理键盘操作(虚拟键盘也包括在内)。例如,响应箭头按键 Up、Down 或者热键(包括组合键)。
试验台
为了更好地理解键盘事件,可以点点下面的试验台。
在文本域中尝试不同的组合键查看效果。
keydown 和 keyup
keydown 事件在按下鼠标时触发;keyup 事件在释放鼠标时触发。
event.code 和 event.key
事件对象的 key 属性用来获得按键表示的字符, code 属性用来获得“物理按键码”。
例如,按下按键 Z 的同时,可以选择按下/不按下 Shift 键。如此一来,我们将得到两个不同的字符:大写的 Z 和小写的 z。
event.key 属性对应实实在在打印出来的那个字符,可能有所不同;但打印出的 event.code 值始终是一样的:
| 按键 | event.key |
event.code |
|---|---|---|
Z |
z(小写的) |
KeyZ |
Shift+Z |
Z(大写的) |
KeyZ |
对于不同语言的键盘,得到的 event.key 值可能就不是“Z”了,而 event.code 值始终是“KeyZ”。
“**KeyZ``”**和其他按键码
键盘上不同的按键有不同的按键码。按键码可参考《UI 事件码规范》:
例如:
字符键的键码值,是“
Key<letter>”的形式:例如“KeyA”、“KeyB”等。数字键的键码值,是“
Digital<number>”的形式:例如“Digital0”、“Digital1”等。其他按键的键码值按照名字的不同而不同:例如“
Enter”、“Backspace” 和“Tab”等。有几种常规键盘布局,规范为每种布局都指定了对应的按键代码。
请参阅《规范的字母数字部分》以获得更多按键码信息。
注意**大小写:是“**KeyZ
**”,不是“****keyZ**”虽然很明显,但还是经常会犯错的地方。
注意,是“
KeyZ”,不是“keyZ”!像event.code === "keyZ"这种检查是无效的。 “Key”必须是首字母大写的形式。
如果一个按键不代表任何字符呢?例如,Shift、F1 或其他一些,对于这类按键,event.key 与 event.code 值基本一致。
| 按键 | event.key |
event.code |
|---|---|---|
F1 |
F1 |
F1 |
Backspace |
Backspace |
Backspace |
Shift |
Shift |
ShiftRight 或 ShiftLeft |
请注意,event.code 的值能让我们确定按下的是哪个按键。例如,大多数的键盘有两个 Shift 按键:左边一个、右边一个。event.code 就能告诉我们按下的是哪一个,event.key 则强调被按下按键的含义“Shift”。
如果需要处理热键:比如 Ctrl+Z(对应 Mac 上的 Cmd+Z)。许多文本编辑器绑定的默认操作是“撤销”。我们可以用 keydown 事件监听哪个按键被按下了——监测什么时候我们使用热键了。
请回答一个问题,在监听器中,我们应该检查 event.key 的值还是 event.code 的值呢?
请停下来想想。
……
想到了吗?
如果你已经理解了前面所说的,答案很明显,应该使用 event.code 而不是 event.key。因为 event.key 的值依赖语言环境或 CapsLock 按键是否开启,而 event.code 的值总是一致的,是于键盘上的按键唯一绑定的。
document.addEventListener('keydown', function (event) {if (event.code === 'keyZ' && (event.ctrlKey || event.metaKey)) {alert('撤销');}});
自动重复
如果一个按键长时间按下不释放,就会重复触发 keydown 事件;当释放按键时,触发一次 keyup 事件。所以结果会看到触发许多的 keydown 事件和一个 keyup 事件。
所有重复 keydown 事件的事件对象的 event.repeat 属性值都为 true。
默认行为
默认行为会有所不同,因为键盘操作可能会启动许多可能的行为。
例如:
出现一个字符(最明显的输出了)。
删除一个字符(按下
Delete按键)。滚动页面(按下
PageDown按键)。浏览器打开“保存”页面对话框(
Ctrl+S)。……
阻止 keydown 事件的默认行为可以取消大多数(除个别操作系统级的快捷键外)上述行为的发生 。例如,Windows 系统里的 Alt+F4 会关闭当前浏览器窗口,这是使用 JavaScript 无法阻止的行为。
下例中,<input> 限制只能输入手机号码,不接受数字、+、() 和 - 外的其他符号:
<script>function checkPhoneKey(key) {return (key >= '0' && key <= '9') || key === '+' || key === '(' || key === ')' || key === '-';}</script><input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">
需要注意的是,在输入框中按下像 Backspace、Left、Right 和 Ctrl+V 等这样特殊的按键也变失效了,这是过滤算法 checkPhoneKey 带来的副作用。
我们放松一下规则:
<script>function checkPhoneKey(key) {return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-' ||key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace';}</script><input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">
现在箭头按键和删除按键都可以使用了。
但是我们仍然可以通过鼠标右键粘贴输入文本,所以这也不是百分之百可信赖的。我们可以先让它这样,因为多数场景下都是可行的。另一个可选方案是检查 input 事件,这个事件会在每次输入框内容修改后触,我们可以在处理器中检查新的值,无效的话就修改它。
遗留问题
过去还会使用 keypress 事件,还有事件对象上的 keyCode、charCode 和 which 属性。
因为这些事件/事件属性并不是浏览器全兼容的,标准开发人员决定弃用它们。旧代码仍然有效,是因为浏览器还在支持它们,但是完全不需要再使用它们了。
曾几何时,本章中包含了对它们的详细描述,但现在我们可以忘记它们了。
总结
按下按键(可能是一个符号按键或是像 Shift、Ctrl 这样的特殊按键)会触发键盘事件。唯一的例外是有时出现在笔记本键盘上的 Fn 键,它没有键盘事件,因为它通常在比操作系统更低的级别上实现的。
键盘事件:
keydown——按下鼠标时触发(按住不放则会重复触发)keyup——释放鼠标时触发。
主要的键盘事件对象属性:
code—— “按键码”(比如“KeyA”、“ArrowLeft”等),表示按键在键盘上的物理位置。key——字符(比如“A”、“a”等),对于非字符按键,值通常跟code一致。
过去,键盘事件有时用于跟踪表单字段中的用户输入。这并不可靠,因为输入有多种方式。我们可以使用 input/change 事件处理用户输入(在《事件:change、input、cut、copy 和 paste》一章中讲到)。它们会在输入值改变后触发,包括鼠标或语音识别的输入。
当我们明确使用键盘操作的时候,才去使用键盘事件。例如,响应热键和特殊按键的操作。
练习题
问题
一、扩展热键
创建一个函数 runOnKeys(func, code1, code2, ... code_n) 仅在同时按下按键码 code1, code2, … code_n 时,才去执行回调函数 func。
例如下面代码里,仅在同时按下“Q”和“W”的时候(所有语言环境下,不管是否开启 CapsLock 键),调用 alert:
runOnKeys(() => alert("Hello!"),"KeyQ","KeyW");
答案
一、扩展热键
我们会用到两个事件处理器:document.onkeydown 和 document.onkeyup。
集合 pressed 中保存现在按下的按键。
第一个处理器用来添加按键,第二个处理器用来删除按键。每次 keydown 的时候,我们就检查下是否按下了所有想要按下的按键,如果是的话,就执行回调函数。
function runOnKeys(func, ...codes) {let pressed = new Set();document.addEventListener('keydown', function(event) {pressed.add(event.code);for (let code of codes) { // 是否按下了所有想要按下的按键if (!pressed.has(code)) {return;}}// 好的,都按下了// 在 alert 的时候,如果用户释放了按键// JavaSript 不会接收到"keyup"事件// pressed 集合里仍然保留所有按下的按键信息// 为了避免"粘性"按键,我们重置了状态(清空了按键集合)// 如果用户想要再一次执行热键回调,就需要再一次全部按下按键pressed.clear();func();});document.addEventListener('keyup', function(event) {pressed.delete(event.code);});}
(完)

“**KeyZ``”**和其他按键码
注意**大小写:是“**KeyZ