Кратко
СкопированоЧтобы приложение было интерактивным, нам нужно понимать, что пользователь совершил то или иное действие на странице. Браузер распознает действия пользователя и создаёт событие.
События — это сигналы, которые браузер посылает разработчику, а разработчик может на сигнал реагировать. По аналогии со светофором: видим зелёный свет, едем дальше 🚦
События бывают разных типов: клик, нажатие клавиши на клавиатуре, прокрутка страницы и так далее.
Происходящие события можно обрабатывать и выполнять код, когда нужное событие происходит. Например, при клике на кнопку показывать всплывающее окно.
Как пишется
СкопированоСуществует два способа обработать события:
- с помощью
on-свойств DOM-элементов; - методом
add.Event Listener ( )
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)]}
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-свойств, то получится добавить только одну функцию-обработчик на каждый элемент. Часто одного обработчика недостаточно. Чтобы не создавать ограничение на пустом месте, используют альтернативный метод подписки на события — метод add.
Метод вызывается у DOM-элемента. Аргументами нужно передать тип события (справочная информация) и функцию, которую нужно выполнить:
const buttonElement = document.getElementById('change')const squareDiv = document.getElementById('square')// Чтобы реагировать на нажатие кнопки, подписываемся// на событие click и передаём функцию-обработчик.// Эта функция будет вызываться при каждом нажатии на кнопкуbuttonElement.addEventListener('click', function() { squareDiv.style = `background-color: ${getColor()};`})
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}`)})
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, а только пишем её имя}
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, а только пишем её имя
}
Распространение событий
СкопированоВажный аспект событийной модели — механизм распространения событий (event propagation). Он определяет, как события взаимодействуют с узлами DOM при достижении целевого элемента.
Возникая, событие проходит через все родительские элементы (capturing phase), достигает целевого элемента (target phase), и затем вновь поднимается по иерархии родительских элементов (bubbling phase).

Обычно события обрабатывают на стадии достижения целевого элемента или всплытия.
Всплытие событий
СкопированоРассмотрим пример. У нас есть <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()})
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 activelet 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 }}
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
}
}
Всплытие события можно остановить с помощью метода stop у объекта события:
video.addEventListener('click', function (event) { event.stopPropagation() this.currentTime = 0 this.play()})
video.addEventListener('click', function (event) {
event.stopPropagation()
this.currentTime = 0
this.play()
})
Захват событий
СкопированоПри всплытии очерёдность обработки события направлена от дочерних элементов к родительским. Это не позволяет элементу получить полный контроль над событиями дочерних элементов. Например, обработчик, привязанный к элементу, может «узнать» о произошедшем событии дочерних элементов только если всплытие не было остановлено в одном из них.
Для решения некоторых задач требуется менять порядок обработки событий. Родительский элемент должен отреагировать на событие ещё до того, как оно получено и обработано дочерним элементом. Например, это нужно при создании элементов-«обёрток», не зависящих от реализации логики дочерних элементов.
В качестве иллюстрации этого подхода, посмотрим на предыдущий пример с котиком в коробке. Допустим, нужно воспроизводить видео только когда фон родительского элемента салатовый или голубой, при этом не меняя логику дочернего элемента. Для этого нам нужно слегка изменить обработчик клика по контейнеру:
// Обрабатываем событие click на <div>container.addEventListener('click', function(event) { 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]}` // Если индекс цвета не соответствует условию (зелёный или голубой), // останавливаем распространение события if (randomColorIndex !== 0 && randomColorIndex !== 2 ) { event.stopPropagation() }}, true) // Событие обработается на стадии захвата
// Обрабатываем событие click на <div>
container.addEventListener('click', function(event) {
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]}`
// Если индекс цвета не соответствует условию (зелёный или голубой),
// останавливаем распространение события
if (randomColorIndex !== 0 && randomColorIndex !== 2 ) {
event.stopPropagation()
}
}, true) // Событие обработается на стадии захвата
Чтобы обработать событие на стадии захвата, добавьте true в вызов add в качестве третьего параметра:
element.addEventListener( 'click', // Событие function (event) { // Код обработки }, // Регистрация обработчика для срабатывания на стадии захвата true)
element.addEventListener(
'click', // Событие
function (event) {
// Код обработки
},
// Регистрация обработчика для срабатывания на стадии захвата
true
)
Теперь событие будет обработано сначала родительским элементом <div>, а затем, если не будет остановлено, станет доступно целевому элементу <video>.
☝️ Обратите внимание, что обработка события происходит на стадии захвата. Из-за этого метод stop останавливает распространение события от родительского элемента к дочерним, и целевой элемент не получает его.
А вот демонстрация распространения события на стадии захвата. Если кликнуть по блокам из демо, увидите, как событие достигает целевого элемента:
На практике
Скопированосоветует
Скопировано🛠 Всегда подписывайтесь на события с помощью add. Так вы избежите доработок, когда потребуется повесить несколько обработчиков на одно и то же событие.
🛠 Если нужно обработать все события определённого типа, вызовите метод add у объекта window:
// Обрабатываем все клики на страницеwindow.addEventListener('click', function () { alert('clicked')})
// Обрабатываем все клики на странице
window.addEventListener('click', function () {
alert('clicked')
})
🛠 Если вы не используете объект события в обработчике, то можно не указывать его в списке параметров обработчика. Вместо function пишите function.
🛠 Всплытие событий — важный концепт, поэкспериментируйте с ним, чтобы лучше в нём разобраться.
На собеседовании
СкопированоЭто вопрос без ответа. Вы можете помочь! Чтобы написать ответ, следуйте инструкции.
отвечает
СкопированоМетоды event и event предназначены для решения похожих задач и часто используются вместе.
Метод event останавливает распространение события (event propagation). Распространение события включает стадию захвата и стадию всплытия. Например, рассмотрим обработку события на стадии захвата — при распространении события от родительских элементов к дочерним. Обработчик, объявленный в дочернем элементе, не будет вызван после выполнения stop в обработчике родительского элемента. При этом действие по умолчанию не отменится.
Метод event отменяет действие по умолчанию. Например, переход по ссылке при клике на элемент <a>. Само событие продолжает распространяться после выполнения prevent.
Рассмотрим пример. Есть простая форма:
<form> <label for="id-checkbox">Это чекбокс:</label> <input type="checkbox" id="id-checkbox"></form>
<form>
<label for="id-checkbox">Это чекбокс:</label>
<input type="checkbox" id="id-checkbox">
</form>
Добавим обработку клика по форме и чекбоксу на стадии всплытия:
const form = document.querySelector('form')const label = form.querySelector('label')const checkbox = form.querySelector('#id-checkbox')form.addEventListener('click', formClick)checkbox.addEventListener('click', checkboxClick)function formClick(event) { form.style.border = '1px solid black'}function checkboxClick(event) { checkbox.style.color = 'blue'}
const form = document.querySelector('form')
const label = form.querySelector('label')
const checkbox = form.querySelector('#id-checkbox')
form.addEventListener('click', formClick)
checkbox.addEventListener('click', checkboxClick)
function formClick(event) {
form.style.border = '1px solid black'
}
function checkboxClick(event) {
checkbox.style.color = 'blue'
}
При клике по чекбоксу изменится цвет подписи, а также появится рамка вокруг формы.
В данном случае действием по умолчанию будет переключение чекбокса. Чтобы отменить это поведение, добавим prevent в обработчик checkbox:
function checkboxClick(event) { checkbox.style.color = 'blue' event.preventDefault()}
function checkboxClick(event) {
checkbox.style.color = 'blue'
event.preventDefault()
}
Теперь, при клике, чекбокс не меняет значение, однако остальные действия по-прежнему происходят.
Чтобы остановить всплытие события, но позволить чекбоксу переключаться, заменим event на stop:
function checkboxClick(event) { checkbox.style.color = 'blue' event.stopPropagation()}
function checkboxClick(event) {
checkbox.style.color = 'blue'
event.stopPropagation()
}
Методы prevent и stop также можно использовать при обработке событий на стадии захвата.
Например, мы можем отменить действие по умолчанию, используя обработчик form. Для этого объявим его для срабатывания на стадии захвата:
// formClick будет работать на стадии захвата событияform.addEventListener('click', formClick, true)checkbox.addEventListener('click', checkboxClick)function formClick(event) { form.style.border = '1px solid black' event.preventDefault()}function checkboxClick(event) { checkbox.style.color = 'blue'}
// formClick будет работать на стадии захвата события
form.addEventListener('click', formClick, true)
checkbox.addEventListener('click', checkboxClick)
function formClick(event) {
form.style.border = '1px solid black'
event.preventDefault()
}
function checkboxClick(event) {
checkbox.style.color = 'blue'
}
☝️ Обратите внимание, что checkbox выполняется, но сам чекбокс не переключается. Это действие уже отменено в функции form, которая теперь срабатывает раньше, чем событие достигнет чекбокса.
Если заменить prevent на stop, обработчик checkbox не выполнится, но действие по умолчанию произойдёт:
function formClick(event) { form.style.border = '1px solid black' event.stopPropagation()}
function formClick(event) {
form.style.border = '1px solid black'
event.stopPropagation()
}