Урок 07

Клавиатура и мышь

События клавиатуры

  • keydown — нажатие
  • keyup — отпускание
document.addEventListener('keydown', (event) => {
    console.log(event.key);  // 'a', 'Enter', 'Escape'
    console.log(event.code); // 'KeyA', 'Enter', 'Escape'
});

key vs code

  • key — символ с учётом раскладки
  • code — физическая клавиша
// Русская раскладка
event.key;  // 'ф' (или 'a' на английской)
event.code; // 'KeyA' — всегда

Для горячих клавиш используй code — не зависит от раскладки.

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

if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    save();
}

if (event.metaKey && event.key === 'k') {
    openSearch(); // Cmd+K на Mac
}

if (event.shiftKey && event.key === 'Enter') {}
if (event.altKey) {}

На Mac Ctrl — это metaKey.

Escape и Enter

document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape') closeModal();
    if (event.key === 'Enter') submit();
});

Ограничение ввода

input.addEventListener('keydown', (event) => {
    if (!/^[0-9]$/.test(event.key) && event.key !== 'Backspace' && event.key !== 'Tab') {
        event.preventDefault();
    }
});

События мыши

  • click — клик
  • dblclick — двойной
  • mousedown, mouseup — нажатие/отпускание
  • mousemove — движение
  • mouseover, mouseout — вход/выход (всплывают)
  • mouseenter, mouseleave — вход/выход (не всплывают)
  • contextmenu — правый клик

Координаты

element.addEventListener('click', (event) => {
    event.clientX; // относительно окна
    event.clientY;
    event.pageX;   // относительно документа (с прокруткой)
    event.pageY;
    event.offsetX; // относительно элемента
    event.offsetY;
});

Кнопки мыши

element.addEventListener('mousedown', (event) => {
    event.button; // 0 — левая, 1 — средняя, 2 — правая
});

Правый клик

document.addEventListener('contextmenu', (event) => {
    event.preventDefault();
    showCustomMenu(event.pageX, event.pageY);
});

mouseover vs mouseenter

// mouseover срабатывает при переходе на дочерние
parent.addEventListener('mouseover', () => console.log('over'));

// mouseenter — только при входе в сам элемент
parent.addEventListener('mouseenter', () => console.log('enter'));

mouseenter обычно нужен.

Drag and drop

draggable.addEventListener('dragstart', (event) => {
    event.dataTransfer.setData('text/plain', 'data');
    event.dataTransfer.effectAllowed = 'move';
});

dropZone.addEventListener('dragover', (event) => {
    event.preventDefault(); // обязательно
});

dropZone.addEventListener('drop', (event) => {
    event.preventDefault();
    const data = event.dataTransfer.getData('text/plain');
});

Pointer events

Современная альтернатива — мышь, касания, перо:

element.addEventListener('pointerdown', (event) => {});
element.addEventListener('pointermove', (event) => {});
element.addEventListener('pointerup', (event) => {});

Один API для всех устройств.

Touch

element.addEventListener('touchstart', (event) => {
    const touch = event.touches[0];
    console.log(touch.clientX, touch.clientY);
});

Практика: горячие клавиши

const shortcuts = {
    'ctrl+s': () => save(),
    'ctrl+z': () => undo(),
    'ctrl+shift+z': () => redo(),
};

document.addEventListener('keydown', (event) => {
    const parts = [];
    if (event.ctrlKey || event.metaKey) parts.push('ctrl');
    if (event.shiftKey) parts.push('shift');
    if (event.altKey) parts.push('alt');
    parts.push(event.key.toLowerCase());

    const combo = parts.join('+');

    if (shortcuts[combo]) {
        event.preventDefault();
        shortcuts[combo]();
    }
});

Итоги

  • keydown, keyup для клавиатуры
  • key — символ, code — физическая клавиша
  • Модификаторы: ctrlKey, shiftKey, altKey, metaKey
  • mouseenter/mouseleave не всплывают
  • Pointer events — единый API
  • Горячие клавиши через комбинации