Интерактивные системы, такие как сайты в браузере или даже терминал, устроены по одному и тому же принципу. После загрузки они находятся в режиме "ожидания" действий от пользователя. К таким действиям относятся клики, набор текста, перемещение мышки, горячие клавиши и многое другое.
С точки зрения кода, все действия представлены событиями. Вот некоторые из них:
Детализация событий достаточно высокая, набор текста раскладывается на два события: кнопка зажата и кнопка отпущена. Кроме этого, есть специальное событие keypress, которое позволяет отличать горячие клавиши от нормального ввода.
Любое событие связано с конкретным элементом в DOM. Чтобы добавить новое поведение, нужно найти элемент и добавить колбек в метод addEventListener()
, который будет вызван тогда, когда произойдет событие:
<button id="myButton"></button>
const button = document.getElementById('myButton');
// Добавляем обработчик, который вызовется при клике на элементе button
button.addEventListener('click', () => alert('Boom 1!'));
// Добавляем второй обработчик
button.addEventListener('click', () => alert('Boom 2!'));
Каждый обработчик события представляет собой функцию, которая будет вызвана в момент наступления события. Обработчики вызываются один за другим, в том же порядке, в котором они были определены.
При необходимости, можно удалить обработчик, хотя, на практике, это делается редко:
const button = document.getElementById('myButton');
const handler = () => alert('Boom 1!');
button.addEventListener('click', handler);
// Важно что сюда передается ровно та же самая функция (по ссылке)
button.removeEventListener('click', handler);
События можно вызывать программно. Например это актуально для фокуса. Представьте, что вы открываете чат в надежде написать туда сообщение, но текст не набирается. Для набора текста нужна фокусировка на поле для ввода. По умолчанию этого не происходит и именно в этом месте мы должны поучаствовать:
const input = document.getElementById('textInput');
// Курсор появляется в поле для ввода
// Попробуйте поменять канал в Slack,
// вы увидите что поле для ввода фокусируется автоматически
input.focus();
Для хорошего понимания темы событий, с ними нужно постоянно практиковаться в браузере. Проще всего сделать это следующим образом:
const element = document.body;
element.addEventListener('click', () => console.log('wow!'));
// Теперь можно кликать по любой части сайта
wow!
// Если нужен элемент формы
// Выберет первый встреченный input
const element2 = document.querySelector('input');
// тут вешаем обработчик и выводим на экран все что нужно
element2.addEventListener('keyup', () => console.log('pressed!'));
Таким способом можно тестировать любые события на любых сайтах.
С каждым возникающим событием связана информация, зависящая от типа события. Например, событие click это не только факт сам по себе, но также и координаты точки на экране, где был совершен клик. Эта информация доступна через специальный объект-событие, передающийся в обработчик события. Такой объект всегда передается в любой обработчик как единственный параметр.
<div id="myElement">Бум!</div>
const button = document.getElementById('myElement');
button.addEventListener('click', (event) => {
// Обычный объект
console.log(event);
// Координаты точки, в которой произошел клик
console.log(event.clientX);
console.log(event.clientY);
});
Каждый клик по кнопке, будет приводить к созданию нового объекта event
со своими значениями, соответствующими текущему событию. event
- обычный объект, наполненный большим количеством свойств, которые проще всего изучать прямо в браузере. У разных событий есть как общие свойства, так и специфичные, например, у клика есть координаты, а у нажатии клавиши ее значение. Подробнее о свойствах смотрите в документации.
Общие свойства:
event.target
- DOM-элемент, на котором произошло событие. Через него проще всего добраться до данных, которые могут понадобится после события.event.type
- имя события, например click, keyup и так далее.Для примера посмотрим на задачу валидации вводимого пароля. Будем подсвечивать поле для ввода красной рамкой если пароль слишком короткий.
See the Pen js_dom_events by Hexlet (@hexlet) on CodePen.
Для некоторых элементов браузер выполняет действие по умолчанию при срабатывании определенных событий. Например, если повесить обработчик на клик по ссылке, то, выполнив этот клик, мы внезапно перейдем на другую страницу, ту, которая была указана в атрибуте href
. Здесь мы видим пример того самого действия по умолчанию, на которое никак не влияет наличие обработчиков. Чтобы отменить это действие, а такое бывает нужно часто, необходимо вызвать метод event.preventDefault()
внутри обработчика.
<a href="#" id="myElement">Бум!</a>
const button = document.getElementById('myElement');
button.addEventListener('click', (e) => {
// Если этого не сделать, то браузер выполнит загрузку новой страницы
e.preventDefault();
alert(e.target.textContent);
});
Действиями по умолчанию обладают следующие элементы:
textarea
передвигает текст, если он не помещаетсяВ процессе выполнения обработчиков могут возникать новые события как от действий пользователя, так и программно, в самих обработчиках, а некоторые события всегда возникают целым блоком, например mouseup и click. Но это не означает, что выполнение кода сразу переключается на обработку этих событий. Вместо этого события складываются в очередь и выполняются последовательно.
Но некоторые события все же берутся в обработку сразу. Это касается тех событий, которые генерируются программно, например focus.
Возникает закономерный вопрос, что происходит со страницей во время выполнения обработчика? И здесь возможны варианты. Если обработчик выполняет некоторый код синхронно, например, занимается вычислениями, то в этот момент блокируется всё остальное и страница замирает (говорят "фризится"). Если такое поведение длится слишком долго, то некоторые браузеры зависают, а другие предлагают закрыть вкладку. Отсюда вывод, обработчики должны выполнять свою задачу максимально быстро. А что если задача асинхронная, например, выполняется запрос к серверу? В таком случае все продолжает прекрасно работать, так как HTTP-запросы не блокируют выполнение кода.
Вообще говоря, из этого урока должно стать понятно, почему JavaScript именно такой, какой есть. Событийная система возможна только в асинхронном коде. По сути, при загрузке страницы происходит инициализация и установка обработчиков, а дальше, как правило, не выполняется никакой код, вся страница находится в ожидании действий от пользователя.
Довольно часто новички путаются в функциях и вместо самой функции передают в обработчик результат вызова функции:
const button = document.getElementById('myElement');
const handler = () => {
alert('Click!');
};
button.addEventListener('click', handler());
Функция handler()
выполняется в момент навешивания обработчика на событие, вместо самой функции будет передан результат вызова handler()
.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Статья «Ловушки обучения»
Вебинар «Как самостоятельно учиться»
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт