Урок 09

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

Клавиатура

onKeyDown

Срабатывает при нажатии:

function Input() {
    function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
        if (e.key === 'Enter') {
            console.log('Enter нажат');
        }
        if (e.key === 'Escape') {
            console.log('Escape');
        }
    }

    return <input onKeyDown={handleKeyDown} />;
}

Свойства события

  • e.key — значение клавиши: 'a', 'Enter', 'Escape', 'ArrowUp'
  • e.code — физическая клавиша: 'KeyA', 'Enter', 'ArrowUp'
  • e.ctrlKey, e.shiftKey, e.altKey, e.metaKey — модификаторы
  • e.repeat — повторное нажатие

Комбинации

function handleKeyDown(e: React.KeyboardEvent) {
    if (e.ctrlKey && e.key === 's') {
        e.preventDefault();
        console.log('Сохранить');
    }

    if (e.metaKey && e.key === 'k') {
        console.log('Открыть поиск');
    }
}

На Mac Ctrl — это metaKey.

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

<input
    onKeyDown={(e) => {
        if (!/^[0-9]$/.test(e.key) && e.key !== 'Backspace' && e.key !== 'Tab') {
            e.preventDefault();
        }
    }}
/>

Enter в input

<input
    value={query}
    onChange={(e) => setQuery(e.target.value)}
    onKeyDown={(e) => {
        if (e.key === 'Enter') {
            search(query);
        }
    }}
/>

Мышь

onClick

<button onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
    console.log(e.clientX, e.clientY);
}}>Клик</button>

Свойства события

  • e.clientX, e.clientY — координаты относительно окна
  • e.pageX, e.pageY — относительно документа
  • e.button — 0 (левая), 1 (средняя), 2 (правая)
  • e.shiftKey, e.ctrlKey, e.altKey — модификаторы

Правый клик

<div onContextMenu={(e) => {
    e.preventDefault();
    console.log('Правый клик');
}}>...</div>

Наведение

<div
    onMouseEnter={() => setHovered(true)}
    onMouseLeave={() => setHovered(false)}
>
    {hovered ? 'Наведено' : 'Наведи'}
</div>

onMouseEnter/onMouseLeave не всплывают. onMouseOver/onMouseOut — всплывают.

Движение мыши

<div onMouseMove={(e) => setPosition({ x: e.clientX, y: e.clientY })} />

Вызывается часто — следи за производительностью.

Двойной клик

<div onDoubleClick={() => console.log('двойной')} />

Глобальные события

События на window или document подписываются через useEffect:

useEffect(() => {
    function handleKeyDown(e: KeyboardEvent) {
        if (e.key === 'Escape') closeModal();
    }

    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
}, []);

Фокус

<input
    onFocus={() => setFocused(true)}
    onBlur={() => setFocused(false)}
/>

onFocus — при получении фокуса, onBlur — при потере.

Фокус можно поставить программно через ref:

const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus();

Скролл

useEffect(() => {
    function handleScroll() {
        console.log(window.scrollY);
    }

    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => window.removeEventListener('scroll', handleScroll);
}, []);

{ passive: true } сообщает браузеру, что preventDefault не будет вызван — улучшает производительность.

Копирование

<input
    onCopy={() => console.log('скопировано')}
    onPaste={(e) => {
        e.preventDefault();
        const text = e.clipboardData.getData('text');
        console.log(text);
    }}
/>

Итоги

  • onKeyDown с e.key для клавиш
  • Комбинации через e.ctrlKey, e.metaKey
  • onClick, onMouseEnter, onMouseLeave
  • Глобальные события — через useEffect
  • onFocus/onBlur для фокуса
  • onScroll с { passive: true }