"Ты властен над своим кодом, но не над действиями пользователя. Когда поймёшь это, ты станешь настоящим программистом."
Что такое события и зачем они нужны?
Привет! Представь, что ты создал классную веб-страницу с кнопками, полями для ввода текста и другими элементами. Но как сделать так, чтобы что-то происходило, когда пользователь нажимает на кнопку или вводит текст? Здесь на помощь приходят события JavaScript!
События - это сигналы, которые браузер посылает, когда что-то происходит на странице. Например:
- Пользователь кликает мышкой
- Нажимает клавишу на клавиатуре
- Прокручивает страницу
- Заполняет форму
Без обработки событий веб-страницы были бы просто как картинки в книге - красивые, но не интерактивные.
Обработчики событий - как поймать событие
Чтобы реагировать на события, мы создаём специальные функции, которые называются обработчиками событий. Они "ловят" события и говорят браузеру, что делать, когда событие происходит.
Давай разберём 3 способа создания обработчиков событий:
Способ 1: Прямо в HTML-коде
Самый простой способ - добавить атрибут в HTML-тег. Атрибуты начинаются с "on", например onclick, onmouseover:
html<button onclick="alert('Привет! Ты нажал на кнопку!')">Нажми меня</button>
Или можно вызвать функцию, которую определили в JavaScript:
html<button onclick="sayHello()">Поздороваться</button>
<script>
function sayHello() {
alert('Привет, мой юный программист!');
}
</script>
Способ 2: Через свойства DOM-элемента
Элементы на странице можно найти с помощью JavaScript и назначить им обработчики:
javascript// Находим кнопку
let button = document.querySelector('button');
// Назначаем обработчик клика
button.onclick = function() {
alert('Кнопка нажата!');
};
// Можно и так:
function showMessage() {
alert('Привет из функции!');
}
button.onclick = showMessage; // Обрати внимание: без скобок!
Если нужно удалить обработчик:
javascriptbutton.onclick = null; // Обработчик удалён
Способ 3: Метод addEventListener (самый крутой!)
Первые два способа имеют ограничение - с их помощью можно назначить только один обработчик на одно событие. Метод addEventListener позволяет добавлять сколько угодно обработчиков:
javascriptlet button = document.querySelector('button');
// Добавляем первый обработчик
button.addEventListener('click', function() {
console.log('Первый обработчик: Привет!');
});
// Добавляем ещё один обработчик на то же событие
button.addEventListener('click', function() {
console.log('Второй обработчик: И от меня привет!');
});
Чтобы удалить обработчик, используем removeEventListener:
javascriptfunction sayHi() {
alert('Привет!');
}
// Добавляем обработчик
button.addEventListener('click', sayHi);
// Позже, когда нам он больше не нужен:
button.removeEventListener('click', sayHi);
Важно! Нельзя удалить обработчик, если мы не сохранили функцию в переменную:
javascript// ЭТОТ ОБРАБОТЧИК НЕЛЬЗЯ БУДЕТ УДАЛИТЬ:
button.addEventListener('click', function() {
alert('Я навсегда!');
});
// А ЭТОТ МОЖНО:
let myHandler = function() {
alert('Меня можно удалить');
};
button.addEventListener('click', myHandler);
Какие бывают события?
События мыши
- click - клик (нажатие и отпускание кнопки мыши)
- mousedown - нажатие кнопки мыши
- mouseup - отпускание кнопки мыши
- mousemove - движение мыши
- mouseover - курсор наводится на элемент
- mouseout - курсор уходит с элемента
События клавиатуры
- keydown - нажатие клавиши
- keyup - отпускание клавиши
- keypress - нажатие клавиши, которая даёт символ
События форм
- submit - отправка формы
- focus - элемент получает фокус
- blur - элемент теряет фокус
- change - изменение значения поля формы
События документа
- DOMContentLoaded - HTML загружен и преобразован в DOM-дерево
- load - страница полностью загружена
- scroll - прокрутка страницы
Объект события - откуда берётся информация
Когда происходит событие, браузер создаёт специальный объект события и передаёт его в обработчик. Этот объект содержит полезную информацию о событии:
javascriptbutton.addEventListener('click', function(event) {
// event - это объект события
console.log('Координаты клика:', event.clientX, event.clientY);
console.log('Нажатая кнопка мыши:', event.which);
console.log('Элемент, на котором произошёл клик:', event.target);
});
Всплытие событий: как события путешествуют
Представь, что у тебя есть такая структура HTML:
html<div id="outer">
<div id="inner">
<button id="button">Нажми меня</button>
</div>
</div>
Когда ты кликаешь на кнопку, событие click сначала происходит на самой кнопке, затем на её родителе (inner), потом на родителе родителя (outer) и так далее до самого верхнего элемента. Это называется всплытием событий.
Давай посмотрим, как это работает:
javascriptlet outer = document.getElementById('outer');
let inner = document.getElementById('inner');
let button = document.getElementById('button');
outer.addEventListener('click', function() {
console.log('Клик на внешнем div');
});
inner.addEventListener('click', function() {
console.log('Клик на внутреннем div');
});
button.addEventListener('click', function() {
console.log('Клик на кнопке');
});
Если ты кликнешь на кнопку, то увидишь в консоли:
- "Клик на кнопке"
- "Клик на внутреннем div"
- "Клик на внешнем div"
Это и есть всплытие событий!
Остановка всплытия
Иногда нужно остановить всплытие, чтобы событие не пошло дальше. Для этого используется метод stopPropagation():
javascriptbutton.addEventListener('click', function(event) {
console.log('Клик на кнопке');
event.stopPropagation(); // Событие дальше не пойдёт!
});
Отмена действий по умолчанию
Некоторые элементы имеют встроенные действия: ссылки открывают новую страницу, формы отправляются на сервер и т.д. Иногда нам нужно отменить эти действия и сделать что-то своё. Для этого используется метод preventDefault():
javascript// Простая ссылка
let link = document.querySelector('a');
link.addEventListener('click', function(event) {
event.preventDefault(); // Отменяем переход по ссылке
console.log('Клик по ссылке, но мы остаёмся на странице!');
});
// Отмена отправки формы
let form = document.querySelector('form');
form.addEventListener('submit', function(event) {
event.preventDefault(); // Отменяем отправку формы
console.log('Форма не отправлена!');
});
Практические примеры
Пример 1: Простой счётчик кликов
html<button id="counter">Кликни меня</button>
<p>Количество кликов: <span id="count">0</span></p>
<script>
let button = document.getElementById('counter');
let countDisplay = document.getElementById('count');
let clickCount = 0;
button.addEventListener('click', function() {
clickCount = clickCount + 1;
countDisplay.textContent = clickCount;
});
</script>
Пример 2: Изменение цвета элемента при наведении
html<style>
#colorBox {
width: 200px;
height: 200px;
background-color: blue;
transition: background-color 0.3s;
}
</style>
<div id="colorBox"></div>
<script>
let box = document.getElementById('colorBox');
box.addEventListener('mouseover', function() {
box.style.backgroundColor = 'red';
});
box.addEventListener('mouseout', function() {
box.style.backgroundColor = 'blue';
});
</script>
Пример 3: Простая рисовалка
Этот пример позволяет рисовать точки на странице при клике мышкой:
html<style>
body {
height: 400px;
background: #f0f0f0;
}
.dot {
height: 10px;
width: 10px;
border-radius: 5px;
background: red;
position: absolute;
}
</style>
<script>
document.addEventListener('click', function(event) {
// Создаём новую точку
let dot = document.createElement('div');
dot.className = 'dot';
// Позиционируем точку под курсором
// Вычитаем половину размера точки, чтобы центрировать её
dot.style.left = (event.pageX - 5) + 'px';
dot.style.top = (event.pageY - 5) + 'px';
// Добавляем точку на страницу
document.body.appendChild(dot);
});
</script>
Пример 4: След за курсором мыши
Этот пример создаёт эффект "следа" за курсором:
html<style>
.trail {
position: absolute;
height: 10px;
width: 10px;
border-radius: 5px;
background: purple;
opacity: 0.7;
}
body {
height: 400px;
background: #f0f0f0;
}
</style>
<script>
// Создаём массив элементов следа
let dots = [];
const TRAIL_LENGTH = 10;
// Создаём элементы следа заранее
for (let i = 0; i < TRAIL_LENGTH; i++) {
let dot = document.createElement('div');
dot.className = 'trail';
document.body.appendChild(dot);
dots.push(dot);
}
// Отслеживаем положение курсора
let currentIndex = 0;
document.addEventListener('mousemove', function(event) {
// Перемещаем текущую точку под курсор
dots[currentIndex].style.left = (event.pageX - 5) + 'px';
dots[currentIndex].style.top = (event.pageY - 5) + 'px';
// Переходим к следующей точке в массиве
currentIndex = (currentIndex + 1) % TRAIL_LENGTH;
});
</script>
Делегирование событий
Представь, что у тебя есть список из 100 кнопок и ты хочешь обработать клик на каждой. Было бы неэффективно назначать 100 обработчиков кликов. Вместо этого можно использовать один обработчик на родительском элементе, а затем проверять, на каком именно элементе произошёл клик. Это называется делегированием событий:
html<div id="buttons-container">
<button data-action="like">Лайк</button>
<button data-action="share">Поделиться</button>
<button data-action="comment">Комментировать</button>
</div>
<script>
let container = document.getElementById('buttons-container');
container.addEventListener('click', function(event) {
// Проверяем, был ли клик по кнопке
if (event.target.tagName === 'BUTTON') {
// Получаем действие из атрибута data-action
let action = event.target.dataset.action;
// Выполняем нужное действие
switch(action) {
case 'like':
console.log('Поставлен лайк!');
break;
case 'share':
console.log('Пост отправлен друзьям!');
break;
case 'comment':
console.log('Открываем окно комментария!');
break;
}
}
});
</script>
Таймеры: выполнение кода через время
В JavaScript есть две функции для запуска кода через определённое время:
setTimeout - выполнить один раз
javascript// Меняем цвет фона через 2 секунды
document.body.style.background = "blue";
setTimeout(function() {
document.body.style.background = "green";
}, 2000); // 2000 миллисекунд = 2 секунды
Можно отменить запланированное выполнение:
javascriptlet timerId = setTimeout(function() {
alert("Бум!");
}, 1000);
// Если передумали:
clearTimeout(timerId);
setInterval - выполнять регулярно
javascript// Создаём мигающий эффект
let isRed = false;
let blinkerId = setInterval(function() {
if (isRed) {
document.body.style.background = "";
} else {
document.body.style.background = "red";
}
isRed = !isRed;
}, 500); // Каждые полсекунды
// Останавливаем мигание через 5 секунд
setTimeout(function() {
clearInterval(blinkerId);
document.body.style.background = "";
}, 5000);
Ограничение частоты событий (debouncing)
Некоторые события (например, mousemove или scroll) могут происходить ОЧЕНЬ часто. Если обработчик выполняет сложные операции, страница может тормозить. Для решения этой проблемы используется техника "debouncing":
javascript// Пример: показываем координаты мыши, но не чаще чем раз в 250 мс
function showCoords(event) {
document.body.textContent =
"Мышь на координатах: " + event.pageX + ", " + event.pageY;
}
let timeout;
document.addEventListener("mousemove", function(event) {
// Отменяем предыдущий запланированный вызов
clearTimeout(timeout);
// Планируем новый вызов
timeout = setTimeout(function() {
showCoords(event);
}, 250);
});
Заключение
События JavaScript - это как нервная система для веб-страницы. Они связывают действия пользователя с реакциями страницы, делая её живой и интерактивной.
Теперь ты знаешь:
- Как обрабатывать события разными способами
- Какие бывают события и что они означают
- Как события всплывают по DOM-дереву
- Как отменять действия по умолчанию
- Как использовать делегирование событий для оптимизации кода
- Как использовать таймеры для выполнения кода через время
Попробуй сам написать код с обработкой событий - это увлекательно и открывает много возможностей для создания интерактивных веб-приложений!
Практические задания для тренировки
Задание 1: Цензура клавиатуры
Создай текстовое поле, которое не позволяет вводить определённые буквы (например, Q, W и X):
html<input type="text" id="censored-input" placeholder="Попробуй ввести Q, W или X">
<script>
let input = document.getElementById('censored-input');
input.addEventListener('keydown', function(event) {
// Получаем символ нажатой клавиши (переводим в верхний регистр для проверки)
let char = String.fromCharCode(event.keyCode).toUpperCase();
// Проверяем, входит ли символ в список запрещённых
if (char === 'Q' || char === 'W' || char === 'X') {
// Если да, отменяем действие по умолчанию (ввод символа)
event.preventDefault();
console.log('Буква ' + char + ' запрещена!');
}
});
</script>
Задание 2: Простые вкладки
Создай простую систему вкладок:
html<style>
.tab-content {
display: none;
padding: 10px;
border: 1px solid #ccc;
}
.active-tab {
display: block;
}
.tab-button {
padding: 5px 10px;
margin-right: 5px;
background: #f0f0f0;
border: 1px solid #ccc;
cursor: pointer;
}
.active-button {
background: #ccc;
}
</style>
<div id="tabs-container">
<div id="tabs-buttons"></div>
<div class="tab-content" data-tab="tab1">
<h3>Вкладка 1</h3>
<p>Содержимое первой вкладки.</p>
</div>
<div class="tab-content" data-tab="tab2">
<h3>Вкладка 2</h3>
<p>Содержимое второй вкладки.</p>
</div>
<div class="tab-content" data-tab="tab3">
<h3>Вкладка 3</h3>
<p>Содержимое третьей вкладки.</p>
</div>
</div>
<script>
// Находим все вкладки
let tabs = document.querySelectorAll('.tab-content');
let buttonsContainer = document.getElementById('tabs-buttons');
// Создаём кнопки для каждой вкладки
tabs.forEach(function(tab) {
let tabName = tab.dataset.tab;
let button = document.createElement('button');
button.className = 'tab-button';
button.textContent = tabName;
button.dataset.tabTarget = tabName;
buttonsContainer.appendChild(button);
});
// Делаем первую вкладку активной по умолчанию
tabs[0].classList.add('active-tab');
document.querySelector('.tab-button').classList.add('active-button');
// Обработчик кликов по кнопкам
buttonsContainer.addEventListener('click', function(event) {
// Проверяем, что клик был по кнопке
if (event.target.classList.contains('tab-button')) {
// Удаляем активный класс у всех вкладок и кнопок
tabs.forEach(function(tab) {
tab.classList.remove('active-tab');
});
let buttons = document.querySelectorAll('.tab-button');
buttons.forEach(function(button) {
button.classList.remove('active-button');
});
// Добавляем активный класс нужной вкладке и кнопке
let targetTabName = event.target.dataset.tabTarget;
document.querySelector(`.tab-content[data-tab="${targetTabName}"]`).classList.add('active-tab');
event.target.classList.add('active-button');
}
});
</script>
Используй эти примеры как начальную точку и экспериментируй с ними, добавляя новые функции и улучшения. Так ты научишься использовать события JavaScript и сможешь создавать интерактивные веб-страницы!