Кратко
СкопированоДобавляет элементу действие, которое будет выполнено после срабатывания события. Например, на клик мышки или нажатие клавиши.
Пример
СкопированоНайдём первую кнопку на странице и будем выводить сообщение в консоль, когда произошёл клик по этой кнопке.
const element = document.querySelector('button')element.addEventListener('click', function (event) { console.log('Произошло событие', event.type)})
const element = document.querySelector('button')
element.addEventListener('click', function (event) {
console.log('Произошло событие', event.type)
})
Как пишется
СкопированоСигнатура функции выглядит следующим образом:
element.addEventListener(eventType, handler, options)
element.addEventListener(eventType, handler, options)
element— любой HTMLElement на странице;event— строка, содержащая название события. Наиболее популярные событияType 'click','change','submit','keydown','keyup','mousemove','mouseenter','mouseleave';handler— функция, которая будет вызвана, когда событие произойдёт;options— необязательный параметр, который определяет дополнительные характеристики обработки события;/ capture capture— включает или выключает обработку события в фазе погружения. Принимает значениеtrueилиfalse, по умолчаниюfalse;options— при передаче объекта аргумент будет распознан как объект настроек, так можно установить больше параметров;: { capture : bool , passive : bool , once : bool , signal : Abort Signal } passive— значениеtrueозначает что внутриhandlerникогда не будет вызвана функцияevent, если функция. prevent Default ( ) eventвсё-таки вызвана, браузер должен её игнорировать и выводить предупредительное сообщение в консоль;. prevent Default ( ) once— включает автоматическую отписку от события после первого срабатывания;signal— передаётся ссылка на объект сигналаAbort, который позволяет отписаться от события.Signal
Ниже приведено несколько вариантов вызова функции с разными параметрами:
function handleMouseClick(event) { console.log('Вы нажали на элемент:', event.target)}window.addEventListener('click', handleMouseClick)window.addEventListener('click', handleMouseClick, true)window.addEventListener('click', handleMouseClick, false)window.addEventListener('click', handleMouseClick, { passive: true, capture: false})const abortController = new AbortController()window.addEventListener('click', handleMouseClick, { signal: abortController.signal})
function handleMouseClick(event) {
console.log('Вы нажали на элемент:', event.target)
}
window.addEventListener('click', handleMouseClick)
window.addEventListener('click', handleMouseClick, true)
window.addEventListener('click', handleMouseClick, false)
window.addEventListener('click', handleMouseClick, {
passive: true,
capture: false
})
const abortController = new AbortController()
window.addEventListener('click', handleMouseClick, {
signal: abortController.signal
})
У объекта event есть специальные методы, такие как prevent и stop. Остальные методы практически не используются:
preventпозволяет заблокировать стандартное поведение браузера. Например, по клику на ссылке — заблокировать переход по этой ссылке.Default ( ) stopпозволяет остановить распространение события по DOM-дереву.Propagation ( )
Как понять
СкопированоПри вызове функции, в неё передаётся специальный объект (в примере выше — event), который у разных типов событий разный. Например, у событий нажатия клавиши есть код клавиши, а у событий перемещения мыши — координаты.
Функция может быть объявлена ранее:
const element = document.querySelector('button')function handleClickFunction(event) { alert('Именованная функция')}element.addEventListener('click', handleClickFunction)
const element = document.querySelector('button')
function handleClickFunction(event) {
alert('Именованная функция')
}
element.addEventListener('click', handleClickFunction)
А может быть анонимной:
element.addEventListener('click', function (event) { alert('Анонимная функция')})
element.addEventListener('click', function (event) {
alert('Анонимная функция')
})
🤖 Заранее созданные функции обычно используют, когда функция содержит в себе много кода или к ней нужно ссылаться несколько раз. Например, когда нужно отписаться от события позже. Для отписки используется метод элемента Element.
Альтернативный способ отписки от события можно реализовать с помощью объекта Abort. Подробнее о нём читайте в разделе «На практике».
Анонимные функции удобно использовать при быстрой разработке или когда обработчик создаётся в одном единственном месте и выносить его в отдельную именованную функцию — дольше, чем писать код самой этой функции. В этом случае очень часто используют короткую, стрелочную запись функции:
element.addEventListener('click', (event) => { alert('Анонимная функция')})
element.addEventListener('click', (event) => {
alert('Анонимная функция')
})
Обработка события в фазе погружения
СкопированоФаза погружения — это первый этап жизни события, когда оно движется от корня документа (window → document → html → ...) к целевому элементу.
Добавить обработчик, который будет отрабатывать на этой фазе, просто:
element.addEventListener('click', handler, true);// илиelement.addEventListener('click', handler, { capture: true });
element.addEventListener('click', handler, true);
// или
element.addEventListener('click', handler, { capture: true });
Третий аргумент add может быть либо булевым значением, либо объектом. Булевое значение true автоматически интерпретируется как { capture, а его отсутствие или false — как { capture.
Это сделано для обратной совместимости, т.к. изначально существовал только булевый параметр use. Когда спецификация эволюционировала и появилась необходимость добавлять другие опции (например, once, passive), разработчики ввели объектный синтаксис. Чтобы не сломать миллионы существующих сайтов, старый синтаксис оставили, но внутри браузера он приводится к новому.
Идентификация обработчиков событий
СкопированоЕсли добавить один и тот же обработчик несколько раз с одинаковыми параметрами capture — новые копии не добавятся. Будет обработан только первый добавленный обработчик, остальные удалять не нужно.
Уникальность обработчика определяется тремя параметрами:
- тип события (например,
'click'или'mousedown'); - ссылка на функцию-обработчик;
- флаг фазы (
captureили: true capture).: false
В этом примере добавится только один обработчик события:
function handlerClick () { console.log('click')}// неявно { capture: true }document.addEventListener('click', handlerClick, true)// второй обработчик для той же фазы не добавитсяdocument.addEventListener('click', handlerClick, { once: true, capture: true })
function handlerClick () {
console.log('click')
}
// неявно { capture: true }
document.addEventListener('click', handlerClick, true)
// второй обработчик для той же фазы не добавится
document.addEventListener('click', handlerClick, { once: true, capture: true })
В этом примере добавится сразу два обработчика событий (один будет срабатывать на фазе погружения, другой на фазе всплытия):
function handlerClick () { console.log('click')}document.addEventListener('click', handlerClick, true)document.addEventListener('click', handlerClick, { passive: true, capture: false })
function handlerClick () {
console.log('click')
}
document.addEventListener('click', handlerClick, true)
document.addEventListener('click', handlerClick, { passive: true, capture: false })
Улучшение производительности скролла
СкопированоВ JavaScript существуют отменяемые события (cancelable event) - это события, в которых с помощью prevent можно отменить действие по умолчанию (прокрутку, переход по ссылке, отправку формы и т. п.). В таких событиях выполнение действия по-умолчанию откладывается до завершения работы обработчиков. Браузер ждёт выполнения всех обработчиков события, чтобы убедиться, что ни один из них не вызывает prevent.
Например, при обработке событий (touchmove, touchstart, wheel и mousewheel), может возникать заметная задержка прокрутки, особенно на мобильных устройствах.
Флаг { passive решает эту проблему. Вы говорите браузеру: «Обещаю, я не буду вызывать prevent внутри этого обработчика». Получив такое обещание, браузер больше не ждёт и начинает прокручивать немедленно, параллельно выполняя ваш код. Такой обработчик событий также называется «пассивный слушатель».
element.addEventListener('wheel', handleScroll, { passive: true });
element.addEventListener('wheel', handleScroll, { passive: true });
Чтобы не было задержки, все обработчики таких событий на элементе и его родителях должны быть пассивными слушателями.
💡 Если вы укажете { passive, но попытаетесь вызвать prevent, браузер:
- проигнорирует вызов (в консоли может быть предупреждение);
- скролл всё равно произойдёт.
💡 Также для события scroll флаг { passive устанавливать не нужно, т.к. scroll — это уже результат прокрутки, он не может блокировать прокрутку страницы, в отличие от touchmove, touchstart, wheel и mousewheel.
💡 Начиная с Chrome 56 (2017 год), браузеры включили это поведение по умолчанию для touchstart, touchmove и wheel событий на глобальных объектах (window, document, body)
То есть, если вы пишете:
window.addEventListener('touchstart', handler);
window.addEventListener('touchstart', handler);
Современный Chrome обработает его так, как если бы вы явно указали { passive.
На практике
Скопированосоветует
Скопировано🛠 Обработка передачи третьего параметра для устаревших браузеров.
Проверим, поддерживает ли браузер объект options. Добавим обработчик события на window, передав ему объект options. В нём поле passive будет менять ранее установленную переменную на true при попытке доступа к объекту. Если браузер проверит содержимое options, он поддерживает данные настройки.
let hasPassiveSupport = falsetry { const options = Object.defineProperty({}, 'passive', { get() { hasPassiveSupport = true }, }) window.addEventListener('test', null, options)} catch (err) {}
let hasPassiveSupport = false
try {
const options = Object.defineProperty({}, 'passive', {
get() {
hasPassiveSupport = true
},
})
window.addEventListener('test', null, options)
} catch (err) {}
Далее можем просто проверить, поддерживается ли passive. Если поддерживается, то передаём options, иначе — false.
window.addEventListener( 'resize', function () { // Обработка события }, hasPassiveSupport ? { passive: true } : false)
window.addEventListener(
'resize',
function () {
// Обработка события
},
hasPassiveSupport ? { passive: true } : false
)
В случае, если используете passive и capture одновременно, такую проверку можно не делать. Старый браузер воспримет переданный объект как true и включит capture, а новый обработает оба параметра внутри объекта.
window.addEventListener('resize', function () { // Обработка события}, { passive: true, capture: true })
window.addEventListener('resize', function () {
// Обработка события
}, { passive: true, capture: true })
советует
Скопировано🛠 Базовая обработка событий клавиатуры.
С помощью событий, можно обрабатывать нажатие клавиш на клавиатуре, когда фокус установлен в поле ввода.
В момент нажатия клавиш будем выводить код клавиши, а по нажатию клавиши Enter добавлять сообщение, которое было введено в поле.
<div class="event">Ожидание ввода...</div><input type="text" placeholder="Введите сообщение"><div class="result"></div>
<div class="event">Ожидание ввода...</div>
<input type="text" placeholder="Введите сообщение">
<div class="result"></div>
Для этого подпишемся на событие keydown. Каждое нажатие клавиши будет создавать событие 'keydown' и функция будет срабатывать. Внутри функции будем получать код клавиши из свойства key объекта события. Если код клавиши оказался ', то будем сбрасывать значение в поле ввода и выводить результат.
const element = document.querySelector('input')element.addEventListener('keydown', function (event) { const message = '<code>' + event.key + '</code>' const value = event.target.value if (event.key === 'Enter' && value.length > 0) { const messageElement = document.createElement('div') messageElement.classList.add('message') messageElement.innerText = value document.querySelector('.result').appendChild(messageElement) event.target.value = '' } document.querySelector('.event').innerHTML = message})
const element = document.querySelector('input')
element.addEventListener('keydown', function (event) {
const message = '<code>' + event.key + '</code>'
const value = event.target.value
if (event.key === 'Enter' && value.length > 0) {
const messageElement = document.createElement('div')
messageElement.classList.add('message')
messageElement.innerText = value
document.querySelector('.result').appendChild(messageElement)
event.target.value = ''
}
document.querySelector('.event').innerHTML = message
})
🛠 Предотвращение срабатывания события по умолчанию.
В этом примере мы заменим стандартное поведение в случае, когда пользователь кликает на ссылку. Чтобы стандартное поведение не сработало, нужно вызывать метод prevent у события.
<a href="https://yandex.ru" target="_blank"> Ссылка на Яндекс</a><a href="https://yandex.ru" target="_blank" id="custom"> Ссылка с изменённым поведением</a><div id="result"></div>
<a href="https://yandex.ru" target="_blank">
Ссылка на Яндекс
</a>
<a href="https://yandex.ru" target="_blank" id="custom">
Ссылка с изменённым поведением
</a>
<div id="result"></div>
Подпишемся на событие клика по ссылке и вызовем метод prevent. После этого определим собственное поведение элемента. Например, будем выводить сообщение на экран:
const linkElement = document.querySelector('#custom')const resultElement = document.querySelector('#result')linkElement.addEventListener('click', function (event) { event.preventDefault() resultElement.innerText = 'Вы нажали на ссылку, но ничего не произошло!' setTimeout(function () { resultElement.innerText = '' }, 2500)})
const linkElement = document.querySelector('#custom')
const resultElement = document.querySelector('#result')
linkElement.addEventListener('click', function (event) {
event.preventDefault()
resultElement.innerText = 'Вы нажали на ссылку, но ничего не произошло!'
setTimeout(function () {
resultElement.innerText = ''
}, 2500)
})
советует
Скопировано🛠 Базовый пример использования Abort для отписки от слушателя событий.
По умолчанию в большинстве случаев для отписки стоит использовать remove. Но, если при подписке была использована анонимная функция, то отписаться от такого слушателя через remove не получится, так как вторым параметром необходимо передать ссылку на функцию-обработчик.
const abortController = new AbortController()const element = document.querySelector('#element1')element.addEventListener('click', () => console.log('Подписка активна'), { signal: abortController.signal})// Вызываем, когда захотим отписаться:abortController.abort()
const abortController = new AbortController()
const element = document.querySelector('#element1')
element.addEventListener('click', () => console.log('Подписка активна'), {
signal: abortController.signal
})
// Вызываем, когда захотим отписаться:
abortController.abort()
🛠 Отписка сразу от нескольких обработчиков.
Abort может быть удобнее, чем remove в случае, если нам нужно отписаться сразу от нескольких обработчиков:
const abortController = new AbortController()const element1 = document.querySelector('#element1')const element2 = document.querySelector('#element2')element1.addEventListener('click', () => { // ...}, {signal: abortController.signal})element2.addEventListener('click', () => { // ...}, {signal: abortController.signal})// Отписываемся одним вызовом сразу от двух обработчиковabortController.abort()
const abortController = new AbortController()
const element1 = document.querySelector('#element1')
const element2 = document.querySelector('#element2')
element1.addEventListener('click', () => {
// ...
}, {signal: abortController.signal})
element2.addEventListener('click', () => {
// ...
}, {signal: abortController.signal})
// Отписываемся одним вызовом сразу от двух обработчиков
abortController.abort()
🛠 Вешаем слушатель событий на Abort.
В случае, если необходимо реализовать логику после отписки, то можно слушать событие abort на Abort:
const abortController = new AbortController()const signal = abortController.signalsignal.addEventListener('abort', () => { console.log('Операция отменена')})
const abortController = new AbortController()
const signal = abortController.signal
signal.addEventListener('abort', () => {
console.log('Операция отменена')
})