В веб-разработке часто возникает необходимость обрабатывать различные типы взаимодействий пользователя, включая короткие и длинные нажатия. В этой статье мы рассмотрим изящный способ реализации обработчиков для этих двух взаимодействий.
▎Зачем нужен Long Press?
Long Press — это взаимодействие, при котором пользователь удерживает палец на экране или кнопку в течение определенного времени. Это может быть полезно для вызова дополнительных действий, таких как контекстное меню, всплывающие подсказки или специальные функции, которые не должны выполняться при обычном клике.
▎Стандартное решение
Реализация обработчиков долгого и короткого нажатия на чистом JavaScript будет выглядеть примерно следующим образом:
/* HTML */
<button id="btn">Click / Long Press</button>
/* JavaScript */
const btn = document.getElementById('btn');
const clickHandler = () => console.log('Сlick Handler');
const longPressHandler = () => console.log('Long Press Handler');
let timerId, longPressed
btn.onmousedown = () => {
longPressed = false
timerId = setTimeout(() => {
longPressed = true
longPressHandler();
}, 200);
}
btn.onclick = () => {
if (!longPressed) clickHandler();
clearTimeout(timerId);
}
btn.onmouseleave = () => {
clearTimeout(timerId);
}
Как мы видим, код достаточно громоздкий; необходимо использование таймера и глобальных переменных. Также нужно учитывать эффект, когда пользователь нажал на элемент, а отпустил его вне границ — такое поведение не должно вызывать ни один из наших обработчиков. Здесь это отслеживается с помощью слушателя mouseleave.
Проблема реализации только с помощью JavaScript заключается в том, что, как правило, после длительного нажатия будет следовать применение каких-либо стилей к элементу, а значит, нам придется писать дополнительный код на CSS.
▎Универсальное решение
Давайте рассмотрим, как мы можем реализовать обработчики короткого и длинного нажатия только с помощью CSS:
/* CSS */
#btn {
transition: background-color 1s 200ms; /* animation for long press */
}
#btn:active {
animation-name: interruptClick;
animation-delay: 200ms;
animation-fill-mode: forwards;
background-color: PaleTurquoise; /* styles for long press */
}
#btn:not(:hover){
animation-play-state: paused;
}
@keyframes interruptClick {
to {
pointer-events: none;
}
}
/* HTML */
<button id="btn">Click / Long Press</button>
/* JavaScript */
const btn = document.getElementById('btn');
btn.onclick = () => console.log('Сlick Handler');
btn.onanimationend = () => console.log('Long Press Handler');
Здесь мы видим, что логика определения долгого и короткого нажатия реализована полностью на CSS. С помощью JavaScript мы только устанавливаем слушателей. При долгом нажатии будет применена отдельная анимация стилизации кнопки (transition: background-color).
▎Как это работает?
1. CSS-анимация для реализации обработчиков:
:active: Этот псевдокласс позволяет применять свойства анимации только в момент удержания кнопки.
animation-name: Определяем @keyframes анимацию с одним ключевым кадром.
pointer-events: Ключевой кадр содержит свойство, управляющее тем, как элемент реагирует на события указателя (например, клики мышью). Клик считается завершенным, когда происходит и нажатие, и отпускание кнопки мыши на одном и том же элементе. Мы используем это свойство со значением “none”, чтобы предотвратить событие клика.
animation-delay: Анимация начинается через 200 миллисекунд после нажатия; это минимальное время удержания, необходимое для его определения как длительного.
animation-fill-mode: Время продолжительности анимации не указано, но благодаря значению “forwards” все свойства ключевого кадра будут оставаться активными до окончания удержания.
:not(:hover): Если пользователь нажал на кнопку, а затем отпустил за её границами, то анимация interruptClick будет остановлена с помощью свойства animation-play-state: paused, и ни один из обработчиков не будет вызван.
2. JavaScript слушатели:
onclick: Этот слушатель соответствует короткому нажатию.
onanimationend: Этот слушатель соответствует поведению долгого нажатия и срабатывает, когда заканчивается анимация interruptClick.
▎Преимущества данного подхода
• Универсальность: Все визуальные эффекты при длинном нажатии можно реализовать только с помощью CSS.
• Простота: Этот метод не требует сложных библиотек или дополнительных зависимостей.
• Эффективность: Использование CSS-анимаций позволяет избежать лишних вычислений в JavaScript, что может улучшить производительность.
• Чистота кода: Код остается чистым и легко читаемым.
▎Заключение
Эта реализация демонстрирует, как можно использовать возможности CSS в сочетании с JavaScript и как их взаимодействие позволяет писать более лаконичный код.
Я протестировал это решение в нескольких браузерах, на компьютере и мобильном устройстве. Перед использованием кода в продакшене рекомендую провести дополнительное тестирование.