События указателя и мыши в JavaScript
Когда пользователь нажимает кнопку, двигает курсор или проводит пальцем по экрану, браузер создаёт события. Они помогают сделать интерфейс отзывчивым: показать подсказку, отследить движение или начать пользовательский жест.
Мышь - только один из видов указателя. В современных интерфейсах пользователь может работать мышью, пальцем на сенсорном экране или стилусом. Поэтому для нового взаимодействия часто выбирают события указателя.
- Указатель и мышь - не одно и то же
- Какое событие выбрать
- Нажатие, движение и отпускание указателя
- Наведение на элемент
- Координаты и тип устройства
- Итого
Указатель и мышь - не одно и то же
События указателя начинаются с pointer: pointerdown, pointermove, pointerup и другие. Они работают с мышью, касанием и стилусом через один набор API.
События мыши - mousedown, mousemove, mouseup, mouseenter - по-прежнему встречаются в существующем коде. Их стоит знать, но для нового взаимодействия, которое должно одинаково работать на компьютере и телефоне, обычно удобнее события указателя.
Какое событие выбрать
| Задача | Подходящее событие |
|---|---|
| Обычное действие кнопки или ссылки | click |
| Начало, движение и завершение пользовательского жеста | pointerdown, pointermove, pointerup |
| Наведение на компонент | pointerenter и pointerleave |
| Поведение, связанное именно с мышью или старым кодом | mouse*-события |
Для обычной кнопки выбирайте click, а не pointerdown. click означает активацию элемента: оно сработает не только при щелчке мышью, но и при касании, а у нативной кнопки - при активации с клавиатуры.
contextmenu сообщает, что пользователь запросил контекстное меню. Обычно это происходит по правой кнопке мыши, но событие может прийти и другим способом, например с клавиатуры.
Не назначайте один и тот же обработчик одновременно наpointerdownиmousedown. При работе мышью браузер вызовет оба события, и код может выполниться дважды.
Нажатие, движение и отпускание указателя
pointerdown происходит в момент нажатия, pointermove - при изменении положения указателя, а pointerup - при отпускании. Их мышиные аналоги называются mousedown, mousemove и mouseup.
Эта тройка нужна не для обычного клика, а для взаимодействия, в котором важен сам жест: рисование, изменение размера блока, ползунок или перетаскивание.
Если логика начинается на pointerdown, учитывайте и pointercancel. Браузер может отменить последовательность событий, например когда распознаёт прокрутку страницы вместо пользовательского жеста.
Наведение на элемент
Чтобы отследить вход указателя на компонент и выход за его пределы, используйте pointerenter и pointerleave. Для событий мыши существуют пары mouseenter и mouseleave.
<article class="product-card">
<h3>Курс по JavaScript</h3>
<button type="button">Подробнее</button>
</article>
.product-card {
padding: 20px;
border: 1px solid #d1d5db;
transition: border-color 0.2s, box-shadow 0.2s;
}
.product-card.is-hovered {
border-color: #2f855a;
box-shadow: 0 6px 18px rgb(47 133 90 / 20%);
}
const productCard = document.querySelector('.product-card');
productCard.addEventListener('pointerenter', function (event) {
event.currentTarget.classList.add('is-hovered');
});
productCard.addEventListener('pointerleave', function (event) {
event.currentTarget.classList.remove('is-hovered');
});
Класс добавляется при входе указателя на карточку и удаляется при выходе. Переход указателя с заголовка на кнопку внутри карточки не вызовет повторное срабатывание этих обработчиков.
pointerover и pointerout, как и mouseover и mouseout, реагируют на переходы между вложенными элементами и всплывают. Они нужны, когда такое поведение действительно требуется. Для обычного наведения на целый компонент выбирайте pointerenter и pointerleave.
Если нужен только визуальный эффект наведения, обычно достаточно CSS-псевдокласса :hover. Не делайте важное действие доступным только при наведении: на сенсорных устройствах постоянного состояния наведения нет.
Координаты и тип устройства
<div class="pointer-area">
Проведите указателем по этой области
<output class="coordinates">Координаты появятся здесь</output>
</div>
.pointer-area {
min-height: 160px;
padding: 20px;
border: 1px dashed #2f855a;
border-radius: 8px;
}
.coordinates {
display: block;
margin-top: 12px;
font-family: monospace;
}
const pointerArea = document.querySelector('.pointer-area');
const coordinates = document.querySelector('.coordinates');
pointerArea.addEventListener('pointermove', function (event) {
const rect = event.currentTarget.getBoundingClientRect();
const x = Math.round(event.clientX - rect.left);
const y = Math.round(event.clientY - rect.top);
coordinates.textContent = 'Тип: ' + event.pointerType + ', X: ' + x + ', Y: ' + y;
});
clientX и clientY содержат координаты внутри видимой области окна браузера. В примере мы вычитаем положение блока, поэтому получаем координаты внутри самой области.
pageX и pageY считают координаты относительно всего документа с учётом прокрутки. Свойство pointerType обычно содержит ‘mouse’, ‘touch’ или ‘pen’.
pointermove может срабатывать много раз за секунду. В таком обработчике оставляйте только лёгкие действия: обновление координат, положения элемента или состояния интерфейса. Тяжёлые вычисления и запросы к серверу лучше выполнять отдельно.
Итого
Для обычной активации кнопки используйте click. Когда нужно отслеживать нажатие, движение или отпускание указателя на разных устройствах, выбирайте pointerdown, pointermove и pointerup. События мыши остаются полезными для мышеспецифичного поведения и при работе со старым кодом.