JavaScript Keyboard Events Keyup and Keydown
Learn the difference between keydown and keyup events, how to read event.key and event.code, detect modifier keys, and build keyboard-driven interactions.
Keyboard events let your code respond to what the user types. There are three keyboard events in the browser: keydown, keyup, and the deprecated keypress. For modern JavaScript, you only need keydown and keyup.
keydown fires when a key is pressed down. If the user holds the key, keydown fires repeatedly. keyup fires once when the key is released.
The diagram shows the lifecycle of a single key press. keydown fires immediately, then repeats while the key stays held, then keyup fires once when the user lifts their finger.
Here is code to see the events in action:
document.addEventListener("keydown", (event) => {
console.log(`keydown: ${event.key} (repeat: ${event.repeat})`);
});
document.addEventListener("keyup", (event) => {
console.log(`keyup: ${event.key}`);
});Press and release the "a" key while this code is running. The two listeners fire independently, so the console shows one keydown entry followed by one keyup entry:
keydown: a (repeat: false)
keyup: aHold "a" down for a moment, then release. You will see multiple keydown entries with repeat: true, then one keyup.
event.key: The Character the Key Produces
event.key returns a string representing the key's meaning. For printable characters, it is the character itself. For special keys, it is a descriptive name:
document.addEventListener("keydown", (event) => {
console.log(event.key);
});| Key Pressed | event.key |
|---|---|
| a | "a" |
| Shift + a | "A" |
| Enter | "Enter" |
| Escape | "Escape" |
| Arrow Right | "ArrowRight" |
| Backspace | "Backspace" |
| Space | " " (a space character) |
event.key respects the current keyboard layout. If the user has a French AZERTY keyboard and presses the key where "A" sits on QWERTY, event.key returns "q" because that is what the key produces in that layout.
Use event.key when your logic depends on what the key means to the user, such as character input, keyboard shortcuts, or text processing.
event.code: The Physical Key Location
event.code returns a string representing the physical position of the key on the keyboard, ignoring the user's layout:
document.addEventListener("keydown", (event) => {
console.log(event.code);
});| Key Pressed (QWERTY) | Same Key (AZERTY) | event.code (both) |
|---|---|---|
| A | Q | "KeyA" |
| Enter | Enter | "Enter" |
| Arrow Left | Arrow Left | "ArrowLeft" |
| Digit 1 | & | "Digit1" |
| Escape | Escape | "Escape" |
event.code is identical regardless of layout. Use it for game controls (WASD movement), where the physical position matters, not the character it produces.
When to Use key vs code
| Situation | Use |
|---|---|
| Keyboard shortcut like Ctrl+S | event.key (checks for "s") |
| WASD game movement | event.code (checks physical position) |
| Detecting Enter to submit a form | event.key === "Enter" |
| Preventing non-numeric input | event.key and a regex |
| Arrow key navigation | Either, both return the same for arrows |
Detecting Modifier Keys
The event object has boolean flags for modifier keys:
document.addEventListener("keydown", (event) => {
console.log("Ctrl:", event.ctrlKey);
console.log("Shift:", event.shiftKey);
console.log("Alt:", event.altKey);
console.log("Meta:", event.metaKey); // Command on Mac, Windows key on Windows
});These let you build keyboard shortcuts. Here is a Ctrl+S (or Cmd+S on Mac) shortcut that intercepts the browser's own save-page behavior:
document.addEventListener("keydown", (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
event.preventDefault();
console.log("Save triggered!");
}
});Checking both ctrlKey and metaKey covers Windows and Mac in one condition. Calling preventDefault() is required here because Ctrl+S normally triggers "Save Page" in most browsers, and skipping it means your custom save runs alongside the browser's default action instead of replacing it.
You can combine more than one modifier for less common shortcuts, such as Ctrl+Shift+ArrowRight for selecting the next word:
document.addEventListener("keydown", (event) => {
if (event.ctrlKey && event.shiftKey && event.key === "ArrowRight") {
event.preventDefault();
console.log("Select next word.");
}
});Chaining && across each modifier flag and event.key means the handler only runs when every condition is true at once.
Practical Examples
Live Character Counter
Count characters as the user types, using keyup so the count reflects the final state:
const textarea = document.querySelector("textarea");
const counter = document.querySelector(".counter");
textarea.addEventListener("keyup", () => {
counter.textContent = `${textarea.value.length} characters`;
});Using keyup here means the counter updates after the character appears in the textarea. If you used keydown, the count would be one character behind because keydown fires before the value changes.
Arrow Key Navigation
Navigate between items with the arrow keys. Start by tracking which item is focused:
const items = document.querySelectorAll(".menu-item");
let currentIndex = 0;currentIndex holds the position of the currently focused item, starting at the first one. Now handle the arrow keys by moving that index up or down and clamping it to stay inside the list:
document.addEventListener("keydown", (event) => {
const delta = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
if (delta === 0) return;
event.preventDefault();
currentIndex = Math.min(Math.max(currentIndex + delta, 0), items.length - 1);
items[currentIndex].focus();
});This creates a focusable list that the user can navigate with arrow keys, similar to how a native dropdown menu behaves. Math.min and Math.max together keep currentIndex from going past either end of the list.
Blocking Specific Keys in an Input
Prevent non-digit characters in a number field:
const numberInput = document.querySelector('input[type="text"]');
const allowedKeys = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"];allowedKeys lists the non-digit keys that should still work, so users can still navigate and edit the field. Without this list, the handler below would also block Backspace and the arrow keys:
numberInput.addEventListener("keydown", (event) => {
if (!/^\d$/.test(event.key) && !allowedKeys.includes(event.key)) {
event.preventDefault();
console.log(`Blocked: ${event.key}`);
}
});keydown fires before the character is inserted, so preventDefault() stops it cleanly. If you used keyup, the character would already appear in the input before you could block it.
The Deprecated keypress Event
You may see keypress in older code, but you should not use it in new projects. It does not fire for non-printable keys like arrows, Escape, or modifiers. Its keyCode and charCode properties are deprecated in favor of event.key.
Replace keypress with keydown in all cases:
// Old (do not use):
element.addEventListener("keypress", (event) => {
console.log(event.keyCode); // Deprecated
});
// Modern (use this):
element.addEventListener("keydown", (event) => {
console.log(event.key); // Correct
});keydown vs keyup: Which to Choose
Use keydown when... | Use keyup when... |
|---|---|
| You need to block a character before it appears | You need the final value after typing |
| Building keyboard shortcuts that should respond immediately | Tracking when a key is released (like stopping an animation) |
| You need auto-repeat for held keys | You only need to know the key was pressed and released |
| Preventing the default action is required | The action should happen after input is complete |
For most keyboard interactions, including shortcuts, navigation, and game controls, keydown is the right choice because it fires immediately and lets you call preventDefault().
Common Mistakes
Using keypress instead of keydown. The keypress event is deprecated and inconsistent. Always use keydown.
Forgetting to prevent default for browser shortcuts. If your app uses Ctrl+S, Ctrl+P, or similar, call preventDefault() or the browser's built-in behavior will also run.
Using keyup when you need to block input. keyup fires after the character is inserted. If you want to prevent a character from appearing, you must use keydown.
Checking keyCode or charCode. Both are deprecated. Use event.key and event.code instead.
Not allowing navigation keys when filtering input. If you block everything except digits, users cannot press Backspace, arrows, or Tab. Always include navigation keys in your allowlist.
Keyboard events are a core part of /javascript/how-to-add-event-listeners-in-js-complete-guide. For blocking default keyboard behavior, see /javascript/using-preventdefault-in-javascript-full-guide.
Rune AI
Key Insights
- keydown fires when a key is pressed and repeats during hold; keyup fires once on release.
- Use event.key for the character produced ('a', 'Enter'); use event.code for the physical key ('KeyA').
- Detect modifier keys with event.ctrlKey, event.shiftKey, event.altKey, and event.metaKey.
- event.repeat is true when keydown fires from auto-repeat.
- Do not use the deprecated keypress event.
- keydown fires before the character appears in an input, so preventDefault works to block it.
Frequently Asked Questions
What is the difference between event.key and event.code?
Should I still use keypress?
How do keydown and keyup behave when a key is held down?
Conclusion
keydown and keyup are the two keyboard events you need. keydown fires when a key is pressed and repeats while held. keyup fires once when the key is released. Use event.key for character-based detection, event.code for physical key position, and the modifier flags (ctrlKey, shiftKey, altKey, metaKey) for keyboard shortcuts.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.