键盘事件
键盘事件(Keyboard Events)用于响应用户的键盘操作,包括按键按下、释放等。键盘事件是实现快捷键、表单验证、游戏控制和无障碍访问的重要基础。现代 Web 开发中推荐使用 keydown/keyup 事件配合 event.key 或 event.code 来识别按键。
前置知识
阅读本节前,建议先了解:鼠标事件
基础概念
键盘事件类型
| 事件 | 触发时机 | 说明 |
|---|---|---|
keydown | 按键按下时 | 重复触发(长按时持续触发) |
keyup | 按键释放时 | 每次释放触发一次 |
keypress | 字符输入时 | 已废弃,使用 keydown 替代 |
html
<!-- HTML 事件属性 -->
<input type="text"
onkeydown="console.log('按下:', event.key)"
onkeyup="console.log('释放:', event.key)">
<!-- JavaScript 方式 -->
<script>
document.addEventListener('keydown', (e) => {
console.log('按键:', e.key, '代码:', e.code);
});
</script>event.key vs event.code
| 属性 | 含义 | 示例 | 特点 |
|---|---|---|---|
event.key | 按键的值 | "a", "Enter", "+" | 受键盘布局影响,有可打印字符 |
event.code | 物理按键位置 | "KeyA", "Enter", "Equal" | 不受键盘布局影响,基于物理位置 |
javascript
document.addEventListener('keydown', (e) => {
// event.key: 受输入法影响
// 按下 QWERTY 键盘的 'A' 键:key = "a"
// 在 Dvorak 键盘上按同一个键:key = "'"(不同)
// event.code: 始终标识物理按键
// 无论什么键盘布局,按左下角第二个键:code = "KeyA"
console.log(`key: ${e.key}, code: ${e.code}`);
});语法
HTML 事件属性
html
<!-- 基本键盘事件 -->
<input onkeydown="handleKeyDown(event)" onkeyup="handleKeyUp(event)">
<!-- 快捷键示例 -->
<div onkeydown="if(event.ctrlKey && event.key==='s'){event.preventDefault();save();}">
按 Ctrl+S 保存
</div>JavaScript 绑定
javascript
const input = document.getElementById('my-input');
// keydown:按键按下时触发
input.addEventListener('keydown', (e) => {
console.log('按下:', e.key, e.code);
});
// keyup:按键释放时触发
input.addEventListener('keyup', (e) => {
console.log('释放:', e.key, e.code);
});详细说明
KeyboardEvent 对象属性
| 属性 | 类型 | 说明 |
|---|---|---|
key | string | 按键值(如 "a", "Enter", "F1") |
code | string | 物理按键代码(如 "KeyA", "Digit1") |
keyCode | number | 已废弃,旧的按键码 |
which | number | 已废弃,等同于 keyCode |
location | number | 按键位置(0=标准 1=左 2=右 3=数字键盘) |
repeat | boolean | 是否为长按重复触发 |
isComposing | boolean | 是否正在使用输入法组合 |
ctrlKey / altKey / shiftKey / metaKey | boolean | 修饰键状态 |
常用按键对照表
| 按键 | event.key | event.code |
|---|---|---|
| A 键 | "a" | "KeyA" |
| 1 键 | "1" | "Digit1" |
| Enter | "Enter" | "Enter" |
| Space | " " | "Space" |
| Tab | "Tab" | "Tab" |
| Escape | "Escape" | "Escape" |
| Backspace | "Backspace" | "Backspace" |
| Delete | "Delete" | "Delete" |
| Arrow keys | "ArrowUp" 等 | "ArrowUp" 等 |
| F1-F12 | "F1" - "F12" | "F1" - "F12" |
| Shift | "Shift" | "ShiftLeft" / "ShiftRight" |
| Ctrl | "Control" | "ControlLeft" / "ControlRight" |
| Alt | "Alt" | "AltLeft" / "AltRight" |
| Meta (Cmd) | "Meta" | "MetaLeft" / "MetaRight" |
修饰键组合
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>键盘快捷键示例</title>
<style>
.shortcut-list {
max-width: 400px;
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
}
.shortcut-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e2e8f0;
}
kbd {
background: #f1f5f9;
border: 1px solid #d1d5db;
border-radius: 4px;
padding: 2px 8px;
font-family: monospace;
font-size: 13px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="shortcut-list">
<div class="shortcut-item">
<span>保存</span>
<span><kbd>Ctrl</kbd> + <kbd>S</kbd></span>
</div>
<div class="shortcut-item">
<span>撤销</span>
<span><kbd>Ctrl</kbd> + <kbd>Z</kbd></span>
</div>
<div class="shortcut-item">
<span>全选</span>
<span><kbd>Ctrl</kbd> + <kbd>A</kbd></span>
</div>
<div class="shortcut-item">
<span>搜索</span>
<span><kbd>Ctrl</kbd> + <kbd>F</kbd></span>
</div>
</div>
<script>
// 快捷键处理
document.addEventListener('keydown', (e) => {
// Ctrl+S:保存
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
console.log('保存');
showNotification('已保存');
}
// Ctrl+Z:撤销
if (e.ctrlKey && e.key === 'z') {
e.preventDefault();
console.log('撤销');
showNotification('已撤销');
}
// Ctrl+A:全选
if (e.ctrlKey && e.key === 'a') {
e.preventDefault();
console.log('全选');
}
// Ctrl+F:搜索
if (e.ctrlKey && e.key === 'f') {
e.preventDefault();
console.log('搜索');
}
// Escape:取消/关闭
if (e.key === 'Escape') {
console.log('取消');
}
});
function showNotification(msg) {
const el = document.createElement('div');
el.textContent = msg;
el.style.cssText = `
position: fixed; bottom: 20px; right: 20px;
background: #22c55e; color: white;
padding: 8px 16px; border-radius: 8px;
z-index: 9999;
`;
document.body.appendChild(el);
setTimeout(() => el.remove(), 2000);
}
</script>
</body>
</html>防止输入非法字符
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>输入过滤示例</title>
</head>
<body>
<!-- 只允许数字输入 -->
<label>
手机号:<input type="text" id="phone" maxlength="11"
placeholder="请输入手机号">
</label>
<!-- 只允许字母和数字 -->
<label>
用户名:<input type="text" id="username"
placeholder="只允许字母、数字和下划线">
</label>
<script>
// 只允许数字
document.getElementById('phone').addEventListener('keydown', (e) => {
// 允许的功能键:退格、删除、方向键、Tab 等
const allowed = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight',
'Home', 'End', 'Tab'];
if (allowed.includes(e.key)) return;
// 允许 Ctrl 组合键(如 Ctrl+A)
if (e.ctrlKey || e.metaKey) return;
// 只允许数字
if (!/^\d$/.test(e.key)) {
e.preventDefault();
}
});
// 只允许字母、数字和下划线
document.getElementById('username').addEventListener('keydown', (e) => {
const allowed = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight',
'Home', 'End', 'Tab'];
if (allowed.includes(e.key)) return;
if (e.ctrlKey || e.metaKey) return;
if (!/^[a-zA-Z0-9_]$/.test(e.key)) {
e.preventDefault();
}
});
</script>
</body>
</html>实战示例
完整的键盘导航
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>键盘导航示例</title>
<style>
.nav-list {
list-style: none;
padding: 0;
max-width: 300px;
}
.nav-item {
padding: 10px 16px;
border: 1px solid #e2e8f0;
border-bottom: none;
cursor: pointer;
transition: background 0.15s;
}
.nav-item:last-child {
border-bottom: 1px solid #e2e8f0;
}
.nav-item:hover,
.nav-item.focused {
background: #eff6ff;
}
.nav-item.active {
background: #3b82f6;
color: white;
border-color: #3b82f6;
}
.nav-item.active + .nav-item {
border-top-color: #3b82f6;
}
.hint {
margin-top: 16px;
font-size: 13px;
color: #64748b;
}
</style>
</head>
<body>
<ul class="nav-list" id="nav-list" role="listbox" aria-label="导航菜单">
<li class="nav-item" role="option" data-index="0">首页</li>
<li class="nav-item" role="option" data-index="1">产品</li>
<li class="nav-item" role="option" data-index="2">关于我们</li>
<li class="nav-item" role="option" data-index="3">联系方式</li>
<li class="nav-item" role="option" data-index="4">帮助中心</li>
</ul>
<p class="hint">使用上下方向键导航,Enter 确认选择</p>
<script>
const navList = document.getElementById('nav-list');
const items = navList.querySelectorAll('.nav-item');
let focusedIndex = -1;
navList.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
focusedIndex = Math.min(focusedIndex + 1, items.length - 1);
updateFocus();
break;
case 'ArrowUp':
e.preventDefault();
focusedIndex = Math.max(focusedIndex - 1, 0);
updateFocus();
break;
case 'Enter':
case ' ':
e.preventDefault();
if (focusedIndex >= 0) {
selectItem(focusedIndex);
}
break;
case 'Home':
e.preventDefault();
focusedIndex = 0;
updateFocus();
break;
case 'End':
e.preventDefault();
focusedIndex = items.length - 1;
updateFocus();
break;
}
});
function updateFocus() {
items.forEach((item, i) => {
item.classList.toggle('focused', i === focusedIndex);
item.setAttribute('aria-selected', i === focusedIndex);
});
if (focusedIndex >= 0) {
items[focusedIndex].scrollIntoView({ block: 'nearest' });
}
}
function selectItem(index) {
items.forEach(item => item.classList.remove('active'));
items[index].classList.add('active');
console.log('选中:', items[index].textContent);
}
</script>
</body>
</html>注意事项
keydown 的重复触发
长按按键时,keydown 会持续触发(event.repeat 为 true)。如果只需要首次按下时响应:
javascript
document.addEventListener('keydown', (e) => {
if (e.repeat) return; // 忽略长按重复
console.log('首次按下:', e.key);
});输入法组合问题
使用中文、日文等输入法时,keydown 会在组合输入过程中触发。应检查 e.isComposing:
javascript
input.addEventListener('keydown', (e) => {
// 输入法组合过程中,不处理按键
if (e.isComposing) return;
if (e.key === 'Enter') {
console.log('提交:', input.value);
}
});已废弃的 keypress 和 keyCode
javascript
// 已废弃,不要使用
// element.addEventListener('keypress', handler);
// if (e.keyCode === 13) { ... } // 不推荐
// 推荐:使用 keydown + event.key
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
// 处理回车
}
});最佳实践
1. 使用 event.key 而非 event.keyCode
javascript
// 推荐
if (e.key === 'Enter') { /* ... */ }
if (e.key === 'Escape') { /* ... */ }
// 不推荐(已废弃)
if (e.keyCode === 13) { /* ... */ }
if (e.which === 27) { /* ... */ }2. 实现键盘快捷键时防止浏览器默认行为
javascript
// 阻止浏览器默认的 Ctrl+S(保存页面)、Ctrl+F(搜索)等
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault(); // 阻止浏览器弹出保存对话框
saveDocument(); // 执行自定义保存
}
});下一节
继续学习:表单事件
参考链接
- MDN: KeyboardEvent
- MDN: event.key
- MDN: event.code
- [Key Code Info](https:// keycode.info/) - 按键查询工具