Метод EventTarget.addEventListener()

Метод EventTarget.addEventListener() регистрирует определенный обработчик события, вызванного на EventTarget.

EventTarget должен быть либо существующим элементом в документе, либо Document, либо Window, либо любым другим объектом, который поддерживает события (такой, как XMLHttpRequest).

Синтаксис

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);
target.addEventListener(type, listener[, useCapture, 
wantsUntrusted  ]); // только Gecko/Mozilla 

Параметры

type
Строка, представляющая тип прослушиваемого события.
listener
Объект, который принимает уведомление, когда событие указанного типа произошло. Это должен быть объект, реализующий интерфейс EventListener или просто функция JavaScript.
options Необязательный
Объект options, который определяет характеристики объекта, прослушивающего событие. Доступны следующие варианты:
  • capture:  Boolean указывает, что события этого типа будут отправлены зарегистрированному слушателю listener перед отправкой на EventTarget, расположенный ниже в дереве DOM.
  • once: Boolean указывает, что слушатель должен быть вызван не более одного раза после добавления. Если true, слушатель автоматически удаляется при вызове.
  • passive:  Boolean указывает, что слушатель никогда не вызовет preventDefault(). Если все же вызов будет произведен, браузер должен игнорировать его и генерировать консольное предупреждение. Пример Улучшение производительности прокрутки с помощью passive true
  •  mozSystemGroup: Boolean указывает, что слушатель должен быть добавлен в системную группу. Доступно только в коде, запущенном в XBL или в расширении Chrome.
useCapture Необязательный
Если равно true, useCapture указывает, что пользователь желает начать захват. После инициализации захвата все события указанного типа будут отправлены в зарегистрированный listener перед отправкой в какой-либо EventTarget под ним в дереве DOM. События, восходящие вверх по дереву, не будут вызывать слушателей, которым назначено использовать захват. Смотрите DOM Level 3 Events для более детального объяснения. Значение useCapture по умолчанию равно false.
Note: Для прослушивателей событий прикреплённых к цели события, событие  находиться в целевой фазе, а не в фазах захвата или всплытия. События в целевой фазе инициируют все прослушиватели на элементе в том порядке, в котором они были зарегистрированы независимо от параметра useCapture.
Note: useCapture не всегда был опциональным. Лучше указывать данный параметр для повышения совместимости.
wantsUntrusted
Если равно true, слушатель будет получать сгенерированные события, посланные со страницы (по умолчанию равно false для chrome и true для обычных веб-страниц). Этот параметр доступен только в Gecko и в основном полезен для использования в дополнениях и самом браузере. Смотрите Взаимодействие между привилегированными и непривилегированными страницами для примеров использования.

Прежде чем использовать определенное значение в объекте options, рекомендуется убедиться, что браузер пользователя поддерживает его, поскольку это дополнение, которое не все браузеры поддерживали исторически.

Возвращаемое значение

undefined

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

Обратный вызов прослушивателя событий

Прослушиватель событий может быть задан либо как функция обратного вызова, либо как объект реализующий EventListener, чей handleEvent() метод служит как функция обратного вызова.

Сама функция обранного вызова имеет те же параметры и возвращаемое значение что и метод handleEvent(); То есть обратный вызов принимает единственный параметр: объект основанный на Event описывая событие, которое произошло и ничего не возвращая.

Например, обратный вызов обработчика событий, который может использоваться для обработки fullscreenchange и fullscreenerror может выглядеть так:

function eventHandler(event) {
  if (event.type == 'fullscreenchange') {
    /* Переключатель полноэкранного режима */
  } else /* fullscreenerror */ {
    /* Ошибка переключателя полноэкранного режима */
  }
}

Безопасная проверка поддержки option

В более старых версиях спецификации DOM третьим параметром addEventListener было логическое значение, указывающее, следует ли захватывать событие на этапе погружения. Со временем стало ясно, что необходимо больше вариантов. Вместо добавления дополнительных параметров в функцию (усложняя ситуацию при использовании необязательных значений) третий параметр был изменен на объект, который может содержать различные свойства, определяющие значения параметров для настройки слушателя событий.

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

Например, если вы хотите проверить параметр passive:

var passiveSupported = false;

try {
  var options = Object.defineProperty({}, "passive", {
    get: function() {
      passiveSupported = true;
    }
  });

  window.addEventListener("test", null, options);
} catch(err) {}

Этот код создает объект options с геттером для свойства passive, устанавливающим флаг passiveSupported в true, если он вызван. Это означает, что если браузер проверяет значение свойства passive на объекте options, значение passiveSupported будет установлено в true; в противном случае он останется ложным. Затем мы вызываем addEventListener, чтобы настроить фальшивый обработчик событий, указав эти параметры для проверки опций, если браузер распознает объект в качестве третьего параметра.

Для проверки поддержки использования какой-либо опции можно просто добавить геттер для нее, используя код, подобный тому, что показан выше.

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

someElement.addEventListener("mouseup", handleMouseUp, passiveSupported
                               ? { passive: true } : false);

Здесь мы добавляем слушателя для события mouseup элемента someElement. Для третьего параметра, если passiveSupported имеет значение true, мы указываем объект options с passive: true; в противном случае мы знаем, что нам нужно передать логическое значение, и мы передаем false как значение параметра useCapture.

Вы можете использовать стороннюю библиотеку, такую как Modernizr или Detect It, чтобы проверить поддержку необходимого свойства.

Узнайте больше о EventListenerOptions из  Web Incubator Community Group.

Примеры

Добавление простого слушателя

Эти примеры демонстрируют как использовать addEventListener() для наблюдения за щелчками мышкой по элементу.

HTML Содержимое

<table id="outside">
    <tr><td id="t1">один</td></tr>
    <tr><td id="t2">два</td></tr>
</table>

JavaScript Содержимое

// Функция изменяет содержимое t2
function modifyText() {
  var t2 = document.getElementById("t2");
  if (t2.firstChild.nodeValue == "три") {
    t2.firstChild.nodeValue = "два";
  } else {
    t2.firstChild.nodeValue = "три";
  }
}

// Добавляет слушателя событий для таблицы
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);

В примере выше, modifyText() регистрирует слушателя для события click, используя addEventListener(). Клик в любом месте таблицы будет поднимать обработчик и запускать modifyText().

Результат

Если вам нужно передать параметры в слушателя, вы можете использовать анонимные функции.

Слушатель события с анонимной функцией

HTML Содержимое

<table id="outside">
    <tr><td id="t1">один</td></tr>
    <tr><td id="t2">два</td></tr>
</table>

JavaScript Содержимое

// Функция, изменяющая содержание t2
function modifyText(new_text) {
  var t2 = document.getElementById("t2");
  t2.firstChild.nodeValue = new_text;    
}
 
// Функция, добавляющая слушатель к таблице
el = document.getElementById("outside");
el.addEventListener("click", function(){modifyText("четыре")}, false);

Notice that the listener is an anonymous function that encapsulates code that is then, in turn, able to send parameters to the modifyText() function, which is responsible for actually responding to the event.

Результат

Слушатель события со стрелочной функцией

HTML

<table id="outside">
    <tr><td id="t1">one</td></tr>
    <tr><td id="t2">two</td></tr>
</table>

JavaScript

// Function to change the content of t2
function modifyText(new_text) {
  var t2 = document.getElementById("t2");
  t2.firstChild.nodeValue = new_text;    
}
 
// Add event listener to table with an arrow function
var el = document.getElementById("outside");
el.addEventListener("click", () => { modifyText("four"); }, false);

Результат

Обратите внимание: несмотря на то, что анонимные и стрелочные функции схожи, они имеют разные значения this.

Заметки

Зачем использовать addEventListener?

addEventListener — это способ зарегистрировать обработчик события, описанный в документации W3C DOM. Вот список преимуществ его использования:

  • Позволяет добавлять множество обработчиков для одного события. Это особенно полезно для DHTML библиотек или Mozilla extensions, которые должны работать в условиях использования сторонних библиотек/расширений.
  • Предоставляет точный контроль фазы срабатывания(вызова) обработчика (захват или всплытие)
  • Срабатывает на любом DOM элементе, а не только на HTML элементах.

Ниже описан другой, более старый способ регистрации обработчиков.

Добавление слушателя во время обработки события

Если EventListener добавлен к EventTarget во время обработки события, он не будет вызван текущими действиями, но может быть вызван на более поздней стадии обработки события, при восходящей обработке.

Несколько одинаковых слушателей события

Если зарегистрировано несколько одинаковых EventListener на одном EventTarget с одинаковыми параметрами, дублирующиеся слушатели игнорируются. Так как одинаковые слушатели игнорируются, не требуется удалять их вручную с помощью метода removeEventListener.

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

Обычно желательно передавать элемент, на котором сработал обработчик события, например, при использовании обобщённых обработчиков для схожих элементов. При добавлении функции при помощи addEventListener() значение переменной this меняется — заметьте, что значение this передаётся в функцию от вызывающего объекта.

В примере выше значение переменной this внутри modifyText() при вызове событием клика равно таблице 't'. Это противоположно поведению, которое возникает, если обработчик добавлен в HTML-разметке:

<table id="t" onclick="modifyText();">
  . . .

Значение переменной this внутри modifyText() при вызове событием клика будет равно ссылке на глобальный (window) объект (или undefined при использовании strict mode)

Note: В JavaScript 1.8.5 введён метод Function.prototype.bind() , который позволяет указать значение, которое должно быть использовано для всех вызовов данной функции. Он позволяет вам легко обходить ситуации, в которых не ясно, чему будет равно this, в зависимости от того, в каком контексте будет вызвана ваша функция. заметьте, также, что Вам будет необходимо иметь внешню ссылку на слушатель, чтобы Вы могли удалить его позже.

Пример с использованием bind и без него:

var Something = function(element) {
  this.name = 'Something Good';
  this.onclick1 = function(event) {
    console.log(this.name); // undefined, так как this является элементом
  };
  this.onclick2 = function(event) {
    console.log(this.name); // 'Something Good', так как в this передано значение объекта Something
  };
  element.addEventListener('click', this.onclick1, false);
  element.addEventListener('click', this.onclick2.bind(this), false); // Trick
}

Проблема в примере выше заключается в том, что Вы не можете удалить слушатель, вызванный с bind. Другое решение использует специальную функцию handleEvent, чтобы перехватывать любые события:

var Something = function(element) {
  this.name = 'Something Good';
  this.handleEvent = function(event) {
    console.log(this.name); // 'Something Good', так как this является объектом Something
    switch(event.type) {
      case 'click':
        // код обработчика...
        break;
      case 'dblclick':
        // код обработчика...
        break;
    }
  };

  // В этом случае слушатели хранятся в this, а не в this.handleEvent
  element.addEventListener('click', this, false);
  element.addEventListener('dblclick', this, false);

  // Вы можете напрямую удалять слушатели
  element.removeEventListener('click', this, false);
  element.removeEventListener('dblclick', this, false);
}

Наследство Internet Explorer и attachEvent

В Internet Explorer младше 9 версии, вы можете использовать attachEvent вместо стандартного addEventListener. Для поддержки IE, пример выше может быть модифицирован следующим образом:

if (el.addEventListener) {
  el.addEventListener('click', modifyText, false); 
} else if (el.attachEvent)  {
  el.attachEvent('onclick', modifyText);
}

У attachEvent есть недостаток: this будет ссылаться на объект window, а не на элемент, на котором он был вызван.

Совместимость

Вы можете обойти методы addEventListener, removeEventListener, Event.preventDefault и Event.stopPropagation не поддерживаемы в IE 8 используя следующий код в начале Вашего скрипта. Этот код подерживает использование handleEvent и события DOMContentLoaded.

Note: useCapture не поддерживается, так как IE 8 не имеет альтернативного метода для этого. Также заметьте, что следующий код только добавляет поддержку IE 8. Также, он работает только при соблюдении стандартов: объявление DOCTYPE страницы обязательно.

(function() {
  if (!Event.prototype.preventDefault) {
    Event.prototype.preventDefault=function() {
      this.returnValue=false;
    };
  }
  if (!Event.prototype.stopPropagation) {
    Event.prototype.stopPropagation=function() {
      this.cancelBubble=true;
    };
  }
  if (!Element.prototype.addEventListener) {
    var eventListeners=[];
    
    var addEventListener=function(type,listener /*, useCapture (will be ignored) */) {
      var self=this;
      var wrapper=function(e) {
        e.target=e.srcElement;
        e.currentTarget=self;
        if (listener.handleEvent) {
          listener.handleEvent(e);
        } else {
          listener.call(self,e);
        }
      };
      if (type=="DOMContentLoaded") {
        var wrapper2=function(e) {
          if (document.readyState=="complete") {
            wrapper(e);
          }
        };
        document.attachEvent("onreadystatechange",wrapper2);
        eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2});
        
        if (document.readyState=="complete") {
          var e=new Event();
          e.srcElement=window;
          wrapper2(e);
        }
      } else {
        this.attachEvent("on"+type,wrapper);
        eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper});
      }
    };
    var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) {
      var counter=0;
      while (counter<eventListeners.length) {
        var eventListener=eventListeners[counter];
        if (eventListener.object==this && eventListener.type==type && eventListener.listener==listener) {
          if (type=="DOMContentLoaded") {
            this.detachEvent("onreadystatechange",eventListener.wrapper);
          } else {
            this.detachEvent("on"+type,eventListener.wrapper);
          }
          eventListeners.splice(counter, 1);
          break;
        }
        ++counter;
      }
    };
    Element.prototype.addEventListener=addEventListener;
    Element.prototype.removeEventListener=removeEventListener;
    if (HTMLDocument) {
      HTMLDocument.prototype.addEventListener=addEventListener;
      HTMLDocument.prototype.removeEventListener=removeEventListener;
    }
    if (Window) {
      Window.prototype.addEventListener=addEventListener;
      Window.prototype.removeEventListener=removeEventListener;
    }
  }
})();

Старый способ регистрации обработчиков событий

addEventListener() был добавлен в спецификации DOM 2 Events. До этого слушатели добавлялись следующим образом:

// Передача ссылки на функцию — не добавляйте '()' после него, это вызовет функцию!
el.onclick = modifyText;

// Использование функционального выражения
element.onclick = function() {
  // ... логика функции ...
};

Этот метод заменяет текущие слушатели события click, если они есть. Тоже самое для других событий и ассоциируемых с ними  обработчиков, таких как blur (onblur), keypress (onkeypress), и так далее.

Так как это по существу было частью DOM 0, этот метод имеет широкую поддержку и не требует специального кросс-браузерного кода; следовательно, это обычно используется, чтобы добавлять слушатели динамически, если не требуются расширенные возможности addEventListener().

Вопросы памяти

var i;
var els = document.getElementsByTagName('*');

// Случай 1
for(i=0 ; i<els.length ; i++){
  els[i].addEventListener("click", function(e){/*некоторые действия*/}, false);
}

// Случай 2
function processEvent(e){
  /*некоторые действия*/
}

for(i=0 ; i<els.length ; i++){
  els[i].addEventListener("click", processEvent, false);
}

В первом случае новая (анонимная) функция создаётся при каждом шаге цикла. Во втором случае одна заранее объявленная функция используется как обработчик события. Из этого следует меньшее потребление памяти. Более того, в первом случае, вследствие отсутствия ссылок на анонимные функции, невозможно вызвать element.removeEventListener, потому что нет ссылки на обработчик, в то время, как во втором случае возможно вызвать myElement.removeEventListener("click", processEvent, false).

Улучшение производительности прокрутки с помощью passive: true

Значение по умолчанию для параметра passive - false. Начиная с Chrome 56 (desktop, Chrome for Android, Android webview) значение по умолчанию для touchstart и touchmove равно true, а вызовы preventDefault() не разрешены. Чтобы отменить это поведение, необходимо установить параметр passive в false (см. пример ниже). Это изменение не позволяет слушателю блокировать показ страницы во время прокрутки пользователя. Демонстрация доступна на сайте разработчиков Google. Обратите внимание, что Edge вообще не поддерживает options, и добавление его без проверки поддержки помешает использовать аргумент useCapture.

/* Feature detection */ 
var passiveSupported = false; 
try {
    window.addEventListener(
        "test", 
        null, 
        Object.defineProperty({}, "passive", { get: function() { passiveSupported = true; } })); 
} catch(err) {} 

/* Event Listener */ 
var elem = document.getElementById('elem'); 
elem.addEventListener(
    'touchmove', 
    function listener() {   /* do something */ }, 
    passiveSupported ? { passive: true } : false
);

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

Совместимость

Update compatibility data on GitHub
КомпьютерыМобильные
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome для AndroidFirefox для AndroidOpera для AndroidSafari on iOSSamsung Internet
addEventListenerChrome Полная поддержка 1
Замечания
Полная поддержка 1
Замечания
Замечания Before Chrome 49, the type and listener parameters were optional.
Edge Полная поддержка 12Firefox Полная поддержка 1IE Полная поддержка 9
Полная поддержка 9
Нет поддержки 6 — 11
Замечания Альтернативное имя
Замечания Older versions of IE supported an equivalent, proprietary EventTarget.attachEvent() method.
Альтернативное имя Использует нестандартное имя: attachEvent
Opera Полная поддержка 7Safari Полная поддержка 1WebView Android Полная поддержка 1
Замечания
Полная поддержка 1
Замечания
Замечания Before Chrome 49, the type and listener parameters were optional.
Chrome Android Полная поддержка 18
Замечания
Полная поддержка 18
Замечания
Замечания Before Chrome 49, the type and listener parameters were optional.
Firefox Android Полная поддержка 4Opera Android Полная поддержка 10.1Safari iOS Полная поддержка 1Samsung Internet Android Полная поддержка Да
useCapture parameter made optionalChrome Полная поддержка 1Edge Полная поддержка ДаFirefox Полная поддержка 6IE Полная поддержка 9Opera Полная поддержка 11.6Safari Полная поддержка ДаWebView Android Полная поддержка 1Chrome Android Полная поддержка 18Firefox Android Полная поддержка 6Opera Android Полная поддержка 12Safari iOS Полная поддержка ДаSamsung Internet Android Полная поддержка Да
Form with options object supported (third parameter can be either options or a Boolean, for backwards compatibility)Chrome Полная поддержка 49Edge Полная поддержка ДаFirefox Полная поддержка 49IE Нет поддержки НетOpera Полная поддержка ДаSafari Полная поддержка 10WebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 49Opera Android Полная поддержка ДаSafari iOS Полная поддержка 10Samsung Internet Android Полная поддержка 5.0
options: capture optionChrome Полная поддержка 52Edge Полная поддержка ДаFirefox Полная поддержка ДаIE Нет поддержки НетOpera Полная поддержка ДаSafari Полная поддержка ДаWebView Android Полная поддержка 52Chrome Android Полная поддержка 52Firefox Android Полная поддержка ДаOpera Android Полная поддержка ДаSafari iOS Полная поддержка ДаSamsung Internet Android Полная поддержка 6.0
options: once optionChrome Полная поддержка 55Edge Полная поддержка ДаFirefox Полная поддержка 50IE Нет поддержки НетOpera Полная поддержка 42Safari Полная поддержка ДаWebView Android Полная поддержка 55Chrome Android Полная поддержка 55Firefox Android Полная поддержка 50Opera Android Полная поддержка 42Safari iOS Полная поддержка ДаSamsung Internet Android Полная поддержка 6.0
options: passive optionChrome Полная поддержка 51Edge Полная поддержка ДаFirefox Полная поддержка ДаIE Нет поддержки НетOpera Полная поддержка ДаSafari Полная поддержка ДаWebView Android Полная поддержка 51Chrome Android Полная поддержка 51Firefox Android Полная поддержка ДаOpera Android Полная поддержка ДаSafari iOS Полная поддержка ДаSamsung Internet Android Полная поддержка 5.0
options: passive option defaults to true for touchstart and touchmove eventsChrome Полная поддержка 55Edge Нет поддержки НетFirefox Полная поддержка 61IE Нет поддержки НетOpera ? Safari Нет поддержки НетWebView Android Полная поддержка 55Chrome Android Полная поддержка 55Firefox Android Полная поддержка 61Opera Android ? Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 6.0
options: passive option defaults to true for wheel and mousewheel eventsChrome Полная поддержка 73Edge Нет поддержки НетFirefox ? IE Нет поддержки НетOpera ? Safari Нет поддержки НетWebView Android Полная поддержка 73Chrome Android Полная поддержка 73Firefox Android ? Opera Android ? Safari iOS Нет поддержки НетSamsung Internet Android ?

Легенда

Полная поддержка  
Полная поддержка
Нет поддержки  
Нет поддержки
Совместимость неизвестна  
Совместимость неизвестна
Смотрите замечания реализации.
Смотрите замечания реализации.
Использует нестандартное имя.
Использует нестандартное имя.

Заметки по Gecko

  • До Firefox 6, браузер выбросит исключение, если параметр useCapture не был точно равен false. До Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6), addEventListener() выбросит исключение, если параметр listener был равен null; сейчас метод завершается без ошибки, но ничего не делает.

Заметки по WebKit

  • Несмотря на то, что в WebKit параметр useCapture был объявлен необязательным только в июне 2011 года, это работало и до этого изменения. Новые изменения были добавлены в Safari 5.1 и Chrome 13.

Смотрите также

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