Keyboard events are essential for creating interactive web applications. This comprehensive guide covers everything you need to know about detecting and handling keyboard input in JavaScript.
Table of Contents
- Keyboard Event Types
- Event Properties
- Detecting Specific Keys
- Modifier Keys
- Building Keyboard Shortcuts
- Best Practices
- Common Patterns
Keyboard Event Types
JavaScript provides three keyboard event types:
keydown
Fires when a key is pressed down. Repeats if key is held.
document.addEventListener('keydown', (event) => {
console.log('Key pressed:', event.key);
});
When to use keydown:
- Detecting key presses for shortcuts
- Game controls
- Real-time input handling
- Most common choice for keyboard interaction
keypress (Deprecated)
Deprecated: The keypress event is deprecated and should not be used. Use keydown instead. It only fires for character keys and has inconsistent behavior across browsers.
keyup
Fires when a key is released.
document.addEventListener('keyup', (event) => {
console.log('Key released:', event.key);
});
When to use keyup:
- Detecting when user stops holding a key
- Game controls (stop moving when key released)
- Toggling states
- Measuring how long a key was held
Event Order
When you press and release a key:
keydown(fires)keydown(repeats if held)keydown(repeats if held)keyup(fires when released)
let keyHeldTime = 0;
let keyDownTime = null;
document.addEventListener('keydown', (event) => {
if (!keyDownTime) {
keyDownTime = Date.now();
}
});
document.addEventListener('keyup', (event) => {
if (keyDownTime) {
keyHeldTime = Date.now() - keyDownTime;
console.log(\`Key held for \${keyHeldTime}ms\`);
keyDownTime = null;
}
});
Event Properties
Modern keyboard events provide several useful properties:
| property | description | example | recommended |
|---|---|---|---|
| event.key | Character pressed | 'a', 'Enter', 'ArrowUp' | ✅ Yes |
| event.code | Physical key location | 'KeyA', 'Enter', 'ArrowUp' | ✅ Yes |
| event.keyCode | Numeric key code | 65, 13, 38 | ❌ Deprecated |
| event.which | Legacy key code | 65, 13, 38 | ❌ Deprecated |
| event.shiftKey | Shift key pressed | true/false | ✅ Yes |
| event.ctrlKey | Ctrl key pressed | true/false | ✅ Yes |
| event.altKey | Alt key pressed | true/false | ✅ Yes |
| event.metaKey | Meta/Cmd key pressed | true/false | ✅ Yes |
event.key vs event.code
Key Difference:
event.keytells you what character was typed ('a', 'A', 'Shift')event.codetells you which physical key was pressed ('KeyA', 'ShiftLeft')
document.addEventListener('keydown', (event) => {
console.log('key:', event.key); // 'a' or 'A' (depends on Shift)
console.log('code:', event.code); // 'KeyA' (always the same)
});
// When pressing 'A' key:
// - event.key = 'a' (without Shift) or 'A' (with Shift)
// - event.code = 'KeyA' (always)
// When pressing '1' key:
// - event.key = '1' (without Shift) or '!' (with Shift)
// - event.code = 'Digit1' (always)
Learn more: keyCode vs key vs code comparison
Detecting Specific Keys
Using event.key (Recommended)
document.addEventListener('keydown', (event) => {
// Single keys
if (event.key === 'Enter') {
console.log('Enter pressed');
}
if (event.key === 'Escape') {
console.log('Escape pressed');
}
if (event.key === ' ') { // Note: space is a single space character
console.log('Space pressed');
}
// Arrow keys
if (event.key === 'ArrowUp') {
console.log('Up arrow pressed');
}
// Letters (case-sensitive)
if (event.key === 'a') {
console.log('Lowercase a pressed');
}
if (event.key === 'A') {
console.log('Uppercase A pressed (or Shift+a)');
}
});
Common Key Values
// Special keys
'Enter' // Enter/Return key
'Escape' // Escape key
'Backspace' // Backspace key
'Tab' // Tab key
' ' // Space (single space character)
'Delete' // Delete key
// Arrow keys
'ArrowUp'
'ArrowDown'
'ArrowLeft'
'ArrowRight'
// Modifier keys
'Shift'
'Control'
'Alt'
'Meta' // Cmd (Mac) or Windows key
// Function keys
'F1', 'F2', 'F3', ... 'F12'
// Letters
'a', 'b', 'c', ... 'z' // Lowercase
'A', 'B', 'C', ... 'Z' // Uppercase (with Shift)
// Numbers
'0', '1', '2', ... '9'
// Special characters (examples)
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')'
View all JavaScript key codes →
Case-Insensitive Key Detection
document.addEventListener('keydown', (event) => {
// Handle both 'a' and 'A'
if (event.key.toLowerCase() === 'a') {
console.log('Letter A pressed (any case)');
}
// Or use event.code for physical key
if (event.code === 'KeyA') {
console.log('A key pressed (regardless of Shift)');
}
});
Modifier Keys
Detecting combinations like Ctrl+S, Shift+Enter, etc.
Ctrl/Cmd Key
document.addEventListener('keydown', (event) => {
// Ctrl on Windows/Linux, Cmd on Mac
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const modifierKey = isMac ? event.metaKey : event.ctrlKey;
if (modifierKey && event.key === 's') {
event.preventDefault(); // Prevent browser's save dialog
console.log('Save shortcut pressed');
saveDocument();
}
});
Multiple Modifiers
document.addEventListener('keydown', (event) => {
// Ctrl+Shift+S (Save As)
if (event.ctrlKey && event.shiftKey && event.key === 's') {
event.preventDefault();
console.log('Save As pressed');
}
// Alt+Shift+F (Format)
if (event.altKey && event.shiftKey && event.key === 'f') {
event.preventDefault();
console.log('Format pressed');
}
// Ctrl+Alt+Delete (don't do this!)
if (event.ctrlKey && event.altKey && event.key === 'Delete') {
console.log('This won\'t work - browser/OS captures this');
}
});
Modifier Helper Function
function getModifierKey(event) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
return isMac ? event.metaKey : event.ctrlKey;
}
// Usage
document.addEventListener('keydown', (event) => {
if (getModifierKey(event) && event.key === 's') {
event.preventDefault();
saveDocument();
}
});
Building Keyboard Shortcuts
Simple Shortcut System
class KeyboardShortcuts {
constructor() {
this.shortcuts = new Map();
this.init();
}
init() {
document.addEventListener('keydown', (event) => {
const key = this.getShortcutKey(event);
const handler = this.shortcuts.get(key);
if (handler) {
event.preventDefault();
handler(event);
}
});
}
getShortcutKey(event) {
const parts = [];
if (event.ctrlKey) parts.push('Ctrl');
if (event.altKey) parts.push('Alt');
if (event.shiftKey) parts.push('Shift');
if (event.metaKey) parts.push('Meta');
parts.push(event.key);
return parts.join('+');
}
register(shortcut, handler) {
this.shortcuts.set(shortcut, handler);
}
unregister(shortcut) {
this.shortcuts.delete(shortcut);
}
}
// Usage
const shortcuts = new KeyboardShortcuts();
shortcuts.register('Ctrl+s', () => {
console.log('Save');
saveDocument();
});
shortcuts.register('Ctrl+Shift+s', () => {
console.log('Save As');
saveDocumentAs();
});
shortcuts.register('Ctrl+z', () => {
console.log('Undo');
undo();
});
shortcuts.register('Ctrl+Shift+z', () => {
console.log('Redo');
redo();
});
Common Keyboard Shortcuts
const commonShortcuts = {
// Editing
'Ctrl+z': 'Undo',
'Ctrl+Shift+z': 'Redo',
'Ctrl+y': 'Redo (alternative)',
'Ctrl+x': 'Cut',
'Ctrl+c': 'Copy',
'Ctrl+v': 'Paste',
'Ctrl+a': 'Select All',
// File operations
'Ctrl+s': 'Save',
'Ctrl+Shift+s': 'Save As',
'Ctrl+o': 'Open',
'Ctrl+n': 'New',
'Ctrl+w': 'Close',
// Search
'Ctrl+f': 'Find',
'Ctrl+h': 'Replace',
'F3': 'Find Next',
'Shift+F3': 'Find Previous',
// Formatting
'Ctrl+b': 'Bold',
'Ctrl+i': 'Italic',
'Ctrl+u': 'Underline',
// Navigation
'Ctrl+Home': 'Go to start',
'Ctrl+End': 'Go to end',
'Ctrl+ArrowLeft': 'Previous word',
'Ctrl+ArrowRight': 'Next word',
};
// Implement these
Object.entries(commonShortcuts).forEach(([shortcut, description]) => {
shortcuts.register(shortcut, () => {
console.log(\`\${description} triggered\`);
// Your handler here
});
});
Best Practices
1. Always Prevent Default for Custom Shortcuts
document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.key === 's') {
event.preventDefault(); // ✅ Prevent browser's save dialog
saveDocument();
}
});
2. Check for Input Elements
document.addEventListener('keydown', (event) => {
// Don't trigger shortcuts when typing in inputs
const target = event.target;
const isInput = target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.isContentEditable;
if (isInput) {
return; // Let user type normally
}
// Handle shortcuts
if (event.key === 'Escape') {
closeModal();
}
});
3. Handle IME Composition
let isComposing = false;
document.addEventListener('compositionstart', () => {
isComposing = true;
});
document.addEventListener('compositionend', () => {
isComposing = false;
});
document.addEventListener('keydown', (event) => {
if (isComposing) {
return; // Ignore keyboard events during IME input
}
// Handle keyboard events
});
4. Use event.code for Games
// For games, use event.code to get physical key position
const keys = {};
document.addEventListener('keydown', (event) => {
keys[event.code] = true;
});
document.addEventListener('keyup', (event) => {
keys[event.code] = false;
});
// Game loop
function gameLoop() {
if (keys['KeyW'] || keys['ArrowUp']) {
moveUp();
}
if (keys['KeyS'] || keys['ArrowDown']) {
moveDown();
}
if (keys['KeyA'] || keys['ArrowLeft']) {
moveLeft();
}
if (keys['KeyD'] || keys['ArrowRight']) {
moveRight();
}
requestAnimationFrame(gameLoop);
}
gameLoop();
5. Clean Up Event Listeners
class KeyboardHandler {
constructor() {
this.handleKeyDown = this.handleKeyDown.bind(this);
this.init();
}
init() {
document.addEventListener('keydown', this.handleKeyDown);
}
handleKeyDown(event) {
// Handle keyboard events
}
destroy() {
document.removeEventListener('keydown', this.handleKeyDown);
}
}
const handler = new KeyboardHandler();
// When component unmounts or page changes:
handler.destroy();
Common Patterns
Form Submission on Enter
const input = document.querySelector('#search');
input.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault();
submitForm();
}
});
Chat Input (Enter to Send, Shift+Enter for New Line)
const chatInput = document.querySelector('#chat-input');
chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage(chatInput.value);
chatInput.value = '';
}
// Shift+Enter allows new line (default behavior)
});
Escape to Close Modal
function openModal() {
const modal = document.querySelector('.modal');
modal.classList.add('open');
const closeOnEscape = (event) => {
if (event.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', closeOnEscape);
}
};
document.addEventListener('keydown', closeOnEscape);
}
function closeModal() {
const modal = document.querySelector('.modal');
modal.classList.remove('open');
}
Arrow Key Navigation
const items = document.querySelectorAll('.list-item');
let selectedIndex = 0;
document.addEventListener('keydown', (event) => {
if (event.key === 'ArrowDown') {
event.preventDefault();
selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
updateSelection();
}
if (event.key === 'ArrowUp') {
event.preventDefault();
selectedIndex = Math.max(selectedIndex - 1, 0);
updateSelection();
}
if (event.key === 'Enter') {
items[selectedIndex].click();
}
});
function updateSelection() {
items.forEach((item, index) => {
item.classList.toggle('selected', index === selectedIndex);
});
items[selectedIndex].scrollIntoView({ block: 'nearest' });
}
Learn more: How to Detect Arrow Keys
Vim-style Navigation
document.addEventListener('keydown', (event) => {
// Only when not in input
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
return;
}
switch(event.key) {
case 'h': // Left
navigateLeft();
break;
case 'j': // Down
navigateDown();
break;
case 'k': // Up
navigateUp();
break;
case 'l': // Right
navigateRight();
break;
case 'g': // Go to top
if (event.shiftKey) { // Shift+G = bottom
scrollToBottom();
} else {
scrollToTop();
}
break;
}
});
Browser Compatibility
Browser Support:
event.key: Chrome 51+, Firefox 23+, Safari 10.1+, Edge 79+event.code: Chrome 48+, Firefox 38+, Safari 10.1+, Edge 79+keydown/keyup: All browsers (universal support)keypress: Deprecated (don't use)
Fallback for Older Browsers
document.addEventListener('keydown', (event) => {
// Modern browsers
if (event.key) {
if (event.key === 'Enter') {
handleEnter();
}
}
// Fallback for old browsers
else if (event.keyCode) {
if (event.keyCode === 13) { // Enter key
handleEnter();
}
}
});
Summary
JavaScript keyboard events are powerful tools for creating interactive web applications. Use event.key for most cases, handle modifiers properly, and always test your keyboard interactions across different scenarios.
Key Takeaways:
✅ Use keydown for most keyboard interactions
✅ Use event.key instead of deprecated keyCode
✅ Always call event.preventDefault() for custom shortcuts
✅ Check if user is typing in an input before handling shortcuts
✅ Use event.code for games (physical key position)
✅ Handle modifier keys (Ctrl, Shift, Alt, Meta) properly
✅ Clean up event listeners when components unmount