JavaScript метод EventTarget.addEventListener()

Определение и применение

JavaScript метод addEventListener() объекта EventTarget позволяет зарегистрировать обработчик событий определенного типа для конкретной цели.

В качестве цели могут выступать как такие объекты как Element, Document, Window, или любые другие объекты, которые поддерживают события, например, такой объект как XMLHttpRequest, который широко используется в асинхронных запросах AJAX (от англ. Asynchronous Javascript and XML — "асинхронный JavaScript и XML"), что позволяет конкретной странице обновлять только её часть, не нарушая при этом работу пользователя.

Опция signal принимает AbortSignal: вызов abort() соответствующего AbortController удаляет обработчик. Уже отменённый сигнал предотвращает регистрацию. Повторная регистрация того же обработчика с одинаковыми type и capture не создаёт дубликат. listener может быть функцией или объектом с методом handleEvent(); null игнорируется.

Посмотрите пример удаления обработчика через сигнал.

Поддержка браузерами

Спецификация: dom.spec.whatwg

Поддерживается во всех основных браузерах с 2015 года.

Отдельные значения (1) поддерживаются не везде — смотрите таблицу.

Показать браузеры
Совместимостьapi.EventTarget.addEventListener
ВозможностьКомпьютерыМобильные
ChromeEdgeFirefoxOperaSafariChrome AndroidFirefox for AndroidOpera AndroidSafari on iOSSamsung BrowserWebView AndroidWebView on iOS
addEventListener✓1✓12✓1✓7✓1✓18✓4✓10.1✓1✓1.0✓1✓1
Form with options object supported (third parameter can be either options or a Boolean, for backwards compatibility)✓49✓16✓49✓36✓10✓49✓49✓36✓10✓5.0✓49✓10
options.capture parameter✓49✓16✓49✓36✓10✓49✓49✓36✓10✓5.0✓49✓10
options.once parameter✓55✓16✓50✓42✓10✓55✓50✓42✓10✓6.0✓55✓10
options.passive parameter✓51✓16✓49✓38✓10✓51✓49✓41✓10✓5.0✓51✓10
options.passive parameter defaults to true for touchstart and touchmove events✓55✓79✓61✓42✓11.1✓55✓61✓42✓11.3✓6.0✓55✓11.3
options.passive parameter defaults to true for wheel and mousewheel events✓73✓79✓84✓60×✓73✓84✓52×✓11.0✓73×
options.signal parameter✓90✓90✓86✓76✓15✓90✓86✓64✓15✓15.0✓90✓15
useCapture parameter is optional✓1✓12✓6✓11.6✓1✓18✓6✓12✓1✓1.0✓4.4✓1

поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31

JavaScript синтаксис

JavaScript
target.addEventListener(type, listener);
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);

Спецификация

DOM Standard.

Значения параметров

Параметры EventTarget.addEventListener()
Параметр Описание
type Строковое значение, представляющее тип события для прослушивания (значение чувствительно к регистру). Обязательный параметр.
listener Объект, который получает уведомление при возникновении события указанного типа. Это должен быть объект, реализующий интерфейс EventListener, или функция JavaScript. Обязательный параметр.
options Объект, указывающий характеристики прослушивателя событий (является необязательным параметром).
Доступные опции:
  • capture — true для перехватывающего обработчика; по умолчанию false. На самой цели оба варианта выполняются при eventPhase === 2.
  • once - Логическое значение, которое указывает на то, что обработчик события должен быть вызван не более одного раза после добавления. Если указано логическое значение true, то прослушиватель будет автоматически удален при вызове.
  • passive - Логическое значение, которое, если соответствует значению true, указывает на то, что функция, указанная прослушивателем, никогда не отменит действие события по умолчанию (вызовет метод preventDefault()). Если обработчик события всё же вызовет метод preventDefault(), то браузер пользователя проигнорирует его, и создаст при этом предупреждение в консоли.
  • signal - объект AbortSignal. При вызове метода abort() связанного AbortController обработчик удаляется. Если сигнал уже отменён, обработчик не регистрируется.
useCapture Логический вариант третьего аргумента, эквивалентный { capture: useCapture }. Значение по умолчанию — false. У предков true означает перехват, false — всплытие; на цели оба варианта выполняются в фазе AT_TARGET.

На самой цели события оба вида обработчиков — с capture: true и capture: false — выполняются при eventPhase === Event.AT_TARGET (2), сначала перехватывающие. Различие фаз перехвата и всплытия относится к предкам цели.

Пример использования

Базовое использование метода

Обработчик на списке и на его пунктах

Нажмите ▶ и кликните по пункту списка: обработчик на ul сделает его зелёным. Двойной клик по пункту вернёт чёрный цвет через обработчик самого пункта.

index.html
HTML
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>
main.js
JavaScript
const ul = document.querySelector('ul'),
    listElements = document.querySelectorAll('li')

const myFunc = function (event) {
    if (event.target.tagName === 'LI') {
        event.target.style.color = 'green'
    }
}

ul.addEventListener('click', myFunc)

listElements.forEach(function (elem) {
    elem.addEventListener('dblclick', function () {
        this.style.color = '#000'
    })
})

В этом примере мы разместили маркированный список (<ul>), внутри которого мы разместили пять элементов списка (<li>). С помощью метода querySelector() мы выбрали первый элемент <ul> в документе, а с помощью метода querySelectorAll() все элементы <li> в документе, и инициализировали переменные этими значениями.

С помощью метода addEventListener() мы установили для элемента <ul> обработчик события "click" (нажатие левой кнопкой мыши на элементе), который с помощью свойства target объекта Event проверяет имя тега элемента, и если это значение соответствует значению "LI", то изменяет стиль цвета текста на зеленый при срабатывании. Обратите внимание, что зачастую удобнее установить один обработчик на родительский элемент, а не для каждого элемента по отдельности, особенно это актуально при динамическом изменении количества элементов, в этом случае отсутствует необходимость обновлять обработчики для элементов.

Далее с помощью метода forEach() объекта Array мы проходим по каждому элементу коллекции, и с помощью метода addEventListener() устанавливаем каждому элементу <li> обработчик события "dblclick" (двойное нажатие левой кнопки мыши), который изменяет стиль цвета текста элемента на черный при срабатывании.

Обращаю Ваше внимание на то, что допускается устанавливать элементу несколько обработчиков как одного, так и разных типов событий, например:

Несколько обработчиков разных событий

Нажмите ▶, затем нажмите и отпустите кнопку мыши на результате: в консоли появятся event 1–3 при нажатии и event 4–5 при отпускании.

index.html
HTML
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
JavaScript
document.body.addEventListener('mousedown', () => console.log('event 1'))
document.body.addEventListener('mousedown', () => console.log('event 2'))
document.body.addEventListener('mousedown', () => console.log('event 3'))
document.body.addEventListener('mouseup', () => console.log('event 4'))
document.body.addEventListener('mouseup', () => console.log('event 5'))

// при нажатии кнопки мыши на элементе body произойдет вызов трёх обработчиков

// event 1
// event 2
// event 3

// при отпускании кнопки мыши на элементе body произойдет вызов еще двух обработчиков
// event 4
// event 5
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

В этом примере с помощью метода addEventListener() для элемента <body> мы установили пять обработчиков событий - три обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе, и еще два сработают в тот момент, когда кнопка будет отпущена (курсор должен находиться при этом на элементе <body>). Обратите внимание, что в качестве обработчиков событий были использованы анонимные стрелочные функции.

В следующем примере мы рассмотрим, что произойдет, если для одного типа событий мы будем использовать именованные функции, а не анонимные:

Повторная регистрация одной функции

Нажмите ▶ и нажмите кнопку мыши на результате: функция зарегистрирована трижды, но в консоли появится только одно сообщение my event.

index.html
HTML
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
JavaScript
const myFunc = () => console.log('my event')

document.body.addEventListener('mousedown', myFunc)
document.body.addEventListener('mousedown', myFunc)
document.body.addEventListener('mousedown', myFunc)

// при нажатии кнопки мыши на элементе body произойдет вызов только одного обработчика

// my event
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

В этом примере с помощью метода addEventListener() для элемента <body> мы установили три обработчика событий "mousedown". Обратите внимание на важный момент, если в одном EventTarget (в нашем случае элемент <body>) с одинаковыми параметрами зарегистрировано несколько одинаковых прослушивателей (обработчиков) событий, повторяющиеся экземпляры в этом случае будут отброшены. Они не вызывают повторный вызов обработчиков событий, и их не нужно удалять вручную с помощью метода removeEventListener().

Использование параметра useCapture

Ранее некоторые браузеры требовали передачи методу addEventListener() третьего аргумента, поэтому при регистрации обычного, неперехватывающего обработчика, который срабатывает в фазе всплытия (bubbling phase) в третьем аргументе следует передавать значение false для поддержки старых версий браузеров.

При capture: true обработчик вызывается во время перехвата у предков и на самой цели. При capture: false (значение по умолчанию) — на цели и при всплытии у предков.

Давайте рассмотрим следующий пример:

Порядок перехвата и всплытия

Нажмите ▶ и нажмите кнопку мыши на результате: сначала выполнятся перехватывающие обработчики event 2 и event 4, затем event 1 и event 3. Поменяйте true и false и сравните порядок.

index.html
HTML
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
JavaScript
document.body.addEventListener('mousedown', () => console.log('event 1'), false)
document.body.addEventListener('mousedown', () => console.log('event 2'), true)
document.body.addEventListener('mousedown', () => console.log('event 3'), false)
document.body.addEventListener('mousedown', () => console.log('event 4'), true)

// при нажатии кнопки мыши на элементе body произойдет вызов четырех обработчиков

// event 2
// event 4
// event 1
// event 3
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

В примере для body зарегистрированы четыре обработчика mousedown. При событии на самом body сначала вызываются два перехватывающих, затем два обычных; у всех eventPhase равен 2. Если событие начинается у потомка, обработчики body относятся соответственно к перехвату и всплытию.

На изображении ниже представлено как будет распространяться событие до элемента <body> с указанием всех фаз (стадий) - фаза перехвата, фаза цели и фаза всплытия:

Схематическое отображение события, отправленного в дереве DOM элементу body
Схематическое отображение события, отправленного в дереве DOM элементу body

Использование параметра options

Ключ capture объекта options действует так же, как логический третий аргумент: true выбирает перехватывающий обработчик, false — обычный. На самой цели оба работают в фазе AT_TARGET.

Давайте рассмотрим следующий пример:

Настройка фазы через объект options

Нажмите ▶ и нажмите кнопку мыши на результате: обработчики с { capture: true } (event 2 и event 4) выполнятся раньше обработчиков с { capture: false }.

index.html
HTML
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
JavaScript
document.body.addEventListener('mousedown', () => console.log('event 1'), { capture: false })
document.body.addEventListener('mousedown', () => console.log('event 2'), { capture: true })
document.body.addEventListener('mousedown', () => console.log('event 3'), { capture: false })
document.body.addEventListener('mousedown', () => console.log('event 4'), { capture: true })

// при нажатии кнопки мыши на элементе body произойдет вызов четырех обработчиков

// event 2
// event 4
// event 1
// event 3
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

В примере для body зарегистрированы четыре обработчика mousedown. При событии на самом body сначала вызываются два перехватывающих, затем два обычных; у всех eventPhase равен 2. Если событие начинается у потомка, обработчики body относятся соответственно к перехвату и всплытию.

Следующий ключ объекта options, который мы рассмотрим это ключ once, он позволяет указать логическое значение, которое будет указывать на то, должен ли быть вызван обработчик события не более одного раза после добавления, или нет. Если указано логическое значение true, то прослушиватель будет автоматически удален после первого вызова. Давайте рассмотрим следующий пример:

Однократный обработчик с once

Нажмите ▶ и дважды нажмите кнопку мыши на результате: при первом нажатии сработают event 1 и event 2, при втором — только event 2, так как первый обработчик удалён после вызова.

index.html
HTML
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
JavaScript
document.body.addEventListener('mousedown', () => console.log('event 1'), { once: true })
document.body.addEventListener('mousedown', () => console.log('event 2'), { once: false })

// при нажатии кнопки мыши на элементе body произойдет вызов двух обработчиков

// event 1
// event 2

// при повторном нажатии кнопки мыши на элементе body произойдет вызов уже только одного обработчика

// event 2
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

В этом примере с помощью метода addEventListener() для элемента <body> мы установили два обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе. Обратите внимание, что при первом нажатии будут вызваны оба обработчика, а во втором случае, только второй, это связано с тем, что для первого обработчика был установлен ключ once объекта options в значение true, в результате чего обработчик события был удален сразу же после первого вызова. Это удобно, если вам необходимо вызвать обработчик только единожды и не беспокоиться о его удалении в дальнейшем.

Заключительный ключ объекта options, который мы рассмотрим это ключ passive, он позволяет указать логическое значение, которое будет указывать на то, может ли функция обработчик отменить действие события по умолчанию (вызвать метод preventDefault() объекта Event).

Если указано логическое значение true, то функция, указанная прослушивателем, никогда не отменит действие события по умолчанию (вызовет метод preventDefault()). Если обработчик события всё же вызовет метод preventDefault(), то браузер пользователя проигнорирует его, и создаст при этом предупреждение в консоли. Логическое значение false свидетельствует о том, что функция обработчик может отменить действие события по умолчанию (может вызвать метод preventDefault()).

Если passive не задан, DOM Standard определяет true для touchstart, touchmove, wheel и mousewheel на Window, Document, корневом элементе документа и его body. Для остальных сочетаний значение по умолчанию — false.

Если обработчик должен отменять действие, задайте { passive: false } явно. Отмена возможна только для отменяемого события. При passive: true вызов preventDefault() не действует.

Вам не нужно беспокоиться о значении passive для базового события прокрутки (scroll), так как его нельзя отменить, прослушиватели событий в любом случае не смогут блокировать показ страницы.

Нюансы использования this в обработчике события

Как правило необходимо передавать элемент, на котором сработал обработчик события, особенно это актуально при использовании обобщённого обработчика событий для элементов одного типа. Если добавить обычную функцию обработчик события к элементу с помощью метода addEventListener(), то значением this внутри такого обработчика будет являться ссылка на элемент. Значение this будет совпадать со значением свойства currentTarget аргумента события объекта Event, передаваемого обработчику события, например:

Значение this в обычном обработчике

Нажмите ▶, затем кнопку в результате: обычная функция получает this, равный кнопке, поэтому в консоли появятся класс action и true.

index.html
HTML
<button id="example" class="action">Проверить this</button>
main.js
JavaScript
const elem = document.getElementById('example')
elem.addEventListener('click', function (e) {
    console.log(this.className) // выведет в консоль значение свойства className элемента elem
    console.log(e.currentTarget === this) // true
})
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

Значение this в стрелочном обработчике

Нажмите ▶, затем кнопку в результате: у стрелочной функции нет собственного this, поэтому className не будет найден, а сравнение с currentTarget даст false.

index.html
HTML
<button id="example" class="action">Проверить this</button>
main.js
JavaScript
const elem = document.getElementById('example')
elem.addEventListener('click', (e) => {
    console.log(this.className) // this не будет ссылаться на элемент elem
    console.log(e.currentTarget === this) // false
})
style.css
CSS
body {
  margin: 16px;
  font:
    16px/1.5 system-ui,
    sans-serif;
  overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
  max-width: 100%;
}

Давайте разберем еще ситуацию, при которой обработчик события указан как атрибут события на HTML элементе. Код JavaScript в значении атрибута эффективно упаковывается в функцию обработчика, которая связывает значение this способом, соответствующим методу addEventListener() (this в коде представляет ссылку на элемент):

HTML
<input id="btn" type="button" onclick="console.log(this.id)"> <!-- выведет в консоль значение свойства id элемента -->

При нажатии на элемент <input> в консоль будет выведена информация о значении глобального атрибута id элемента, так как значение this внутри функции, вызываемой кодом в значении атрибута ведет себя согласно стандартным правилам, и ссылается в данном случае на элемент, который инициировал событие. Давайте рассмотрим следующий пример:

HTML
<script>
  const myFunc = function() {
      console.log(this.id) // this не будет ссылаться на элемент, который инициировал событие
  };
</script>

<input id="btn" type="button" onclick="myFunc()"> <!-- добавляем атрибут событий onclick -->

Для того, чтобы передать необходимый контекст, вы можете использовать метод call() объекта Function, который позволяет вызывать (выполнять) функцию как метод объекта, устанавливая ее контекст исполнения (this) в указанное значение, передавая при этом необходимые аргументы:

HTML
<script>
  const myFunc = function() {
      console.log(this.id) // this будет ссылаться на элемент, который инициировал событие
  };
</script>

<input id="btn" type="button" onclick="myFunc.call(this)"> <!-- добавляем атрибут событий onclick -->

В этом случае при срабатывании события в консоль будет выводиться значение глобального атрибута id элемента, который вызвал событие.

Удаление обработчика через AbortSignal

Нажмите ▶, затем «Событие» несколько раз. Нажмите «Отменить подписку»: новые события больше не увеличивают счётчик. Повторный запуск создаёт новую подписку.

index.html
HTML
<button id="event" type="button">Событие</button>
<button id="abort" type="button">Отменить подписку</button>
<p>Обработано: <output>0</output></p>
main.js
JavaScript
const controller = new AbortController()
const output = document.querySelector('output')
let count = 0
document.querySelector('#event').addEventListener(
    'click',
    () => {
        output.textContent = String(++count)
    },
    { signal: controller.signal }
)
document.querySelector('#abort').addEventListener('click', () => controller.abort())

Фаза на цели и отмена события

Нажмите кнопку и сравните фазы 1, 2, 2, 3. Затем измените bubbles или cancelable: отмена действия не останавливает распространение.

index.html
HTML
<div id="parent"><button id="target" type="button">Отправить событие</button></div>
<pre id="result" aria-live="polite"></pre>
main.js
JavaScript
const target = document.querySelector('#target')
const parent = document.querySelector('#parent')
const result = document.querySelector('#result')
function log(label, event) {
    result.textContent += label + ': фаза ' + event.eventPhase + '\n'
}
parent.addEventListener('check', (event) => log('Предок, capture', event), true)
target.addEventListener('check', (event) => log('Цель, capture', event), true)
target.addEventListener('check', (event) => {
    log('Цель, обычный обработчик', event)
    event.preventDefault()
})
parent.addEventListener('check', (event) => log('Предок, всплытие', event))
target.addEventListener('click', () => {
    result.textContent = 'До dispatchEvent\n'
    const event = new Event('check', { bubbles: true, cancelable: true })
    const accepted = target.dispatchEvent(event)
    result.textContent += 'После dispatchEvent: ' + accepted + '\n'
    result.textContent += 'Действие отменено: ' + event.defaultPrevented
})