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.

5 min read

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.

Keyboard event sequence: press and release

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:

javascriptjavascript
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:

texttext
keydown: a (repeat: false)
keyup: a

Hold "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:

javascriptjavascript
document.addEventListener("keydown", (event) => {
  console.log(event.key);
});
Key Pressedevent.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:

javascriptjavascript
document.addEventListener("keydown", (event) => {
  console.log(event.code);
});
Key Pressed (QWERTY)Same Key (AZERTY)event.code (both)
AQ"KeyA"
EnterEnter"Enter"
Arrow LeftArrow Left"ArrowLeft"
Digit 1&"Digit1"
EscapeEscape"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

SituationUse
Keyboard shortcut like Ctrl+Sevent.key (checks for "s")
WASD game movementevent.code (checks physical position)
Detecting Enter to submit a formevent.key === "Enter"
Preventing non-numeric inputevent.key and a regex
Arrow key navigationEither, both return the same for arrows

Detecting Modifier Keys

The event object has boolean flags for modifier keys:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
// 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 appearsYou need the final value after typing
Building keyboard shortcuts that should respond immediatelyTracking when a key is released (like stopping an animation)
You need auto-repeat for held keysYou only need to know the key was pressed and released
Preventing the default action is requiredThe 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

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.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between event.key and event.code?

event.key returns the character the key produces ('a', 'A', 'Enter'). event.code returns the physical key location on the keyboard ('KeyA', 'Enter'), ignoring layout. Use event.key for character-based logic and event.code for position-based logic like game controls.

Should I still use keypress?

No. The keypress event is deprecated. It does not fire for non-printable keys like arrows or modifiers, and its behavior varies across browsers. Use keydown for all keyboard handling instead.

How do keydown and keyup behave when a key is held down?

When you hold a key, keydown fires repeatedly (auto-repeat). keyup fires only once when the key is released. Use event.repeat to check if a keydown is from auto-repeat.

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.