Чтобы приложение было интерактивным, нам нужно понимать, что пользователь совершил то или иное действие на странице. Браузер распознает действия пользователя и создаёт событие.
События — это сигналы, которые браузер посылает разработчику, а разработчик может на сигнал реагировать. По аналогии со светофором: видим зелёный свет, едем дальше 🚦
События бывают разных типов: клик, нажатие клавиши на клавиатуре, прокрутка страницы и так далее. Происходящие события можно обрабатывать и выполнять код, когда нужное событие происходит. Например, при клике на кнопку показывать всплывающее окно.
Как пишется
Существует два способа обработать события:
- с помощью
on
-свойств DOM-элементов; - методом
addEventListener()
.
on
-свойства DOM-элементов
Большинство событий связаны с DOM-элементами. Если пользователь кликнул на кнопку, то событие click
связано с конкретным DOM-элементом — кнопкой, на которой кликнул пользователь.
Каждый DOM-элемент имеет большой набор свойств, которые начинаются на on
:
onclick
;onscroll
;onkeypress
;onmouseenter
;- и так далее.
Если в это свойство записать анонимную функцию, то эта функция будет вызываться каждый раз, когда браузер будет создавать событие, связанное с этим элементом. Такие функции называют функциями-обработчиками события.
const buttonElement = document.getElementById('change')
const squareDiv = document.getElementById('square')
// чтобы реагировать на нажатие кнопки, записываем функцию в свойство onclick.
// Эта функция будет вызываться при каждом нажатии на кнопку. Часто говорят,
// что эта функция обрабатывает событие
buttonElement.onclick = function() {
squareDiv.style = `background-color: ${getColor()};`
}
function getColor() {
const colors = [
'#49A16C', '#064236',
'#ED6742', '#F498AD',
'#1A5AD7', '#AFC9DA',
'#FFD829', '#282A2E',
'#5E6064', '#E6E6E6'
]
return colors[Math.floor(Math.random() * colors.length)]
}
Чтобы перестать обрабатывать событие, нужно записать в свойство значение null
.
Метод addEventListener()
🤖 Если обрабатывать события с помощью on
-свойств, то получится добавить только одну функцию-обработчик на каждый элемент. Часто одного обработчика недостаточно. Чтобы не создавать ограничение на пустом месте, используют альтернативный метод подписки на события — метод addEventListener()
.
Метод вызывается у DOM-элемента. Аргументами нужно передать тип события (справочная информация) и функцию, которую нужно выполнить:
const buttonElement = document.getElementById('change')
const squareDiv = document.getElementById('square')
// чтобы реагировать на нажатие кнопки, подписываемся на событие click и передаем
// функцию-обработчик. Эта функция будет вызываться при каждом нажатии на кнопку
buttonElement.addEventListener('click', function() {
squareDiv.style = `background-color: ${getColor()};`
})
Как понять
Функция-обработчик
Функция-обработчик, или просто обработчик, — это функция, которая вызывается браузером при наступлении события.
При вызове браузер передаёт в обработчик объект события с помощью аргумента.
Объект события — это JavaScript-объект с информацией о событии. В объекте события есть как общие свойства (тип события, время события), так и свойства, которые зависят от типа события (например, на какую кнопку нажал пользователь).
Чтобы работать с объектом события, нужно добавить параметр в объявление обработчика. Обработаем нажатие на кнопки клавиатуры и получим из объекта события информацию о нажатой кнопке:
window.addEventListener('keydown', function (event) {
// используем объект события, чтобы получить информацию о нажатой клавише
alert(`Вы нажали на кнопку: ${event.key}`)
})
Помимо объекта события, внутри функции можно использовать ключевое слово this
. Оно позволяет получить DOM-элемент, на котором сработал обработчик. Это позволяет создать обработчик один раз, но привязать её к нескольким DOM-элементам.
Например, мы объявим обработчик в виде именованной функции и повесим её на нажатие нескольких кнопок. При клике на кнопку будем менять её цвет:
function changeColor() {
// меняем цвет кнопки, на которой произошло событие. кнопка доступна с помощью
// ключевого слова this
this.style = `background-color: ${getColor()};`;
};
const buttons = document.getElementsByTagName('button');
for (let i = 0; i < buttons.length; ++i) {
const button = buttons[i];
// к каждой кнопке привязываем обработчик
button.addEventListener('click', changeColor); // обратите внимание, что мы не вызываем
// функцию changeColor, а только пишем ее имя
}
Всплытие событий
Подробнее: 0009 Всплытие и погружение событий
Рассмотрим пример. У нас есть <div>
элемент, в который вложено видео. Мы подписались на события click
как на <div>
, такч и на <video>
. Если событие происходит на <div>
, то мы меняем его цвет на случайный из списка. Если событие происходит на <video>
, то мы запускаем видео. Попробуйте кликнуть на коробку:
const container = document.getElementById('container')
const video = document.getElementById('cat')
// обрабатываем событие click на <div>
container.addEventListener('click', function() {
const colors = ['#49A16C', '#064236', '#ED6742', '#F498AD', '#1A5AD7', '#AFC9DA',
'#FFD829', '#282A2E', '#5E6064']
const randomColorIndex = Math.floor(Math.random() * colors.length)
container.style = `background-color: ${colors[randomColorIndex]}`
});
// обрабатываем событие click на видео
video.addEventListener('click', function() {
this.currentTime = 0 // отматываем видео на начало
this.play()
})
🤖 Обратите внимание, что событие срабатывает на обоих элементах — цвет фона меняется и запускается видео. Этому есть объяснение, оно называется всплытие событий (event bubbling).
Когда пользователь совершает действие, браузер ищет самый вложенный элемент, к которому относится событие. Затем это событие передаётся родительскому элементу и так далее до самого корня DOM.
В нашем примере мы кликнули на <video>
, это самый вложенный элемент. Браузер создал событие, и мы обработали его в коде. После этого браузер передаёт событие родителю <video>
(то есть элементу, который содержит <video>
) — элементу <div>
. Мы получаем его и обрабатываем. И он всплывает дальше, пока не дойдёт до <body>
.
Обработчики сначала срабатывают на самом вложенном элементе, затем на его родителе, затем выше и так далее, вверх по цепочке вложенности.
Если кликнуть по блокам на демо, то можно увидеть, как событие всплывает вверх к родителям:
let active
let counter = 0
// обрабатываем событие click на всех <div>
let divs = Array.from(document.querySelectorAll('div')).reverse()
for (let i = 0; i < divs.length; ++i) {
const isLast = (i + 1 === divs.length)
divs[i].addEventListener('click', clickHandlerGenerator(isLast))
}
function clickHandlerGenerator(isLast = false) {
return function() {
let me = this
setTimeout(function() {
if (active) {
active.classList.remove('active')
}
me.classList.add('active')
active = me
if (isLast) {
setTimeout(function() {
active.classList.remove('active')
active = undefined
counter = 0
}, 300)
}
}, counter * 300)
++counter
}
}
Всплытие события можно остановить с помощью метода stopPropagation()
у объекта события:
video.addEventListener('click', function (event) {
event.stopPropagation()
this.currentTime = 0
this.play()
})