keecode logokeecode
JavaScript
javascript keyboard events
keydown keypress keyup
event.key
event.code
keyboard shortcuts javascript
detect keys javascript

JavaScript Keyboard Event Handling: Complete Tutorial

Master JavaScript keyboard events with this comprehensive guide. Learn keydown, keypress, keyup events, event.key, event.code, and build practical keyboard shortcuts.

Updated January 15, 2025

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

  1. Keyboard Event Types
  2. Event Properties
  3. Detecting Specific Keys
  4. Modifier Keys
  5. Building Keyboard Shortcuts
  6. Best Practices
  7. 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)

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:

  1. keydown (fires)
  2. keydown (repeats if held)
  3. keydown (repeats if held)
  4. 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:

propertydescriptionexamplerecommended
event.keyCharacter pressed'a', 'Enter', 'ArrowUp'✅ Yes
event.codePhysical key location'KeyA', 'Enter', 'ArrowUp'✅ Yes
event.keyCodeNumeric key code65, 13, 38❌ Deprecated
event.whichLegacy key code65, 13, 38❌ Deprecated
event.shiftKeyShift key pressedtrue/false✅ Yes
event.ctrlKeyCtrl key pressedtrue/false✅ Yes
event.altKeyAlt key pressedtrue/false✅ Yes
event.metaKeyMeta/Cmd key pressedtrue/false✅ Yes

event.key vs event.code

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

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

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