Что такое событийная модель?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое событийная модель
Событийная модель — это механизм взаимодействия между пользователем, браузером и JavaScript кодом. Она определяет, как события (клики, ввод текста, прокрутка) распространяются через DOM и обрабатываются компонентами.
Основные компоненты события
1. Источник события (Event Target)
Элемент, на котором произошло событие:
<button id="btn">Нажми меня</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click', (event) => {
console.log(event.target); // <button id="btn">...</button>
console.log(event.target === btn); // true
});
</script>
2. Слушатель события (Event Listener)
Функция, которая вызывается при событии:
function handleClick(event) {
console.log('Кнопка нажата!');
}
// Способ 1: addEventListener (рекомендуется)
button.addEventListener('click', handleClick);
// Способ 2: атрибут в HTML (старый стиль)
// <button onclick="handleClick(event)">...</button>
// Способ 3: свойство (устаревает)
button.onclick = handleClick;
3. Объект события (Event Object)
Содержит информацию о событии:
button.addEventListener('click', (event) => {
console.log(event.type); // 'click'
console.log(event.target); // элемент, на котором событие
console.log(event.currentTarget); // элемент, слушатель которого вызовется
console.log(event.timeStamp); // время события
console.log(event.defaultPrevented); // предотвращено ли стандартное поведение
});
Фазы события (Event Phases)
Трёхфазная модель событий:
<div id="outer">
<div id="middle">
<button id="inner">Нажми</button>
</div>
</div>
<script>
const outer = document.getElementById('outer');
const middle = document.getElementById('middle');
const inner = document.getElementById('inner');
// Фаза 1: Capturing (захват) - событие идет ВНИЗ по дереву
outer.addEventListener('click', (e) => {
console.log('Outer - CAPTURING');
}, true); // true = использовать capturing фазу
middle.addEventListener('click', (e) => {
console.log('Middle - CAPTURING');
}, true);
inner.addEventListener('click', (e) => {
console.log('Inner - CAPTURING');
}, true);
// Фаза 2: Target - событие достигло целевого элемента
outer.addEventListener('click', (e) => {
console.log('Outer - TARGET');
});
middle.addEventListener('click', (e) => {
console.log('Middle - TARGET');
});
inner.addEventListener('click', (e) => {
console.log('Inner - TARGET');
});
// Фаза 3: Bubbling (всплытие) - событие идет ВВЕРХ по дереву
outer.addEventListener('click', (e) => {
console.log('Outer - BUBBLING');
});
middle.addEventListener('click', (e) => {
console.log('Middle - BUBBLING');
});
inner.addEventListener('click', (e) => {
console.log('Inner - BUBBLING');
});
// При клике на кнопку выведет:
// Outer - CAPTURING
// Middle - CAPTURING
// Inner - CAPTURING
// Inner - TARGET
// Inner - BUBBLING
// Middle - BUBBLING
// Outer - BUBBLING
Event Bubbling (Всплытие)
Событие распространяется от элемента вверх к родительским элементам:
<div class="container">
<ul id="list">
<li><a href="#">Пункт 1</a></li>
<li><a href="#">Пункт 2</a></li>
<li><a href="#">Пункт 3</a></li>
</ul>
</div>
<script>
// Было бы неудобно:получать слушатель на каждый <a>
// const links = document.querySelectorAll('a');
// links.forEach(link => {
// link.addEventListener('click', handleLinkClick);
// });
// Лучше использовать Event Delegation (делегирование):
const list = document.getElementById('list');
list.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (link) {
event.preventDefault();
console.log('Клик на ссылку:', link.textContent);
}
});
</script>
Использование всплытия:
- Оптимизация памяти (один слушатель вместо сотен)
- Динамические элементы (новые элементы автоматически слушаются)
- Делегирование ответственности
Event Capturing (Захват)
Событие распространяется от корня вниз к целевому элементу:
button.addEventListener('click', (e) => {
console.log('Bubbling');
}, false); // false или omitted = bubbling фаза
button.addEventListener('click', (e) => {
console.log('Capturing');
}, true); // true = capturing фаза
// При клике выведет:
// Capturing
// Bubbling
Capturing используется редко (в основном для специальных целей).
Остановка распространения события
button.addEventListener('click', (event) => {
// Остановить ВСПЛЫТИЕ события
event.stopPropagation();
// Остановить стандартное поведение (не используется с bubbling/capturing)
event.preventDefault();
// Остановить ВСЁ (и bubbling и другие слушатели)
event.stopImmediatePropagation();
});
Пример с реальным кейсом:
// Модальное окно
const modal = document.getElementById('modal');
const backdrop = document.getElementById('backdrop');
backdrop.addEventListener('click', (e) => {
// Закрыть модаль при клике на фон
closeModal();
});
modal.addEventListener('click', (e) => {
// Но НЕ закрыть при клике внутри модали
e.stopPropagation();
});
Частые события
// Мышь
button.addEventListener('click', handler); // клик
button.addEventListener('dblclick', handler); // двойной клик
button.addEventListener('mousedown', handler); // нажата кнопка мыши
button.addEventListener('mouseup', handler); // отпущена кнопка мыши
button.addEventListener('mouseover', handler); // мышь над элементом
button.addEventListener('mouseout', handler); // мышь ушла из элемента
button.addEventListener('mousemove', handler); // движение мыши
// Клавиатура
input.addEventListener('keydown', handler); // нажата клавиша
input.addEventListener('keyup', handler); // отпущена клавиша
input.addEventListener('keypress', handler); // нажата символьная клавиша (устаревает)
// Форма
form.addEventListener('submit', handler); // отправка формы
input.addEventListener('change', handler); // изменилось значение
input.addEventListener('input', handler); // вводится текст (в реальном времени)
// Документ
document.addEventListener('DOMContentLoaded', handler); // DOM готов
window.addEventListener('load', handler); // страница полностью загружена
window.addEventListener('scroll', handler); // прокрутка
В React
React имеет свою событийную систему (SyntheticEvent):
function Button() {
const handleClick = (event) => {
console.log(event.type); // 'click'
console.log(event.target); // DOM элемент
event.preventDefault(); // стандартное behavior
event.stopPropagation(); // остановить bubbling
};
return <button onClick={handleClick}>Нажми</button>;
}
Важно: React 17+ использует собственные события на корневом элементе (event delegation).
Event Delegation в React
function TodoList() {
const handleTodoClick = (event) => {
const todoId = event.currentTarget.dataset.id;
console.log('Клик на todo:', todoId);
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id} data-id={todo.id} onClick={handleTodoClick}>
{todo.title}
</li>
))}
</ul>
);
}
Custom Events (Пользовательские события)
Можно создавать свои события:
// Создание события
const event = new CustomEvent('myEvent', {
detail: { message: 'Привет!' }
});
// Отправка события
button.dispatchEvent(event);
// Слушание события
button.addEventListener('myEvent', (e) => {
console.log(e.detail.message); // 'Привет!'
});
Практический пример:
// Компонент отправляет событие
class DataLoader {
async loadData() {
const data = await fetch('/api/data');
// Отправляем событие после загрузки
const event = new CustomEvent('dataLoaded', {
detail: { data }
});
document.dispatchEvent(event);
}
}
// Другой компонент слушает событие
document.addEventListener('dataLoaded', (e) => {
console.log('Данные загружены:', e.detail.data);
});
Лучшие практики
1. Используйте addEventListener вместо onclick атрибута
// Плохо
// <button onclick="handleClick()">Нажми</button>
// Хорошо
button.addEventListener('click', handleClick);
2. Используйте Event Delegation для динамических элементов
// Плохо - не работает для новых элементов
const items = document.querySelectorAll('.item');
items.forEach(item => {
item.addEventListener('click', handleItem);
});
// Хорошо - работает для новых элементов
container.addEventListener('click', (e) => {
const item = e.target.closest('.item');
if (item) handleItem(e);
});
3. Всегда удаляйте слушатели при очистке
function MyComponent() {
useEffect(() => {
const handleResize = () => console.log('resize');
window.addEventListener('resize', handleResize);
return () => {
// Очистка важна!
window.removeEventListener('resize', handleResize);
};
}, []);
}
4. Избегайте множественных addEventListener с одной функцией
// Сложно удалить
button.addEventListener('click', handler);
button.addEventListener('click', handler); // Добавится ещё раз
// Правильно
function setupButton() {
button.addEventListener('click', handler);
}
function teardownButton() {
button.removeEventListener('click', handler);
}
Итог
Событийная модель включает:
- Источник события (целевой элемент)
- Слушатель события (функция-обработчик)
- Объект события (информация о событии)
- Фазы события (capturing, target, bubbling)
Ключевые моменты:
- События всплывают вверх по DOM
- Event Delegation экономит память
- Всегда удаляйте слушатели после использования
- В React используйте синтетические события
- Для специальных нужд создавайте Custom Events