Какая разница между addEventListener
и onclick
?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
Код выше находится в отдельном файле .js, и оба они отлично работают.
Какая разница между addEventListener
и onclick
?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
Код выше находится в отдельном файле .js, и оба они отлично работают.
Оба правильные, но ни один из них не является "лучшим" как таковой, и может быть причина, по которой разработчик решил использовать оба подхода.
Слушатели событий (addEventListener и IE attachEvent)
Более ранние версии Internet Explorer реализуют javascript по-разному от почти любого другого браузера. С версиями менее 9 вы используете метод attachEvent
[ doc], например:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
Используя этот подход (DOM Level 2 events), вы можете присоединить теоретически неограниченное количество событий к любому отдельному элементу. Единственным практическим ограничением является память на стороне клиента и другие проблемы производительности, которые различны для каждого браузера.
Приведенные выше примеры представляют собой анонимную функцию [doc]. Вы также можете добавить прослушиватель событий, используя ссылку на функцию [doc] или закрытие [doc]:
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
Еще одна важная особенность addEventListener
- это последний параметр, который контролирует реакцию слушателя на события барботажа [doc]. В примерах я ошибался, что является стандартным, вероятно, 95% случаев использования. Нет эквивалентного аргумента для attachEvent
или при использовании встроенных событий.
Встроенные события (HTML onclick = "" свойство и element.onclick)
Во всех браузерах, поддерживающих javascript, вы можете поместить прослушиватель событий inline, что означает прямо в HTML-коде. Вы, наверное, видели это:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
Большинство опытных разработчиков избегают этого метода, но он выполняет свою работу; это просто и прямо. Здесь вы не можете использовать закрытие или анонимные функции (хотя сам обработчик является анонимной функцией), и ваш контроль над областью ограничен.
Другой метод, который вы упомянули:
element.onclick = function () { /*do stuff here */ };
... является эквивалентом встроенного javascript, за исключением того, что вы больше контролируете область (поскольку вы пишете script, а не HTML) и можете использовать анонимные функции, ссылки на функции и/или закрытие.
Существенным недостатком встроенных событий является то, что в отличие от прослушивателей событий, описанных выше, у вас может быть только одно встроенное событие. Встроенные события сохраняются как атрибут/свойство элемента [doc], что означает, что его можно перезаписать.
Используя пример <a>
из HTML выше:
var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };
... когда вы щелкнете по элементу, вы бы только смотрели "Сделал материал №2" - вы перенесили первый присваиваемый свойство onclick
со вторым значением, и вы перезаписали оригинальное встроенное свойство HTML onclick
. Проверьте здесь: http://jsfiddle.net/jpgah/.
Что лучше?
Вопрос заключается в совместимости браузера и необходимости. Вам в настоящее время нужно прикрепить несколько элементов к элементу? Будете ли вы в будущем? Скорее всего, вы это сделаете. attachEvent и addEventListener необходимы. Если нет, встроенное событие сделает трюк.
jQuery и другие фреймворки javascript инкапсулируют различные версии браузера DOM уровня 2 в общих моделях, поэтому вы можете писать код, совместимый с кросс-браузером, без необходимости беспокоиться об истории IE как повстанца. Тот же код с jQuery, весь кросс-браузер и готовый к скале:
$(element).on('click', function () { /* do stuff */ });
Не заканчивайте и не создавайте рамки только для этой одной вещи. Вы можете легко развернуть свою небольшую утилиту, чтобы заботиться о старых браузерах:
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
// example
addEvent(
document.getElementById('myElement'),
'click',
function () { alert('hi!'); }
);
Попробуйте: http://jsfiddle.net/bmArj/
Принимая во внимание все это, если только script, который вы просматриваете, не учитывает различия браузера другим способом (в коде, не указанном в вашем вопросе), часть с использованием addEventListener
не будет работать в IE версии менее 9.
Документация и связанное с ней чтение
Разница, которую вы могли видеть, если бы у вас была еще одна пара функций:
var h = document.getElementById('a');
h.onclick = doThing_1;
h.onclick = doThing_2;
h.addEventListener('click', doThing_3);
h.addEventListener('click', doThing_4);
Функции 2, 3 и 4 работают, но 1 нет. Это связано с тем, что addEventListener
не перезаписывает существующие обработчики событий, тогда как onclick
переопределяет все существующие обработчики событий onclick = fn
.
Другое существенное различие, конечно, в том, что onclick
всегда будет работать, тогда как addEventListener
не работает в Internet Explorer до версии 9. Вы можете использовать аналогичный attachEvent
(который имеет несколько иной синтаксис) в IE <9.
В этом ответе я опишу три метода определения обработчиков событий DOM.
element.addEventListener()
Пример кода:
const element = document.querySelector('a');
element.addEventListener('click', event => event.preventDefault(), true);
<a href="//google.com">Try clicking this link.</a>
Пока onclick
работает во всех браузерах, addEventListener
не работает в более старых версиях Internet Explorer, который вместо этого использует attachEvent
.
Недостатком onclick
является то, что может быть только один обработчик событий, в то время как другие два будут запускать все зарегистрированные обратные вызовы.
Насколько я знаю, событие "загрузки" DOM по-прежнему работает очень ограниченно. Это означает, что он будет срабатывать только для элементов window object
, images
и <script>
. То же самое касается прямого назначения onload
. Технической разницы между этими двумя нет. Вероятно, .onload =
имеет лучшую кросс-браузерную доступность.
Однако вы не можете назначить load event
элементу <div>
или <span>
или еще что-то.
Если вы не слишком беспокоитесь о поддержке браузера, есть способ восстановить ссылку 'this' в функции, вызванной событием. Он обычно указывает на элемент, который генерировал событие, когда функция выполняется, что не всегда то, что вы хотите. Сложная часть состоит в том, чтобы одновременно удалить одного и того же прослушивателя событий, как показано в этом примере: http://jsfiddle.net/roenbaeck/vBYu3/
/*
Testing that the function returned from bind is rereferenceable,
such that it can be added and removed as an event listener.
*/
function MyImportantCalloutToYou(message, otherMessage) {
// the following is necessary as calling bind again does
// not return the same function, so instead we replace the
// original function with the one bound to this instance
this.swap = this.swap.bind(this);
this.element = document.createElement('div');
this.element.addEventListener('click', this.swap, false);
document.body.appendChild(this.element);
}
MyImportantCalloutToYou.prototype = {
element: null,
swap: function() {
// now this function can be properly removed
this.element.removeEventListener('click', this.swap, false);
}
}
Приведенный выше код хорошо работает в Chrome, и, возможно, есть некоторый подгон, который делает привязку совместимой с другими браузерами.
Использование встроенных обработчиков несовместимо с политикой безопасности контента, поэтому подход addEventListener
более безопасен с этой точки зрения. Конечно, вы можете включить встроенные обработчики с помощью unsafe-inline
, но, как следует из названия, оно небезопасно, поскольку оно возвращает все орды эксплойтов JavaScript, которые предотвращает CSP.
Одна деталь еще не отмечена: современные настольные браузеры рассматривают разные нажатия кнопок для "щелчков" для AddEventListener('click'
и onclick
по умолчанию.
onclick
, так и AddEventListener
нажмите стрелку по левому и среднему нажатию.onclick
срабатывает только при щелчке левой кнопкой мыши, но AddEventListener
нажимает на стрелки влево, в середине и вправо.Кроме того, поведение срединного клика очень несовместимо в браузерах при использовании указателей прокрутки:
Также стоит отметить, что события "click" для любого выбираемого клавиатурой элемента HTML, такого как input
, также запускаются в пространстве или вводятся, когда элемент выбран.
Также должно быть возможно либо расширить слушатель, прототипируя его (если у нас есть ссылка на него и его не анонимная функция), либо сделайте вызов "onclick" вызовом библиотеки функций (функция, вызывающая другие функции)
как
elm.onclick = myFunctionList
function myFunctionList(){
myFunc1();
myFunc2();
}
это означает, что нам никогда не нужно вводить вызов onclick, просто изменив функцию myFunctionList(), чтобы делать то, что мы хотим, но это оставляет нас без контроля фаз пузырьков/ловушек, поэтому их следует избегать для более новых браузеров.
на всякий случай кто-то найдет эту тему в будущем...
Согласно MDN, разница следующая:
addEventListener:
Метод EventTarget.addEventListener() добавляет указанный объект, совместимый с EventListener, в список прослушивателей событий для указанного типа события на EventTarget, на котором он вызвал. Целью мероприятия может быть элемент в документе, сам документ, окно или любой другой объект, который поддерживает события (например, XMLHttpRequest).
по щелчку:
Свойство onclick возвращает код обработчика события клика для текущего элемента. При использовании события click для запуска действия также рассмотрите возможность добавления этого же действия к событию keydown, чтобы разрешить использование этого же действия людьми, которые не используют мышь или сенсорный экран. Синтаксис element.onclick = functionRef; где functionRef - это функция - часто это имя функции, объявленной в другом месте, или выражение функции. Подробнее см. "Руководство JavaScript: Функции".
Существует также синтаксическая разница в использовании, как вы видите в приведенных ниже кодах:
addEventListener:
// Function to change the content of t2
function modifyText() {
var t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue == "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
}
}
// add event listener to table
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
по щелчку:
function initElement() {
var p = document.getElementById("foo");
// NOTE: showAlert(); or showAlert(param); will NOT work here.
// Must be a reference to a function name, not a function call.
p.onclick = showAlert;
};
function showAlert(event) {
alert("onclick Event detected!");
}
addEventListener
позволяет устанавливать несколько обработчиков, но не поддерживается в IE8 или ниже.
IE имеет attachEvent
, но это не совсем то же самое.
Javascript имеет тенденцию смешивать все с объектами, и это может сбить с толку. Все в одном - это способ JavaScript.
По существу onclick - это атрибут HTML. И наоборот, addEventListener - это метод объекта DOM, представляющий элемент HTML.
В объектах JavaScript метод - это просто свойство, которое имеет функцию как значение и работает против объекта, к которому он привязан (используя это, например).
В JavaScript, поскольку HTML-элемент, представленный DOM, будет иметь атрибуты, сопоставленные его свойствам.
Здесь люди путаются, потому что JavaScript объединяет все в один контейнер или пространство имен без слоя косвенности.
В обычном макете OO (который, по крайней мере, объединяет пространство имен свойств/методов), вы могли бы иметь что-то вроде:
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
Есть такие варианты, как он может использовать getter/setter для onload или HashMap для атрибутов, но в конечном счете, как он будет выглядеть. JavaScript устранил этот слой косвенности, ожидая узнать, что среди прочего. Он объединил domElement и атрибуты вместе.
Устранение совместимости, которую вы должны использовать в качестве лучшей практики, использует addEventListener. Поскольку другие ответы говорят о различиях в этом отношении, а не о фундаментальных программных различиях, я откажусь от этого. По сути, в идеальном мире вы действительно должны использовать только * из HTML, но в еще более идеальном мире вы не должны делать что-либо подобное из HTML.
Почему это доминирует сегодня? Это быстрее писать, легче учиться и имеет тенденцию просто работать.
Весь смысл загрузки в HTML - это, в первую очередь, доступ к методу или функциям addEventListener. Используя его в JS, вы просматриваете HTML, когда можете применить его напрямую.
Гипотетически вы можете создавать свои собственные атрибуты:
$('[myclick]').each(function(i, v) {
v.addEventListener('click', function() {
eval(v.myclick); // eval($(v).attr('myclick'));
});
});
То, что делает JS, немного отличается от этого.
Вы можете приравнять его к чему-то вроде (для каждого созданного элемента):
element.addEventListener('click', function() {
switch(typeof element.onclick) {
case 'string':eval(element.onclick);break;
case 'function':element.onclick();break;
}
});
Фактические детали реализации, скорее всего, будут отличаться диапазоном тонких вариаций, в результате чего в некоторых случаях они немного отличаются друг от друга, но суть его.
Возможно, это взлом совместимости, который вы можете привязать к атрибуту on, поскольку по умолчанию атрибуты - это все строки.
addEventListener
может добавлять несколько событий, тогда как с onclick
это невозможно.onclick
может быть добавлен как атрибут HTML
, тогда как addEventListener
может быть добавлен только в элементы <script>
.addEventListener
может принять третий аргумент, который может остановить распространение события. Оба могут использоваться для обработки событий. Однако addEventListener
должен быть предпочтительным выбором, поскольку он может делать все, что делает onclick
, и многое другое. Не используйте inline onclick
как атрибуты HTML, так как это смешивает javascript и HTML, что является плохой практикой. Это делает код менее ремонтопригодным.
Контекст, на который ссылается ключевое слово 'this'
в JavasSript, отличается.
посмотрите следующий код:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<input id="btnSubmit" type="button" value="Submit" />
<script>
function disable() {
this.disabled = true;
}
var btnSubmit = document.getElementById('btnSubmit');
btnSubmit.onclick = disable();
//btnSubmit.addEventListener('click', disable, false);
</script>
</body>
</html>
То, что он делает, действительно просто. при нажатии кнопки кнопка автоматически отключается.
Сначала, когда вы пытаетесь подключить события таким образом button.onclick = function(),
Событие onclick будет вызвано нажатием кнопки, однако кнопка не будет отключена, потому что нет явного связывания между button.onclick и обработчиком события onclick. Если вы отлаживаете объект 'this'
, вы можете увидеть, что он относится к объекту 'window'
.
Во-вторых, если вы прокомментируете btnSubmit.onclick = disable();
и раскомментируете
//btnSubmit.addEventListener('click', disable, false);
вы можете видеть, что кнопка отключена, потому что таким образом существует явная привязка между событием button.onclick и обработчиком события onclick. Если вы отлаживаете функцию отключения, вы можете видеть, что 'this'
относится к button control
, а не к window
.
Это то, что мне не нравится в JavaScript, что является несогласованностью.
Btw, если вы используете jQuery ($('#btnSubmit').on('click', disable);
), он использует явное связывание.