События мыши: click, mouseover, mouseenter
События мыши позволяют реагировать на взаимодействие пользователя с указателем: клики, наведение, движение, нажатие и отпускание кнопок мыши; каждое событие несёт координаты, информацию о кнопке и модификаторах.
Зачем нужно
Большинство пользовательских взаимодействий в браузере — события мыши. Понимание разницы между всплывающими и невсплывающими событиями (mouseover vs mouseenter) позволяет выбрать правильный подход и не создавать лишних обработчиков.
Где используется
- Интерактивные кнопки, меню, карточки
- Drag and drop
- Кастомные контекстные меню
- Tooltip и popup при наведении
- Canvas рисование
Основные события мыши
| Событие | Когда | Всплывает |
|---|---|---|
click |
Нажата и отпущена левая кнопка | Да |
dblclick |
Двойной клик | Да |
mousedown |
Кнопка нажата | Да |
mouseup |
Кнопка отпущена | Да |
mousemove |
Курсор движется | Да |
mouseover |
Вход курсора (в том числе в потомков) | Да |
mouseout |
Выход курсора | Да |
mouseenter |
Вход курсора в элемент | Нет |
mouseleave |
Выход курсора из элемента | Нет |
contextmenu |
Правый клик | Да |
click и свойства события
const button = document.querySelector('#btn');
button.addEventListener('click', (event) => {
// Координаты клика
console.log('Относительно viewport:', event.clientX, event.clientY);
console.log('Относительно страницы:', event.pageX, event.pageY);
console.log('Относительно элемента:', event.offsetX, event.offsetY);
// Кнопка мыши: 0=левая, 1=средняя, 2=правая
console.log('Кнопка:', event.button);
// Модификаторы
console.log('Ctrl:', event.ctrlKey, 'Shift:', event.shiftKey);
// Цель события
console.log('Элемент:', event.target);
console.log('Текущий обработчик:', event.currentTarget);
});
mouseover vs mouseenter (ключевое отличие)
<div class="parent">
<span class="child">Дочерний</span>
</div>
const parent = document.querySelector('.parent');
// mouseover — срабатывает при входе в parent И при входе в child (всплывает)
parent.addEventListener('mouseover', () => {
console.log('mouseover'); // сработает 2 раза: при входе в parent и в child
});
// mouseenter — срабатывает ТОЛЬКО при первом входе в parent (не всплывает)
parent.addEventListener('mouseenter', () => {
console.log('mouseenter'); // сработает 1 раз — при входе в parent
});
// Правило: для hover-эффектов на элемент с потомками используй mouseenter/mouseleave
Tooltip при наведении
const card = document.querySelector('.product-card');
const tooltip = document.querySelector('.tooltip');
card.addEventListener('mouseenter', () => {
tooltip.style.display = 'block';
});
card.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
});
// Позиционирование тултипа по курсору
card.addEventListener('mousemove', (e) => {
tooltip.style.left = `${e.pageX + 10}px`;
tooltip.style.top = `${e.pageY + 10}px`;
});
Делегирование событий
// Вместо обработчика на каждом элементе — один на родителе
const list = document.querySelector('.item-list');
list.addEventListener('click', (event) => {
// Находим ближайшего предка с классом .item
const item = event.target.closest('.item');
if (!item) return;
const id = item.dataset.id;
console.log('Клик по item:', id);
});
Правый клик и contextmenu
document.addEventListener('contextmenu', (event) => {
event.preventDefault(); // отменяем стандартное меню браузера
showCustomMenu(event.clientX, event.clientY);
});
document.addEventListener('click', () => {
hideCustomMenu; // закрываем при клике вне меню
});
Частые ошибки
1. Путаница mouseover / mouseenter
// Если parent большой, а внутри много потомков — mouseover будет срабатывать
// при каждом пересечении границ дочерних элементов
// Используй mouseenter/mouseleave для hover-состояний
2. Обработчик на каждом элементе списка
// Плохо: 1000 обработчиков для 1000 элементов
items.forEach(item => item.addEventListener('click', handler));
// Хорошо: 1 обработчик + делегирование
container.addEventListener('click', (e) => {
const item = e.target.closest('[data-id]');
if (item) handler(item.dataset.id);
});
3. Использование onclick (атрибут HTML)
// Плохо: только один обработчик, смешение разметки и логики
// <button onclick="doSomething">...</button>
// Хорошо: addEventListener — несколько обработчиков, разделение
button.addEventListener('click', doSomething);
Связанные темы
- Событие scroll
- События клавиатуры -- keydown, keyup
- Пользовательские события -- CustomEvent
- DOM дерево
- _MOC DOM