Keyboard Key Codes
Side-by-side event.key, event.code and legacy keyCode for common keys — quick reference for shortcuts and keyboard handlers.
How It Works
Keyboard events expose two identifiers: event.key is the logical key (the character shown, e.g. "a", "Enter"), event.code is the physical key (its position, e.g. "KeyA", "Digit1"). Under different layouts (QWERTY/Dvorak) the same key can come from a different code.
Pick by intent: use code to bind to a physical position (games, instruments), use key to bind to the typed character (text handling). event.keyCode is deprecated and new code should not rely on it.
In practice, decide by whether you need layout independence. Games, instruments and CAD tools that bind physical positions should use code directly, because switching between QWERTY, Dvorak or AZERTY moves the letter keys and key would map one physical key to different letters. Pure text entry — login, search, chat — only cares which character was produced, so key is enough and code would only add complexity. Shortcuts are where both are needed: use code to pin the physical key and ctrlKey/metaKey/shiftKey/altKey to check modifiers, so Shift+1 still binds the same physical combination whether it yields "!" on a US layout or "1" on a French one.
Also mind when events fire: keydown fires on press (repeating while held), keyup on release, while input/change only after the value actually changes. Shortcuts normally trigger on keydown, but asking "what did the user finally enter" needs input — mixing them up yields intermediate states during IME composition or while a key is held. Separately, Enter inside a form implicitly submits it; that is browser behaviour, not a shortcut you listen for yourself, so confirm you will not break accessible submission before calling preventDefault.
When to Use This Table
Worth consulting this table in three cases: (1) you are implementing hotkeys and must distinguish "which character was typed" from "which physical key was pressed"; (2) you are debugging a keyboard issue that only reproduces on one machine; (3) you maintain legacy code that still returns keyCode.
When not to consult it: you only want a text field to receive text. That is form and input handling, not keyboard codes — use input events and form controls, and wrapping them in keyCode only adds complexity.
Another bad signal: you are designing game controls but plan to bind on key. Games, instruments and drawing apps need physical positions (the WASD row is not the same row on every layout), so those must use event.code, otherwise users with a different layout get wrong controls.
The test is simple: if your logic cares about "what the user meant", use key; if it cares about "where the user’s hand was", use code. If it needs neither, this table is irrelevant.
Boundaries and Common Pitfalls
keyCode is not "the key number" but a legacy leftover
keyCode comes from the IE era and its numbering is layout-dependent: the same physical key can report different numbers across layouts and IMEs. New code should not depend on it. Its only remaining use is identifying keys that some browsers or IMEs cannot map (e.g. full-width/half-width, ScrollLock), where both key and code may be empty and keyCode is the only fallback.
event.key changes with IME state and modifiers
A combo reports different values at keydown and keyup: holding Ctrl then pressing S gives an undefined or "s" key at keydown, and the change surfaces at keyup. During IME composition, event.key may be swallowed or reported as "Process". Detect combos via event.code plus modifier flags rather than waiting for an expected character in event.key.
Digit and symbol keys sit in different positions per layout
In event.code, digits are always Digit0–Digit9 and letters KeyA–KeyZ — that is exactly its layout-independence. Mind the separate namespaces: main-row 1 is Digit1 while keypad 1 is Numpad1, and Numpad1 can act as End on some layouts. Mixing them up breaks requirements like "numpad Enter".
preventDefault cannot stop browser-process shortcuts
F11 fullscreen, Ctrl+Shift+I / Ctrl+Shift+J (devtools), Ctrl+T / Ctrl+N / Ctrl+W are handled at the browser process or UI layer; page-level keydown handlers either never see them or cannot stop them. Before promising to block something like Ctrl+W, verify the target browsers actually allow it, or you will ship an implementation that looks effective but is not.
Auto-repeat inflates side effects
Holding an arrow key fires keydown repeatedly at the OS repeat rate (often 10–30 per second). If the handler writes, fetches or recomputes, guard with e.repeat or throttle — otherwise holding an arrow to scroll a list can fire hundreds of requests.
How This Relates to Neighbours
To convert the character from event.key into an ASCII/code-point number, use the in-site ASCII table: it gives decimal/hex/octal side by side, saving you a hand-rolled charCodeAt.
Key names (.key / .code) and character encoding are different things. To learn how a character becomes bytes, go to the Character Encodings page, not this one.
Looking up code points for emoji and symbols is a Unicode question, not a keyboard-layout one; the in-site Emoji page covers code points and search.
If the requirement is "let the user paste formatted text into the field", that is input and clipboard work — this key table is not the tool for it.
Practical Snippet
Key points: (1) match combos on e.code, not e.key, to stay clear of IME and case; (2) reject e.repeat first so holding a key fires once; (3) keypad and main row are separate namespaces (NumpadEnter vs Enter), so mixing them breaks numpad-Enter handling; (4) detect the Ctrl vs ⌘ difference at runtime via navigator.platform rather than a user-agent string.
// 稳定的组合键判定:用 code + 修饰键,不等 key
function onKeydown(e: KeyboardEvent) {
// 长按自动重复:避免同一次按住触发多次副作用
if (e.repeat) return
// Mac 用 metaKey(⌘),Windows/Linux 用 ctrlKey
const mod = navigator.platform.includes('Mac') ? e.metaKey : e.ctrlKey
// 小键盘回车:注意主键盘 Enter 叫 "Enter",小键盘叫 "NumpadEnter"
if (e.code === 'NumpadEnter' && mod) {
e.preventDefault()
submit()
return
}
// 方向键:只响应主键盘区,避免小键盘误触
if (e.code.startsWith('Arrow') && e.target === document.activeElement) return
// 保存:Cmd/Ctrl + S。code 稳定,不受输入法与大小写影响
if (e.code === 'KeyS' && mod) {
e.preventDefault()
save()
}
}Practical Notes
- keyCode is deprecated and not equivalent to keykeyCode is marked deprecated (Enter is always 13), while event.key carries the produced character and event.code carries the physical key. Mixing them breaks IME input and keyboard-layout switching; new code should use key or code.
- An IME fires Enter twiceWith a Chinese IME, the Enter that confirms a candidate is first reported with isComposing / keyCode 229, and only then does the real submit fire. A form that submits on Enter without checking event.isComposing will submit while the user is still picking a word.
- key changes when modifiers are heldHolding Shift makes event.key an uppercase A while the unmodified value is lowercase, whereas event.code always stays KeyA. Whether to test code or key depends on whether you are mapping a physical key or a produced character.
Data Sources
- W3C UI Events (key and code values)
- MDN Web Docs (KeyboardEvent)
Codes, names and definitions above come from public specifications; we claim no originality over the specification data itself. The arrangement, Chinese annotations and practical notes on this page are written by us.
| Key | event.key | event.code | keyCode |
|---|---|---|---|
| A | 'a' / 'A' | 'KeyA' | 65 |
| Z | 'z' / 'Z' | 'KeyZ' | 90 |
| 0 | '0' | 'Digit0' | 48 |
| 1 | '1' | 'Digit1' | 49 |
| Enter | 'Enter' | 'Enter' | 13 |
| 空格 Space | ' ' | 'Space' | 32 |
| Tab | 'Tab' | 'Tab' | 9 |
| Esc | 'Escape' | 'Escape' | 27 |
| Backspace | 'Backspace' | 'Backspace' | 8 |
| Delete | 'Delete' | 'Delete' | 46 |
| ← | 'ArrowLeft' | 'ArrowLeft' | 37 |
| → | 'ArrowRight' | 'ArrowRight' | 39 |
| ↑ | 'ArrowUp' | 'ArrowUp' | 38 |
| ↓ | 'ArrowDown' | 'ArrowDown' | 40 |
| Shift | 'Shift' | 'ShiftLeft' / 'ShiftRight' | 16 |
| Ctrl | 'Control' | 'ControlLeft' / 'ControlRight' | 17 |
| Alt | 'Alt' | 'AltLeft' / 'AltRight' | 18 |
| F1 | 'F1' | 'F1' | 112 |
| F12 | 'F12' | 'F12' | 123 |
| / | '/' | 'Slash' | 191 |
| . | '.' | 'Period' | 190 |
| , | ',' | 'Comma' | 188 |
Frequently Asked Questions
What is the difference between event.key and event.code?
event.key is the "character value" (affected by Shift/caps, e.g. "a" or "A"); event.code is the physical "position" (always "KeyA", layout-independent). For shortcuts prefer event.code or event.key with modifier checks.
Why is keyCode deprecated?
KeyboardEvent.keyCode is a legacy IE-era numeric code inconsistent across layouts and deprecated by the Web standard. New code should use event.key / event.code, but legacy libraries still emit keyCode, so this table helps when maintaining old code.
How do I block browser default shortcuts (Ctrl+S, F12)?
Call event.preventDefault() on the keydown event for the target combination. Note: some shortcuts (e.g. F11 fullscreen, DevTools combos) are intercepted at the browser-process level and cannot be fully blocked by a page.
How do I detect combos like Ctrl+C / Ctrl+Enter?
In keydown, check both event.ctrlKey (metaKey on Mac) and event.key or event.code. Example Ctrl+C: event.ctrlKey && (event.key === "c" || event.code === "KeyC"). Prefer code to avoid IME/case issues.