События клавиатуры: keydown, keyup

События keydown и keyup возникают при нажатии и отпускании клавиши; они содержат информацию о нажатой клавише (key, code) и модификаторах (ctrlKey, shiftKey, altKey).

Зачем нужно

Обработка клавиатурных событий необходима для создания интерактивных интерфейсов: горячих клавиш, навигации с клавиатуры, игровых управлений, редакторов кода, форм с мгновенной обратной связью. Без них невозможна доступность (a11y) и удобство работы с приложением.

Где используется

  • Горячие клавиши (Ctrl+S, Escape, Enter)
  • Навигация по спискам стрелками
  • Игры и интерактивные демо
  • Автодополнение в полях ввода
  • Закрытие модальных окон по Escape

Базовые события

Событие Когда Повторяется при удержании
keydown Клавиша нажата Да
keyup Клавиша отпущена Нет
keypress Устаревшее, не использовать
document.addEventListener('keydown', (event) => {
  console.log('key:', event.key);   // 'a', 'Enter', 'ArrowLeft', ' '
  console.log('code:', event.code); // 'KeyA', 'Enter', 'ArrowLeft', 'Space'
  console.log('keyCode:', event.keyCode); // устаревшее, 65 для 'a'
});

key vs code

// key — символ или действие (зависит от раскладки и регистра)
// 'a', 'A', 'й', 'Й', 'Enter', 'ArrowUp', ' '

// code — физическая клавиша (не зависит от раскладки)
// 'KeyA', 'Enter', 'ArrowUp', 'Space', 'Digit1', 'F1'

// Для горячих клавиш используй code:
document.addEventListener('keydown', (e) => {
  if (e.code === 'KeyS' && (e.ctrlKey || e.metaKey)) {
    e.preventDefault();
    save;
  }
});

// Для ввода пользователем используй key:
input.addEventListener('keydown', (e) => {
  if (e.key === 'Enter') submitForm;
});

Модификаторы

document.addEventListener('keydown', (e) => {
  console.log({
    ctrlKey:  e.ctrlKey,  // Ctrl (Windows/Linux) или Control (Mac)
    metaKey:  e.metaKey,  // Cmd (Mac) или Win (Windows)
    shiftKey: e.shiftKey, // Shift
    altKey:   e.altKey    // Alt / Option
  });

  // Ctrl+Z или Cmd+Z (undo)
  if ((e.ctrlKey || e.metaKey) && e.code === 'KeyZ') {
    e.preventDefault();
    undo;
  }

  // Shift+Enter — новая строка в чате
  if (e.shiftKey && e.key === 'Enter') {
    e.preventDefault();
    insertNewline;
  }
});

Навигация по списку стрелками

const items = document.querySelectorAll('.menu-item');
let activeIndex = 0;

document.addEventListener('keydown', (e) => {
  switch (e.key) {
    case 'ArrowDown':
      e.preventDefault();
      activeIndex = (activeIndex + 1) % items.length;
      items[activeIndex].focus();
      break;
    case 'ArrowUp':
      e.preventDefault();
      activeIndex = (activeIndex - 1 + items.length) % items.length;
      items[activeIndex].focus();
      break;
    case 'Escape':
      closeMenu;
      break;
    case 'Enter':
      items[activeIndex].click();
      break;
  }
});

Удержание клавиши (keydown repeat)

document.addEventListener('keydown', (e) => {
  if (e.repeat) {
    // Клавиша удерживается (keydown срабатывает повторно)
    console.log('Удерживается:', e.key);
  }
});

// Отслеживание нажатых клавиш для игр
const pressed = new Set();

document.addEventListener('keydown', (e) => pressed.add(e.code));
document.addEventListener('keyup', (e) => pressed.delete(e.code));

function gameLoop() {
  if (pressed.has('ArrowLeft')) moveLeft;
  if (pressed.has('ArrowRight')) moveRight;
  requestAnimationFrame(gameLoop);
}

Частые ошибки

1. Использование keypress (устаревший)

// keypress не срабатывает для служебных клавиш (Delete, Arrow, F1...)
document.addEventListener('keypress', handler); // устаревший!

// Используй keydown для всего
document.addEventListener('keydown', handler);

2. Блокировка стандартного поведения без нужды

// Плохо: блокируем Copy/Paste
document.addEventListener('keydown', (e) => {
  e.preventDefault(); // блокирует ВСЕ клавиши!
});

// Хорошо: блокируем только конкретную комбинацию
document.addEventListener('keydown', (e) => {
  if (e.key === 'F5') e.preventDefault(); // запрет обновления
});

3. Зависимость от keyCode (устаревший)

// keyCode — устаревший, не используй
if (e.keyCode === 13) {} // плохо

// Современный способ:
if (e.key === 'Enter') {} // хорошо
if (e.code === 'Enter') {} // хорошо

Связанные темы

Ресурсы