addEventListener和onclick有什么区别?

var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);

上面的代码一起驻留在一个单独的.js文件中,它们都可以完美地工作。


当前回答

一个元素对每种事件类型只能有一个事件处理程序,但可以有多个事件监听器。


那么,它的实际情况如何呢?

只有最后分配的事件处理程序才会运行:

const button = document.querySelector(".btn")
button.onclick = () => {
  console.log("Hello World");
};
button.onclick = () => {
  console.log("How are you?");
};
button.click() // "How are you?" 

所有事件监听器将被触发:

const button = document.querySelector(".btn")
button.addEventListener("click", event => {
  console.log("Hello World");
})
button.addEventListener("click", event => {
  console.log("How are you?");
})
button.click() 
// "Hello World"
// "How are you?"

IE注:attachEvent不再支持。从IE 11开始,使用addEventListener: docs。

其他回答

如果你有另外两个函数,你可以看到区别:

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在版本9之前的ie中不能工作。你可以在IE <9中使用类似的attachEvent(语法略有不同)。

这两种方法都是正确的,但没有一种方法本身是“最好的”,开发人员选择使用这两种方法可能是有原因的。

《捉鬼列表和衰人》

早期版本的Internet Explorer对JavaScript的实现与其他浏览器几乎不同。对于小于9的版本,您使用attachEvent[doc]方法,如下所示:

element.attachEvent('onclick', function() { /* do stuff here*/ });

在大多数其他浏览器(包括ie9及以上)中,您使用addEventListener[doc],如下所示:

element.addEventListener('click', function() { /* do stuff here*/ }, false);

使用这种方法(DOM Level 2事件),理论上可以将无限数量的事件附加到任何单个元素。唯一的实际限制是客户端内存和其他性能问题,这对于每个浏览器都是不同的。

上面的例子表示使用匿名函数[doc]。你也可以使用函数引用[doc]或闭包[doc]来添加事件监听器:

var myFunctionReference = function() { /* do stuff here*/ }

element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);

addEventListener的另一个重要特性是final参数,它控制监听器如何对冒泡事件做出反应。我在示例中传递了false,这可能是95%用例的标准。对于attachEvent,或者在使用内联事件时,没有等效的参数。

内联事件(HTML onclick=""属性和元素。onclick)

在所有支持javascript的浏览器中,您都可以将事件监听器内联,也就是在HTML代码中。你可能看过这个:

<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>

大多数有经验的开发人员都避免使用这种方法,但它确实可以完成工作;它简单直接。这里您不能使用闭包或匿名函数(尽管处理程序本身是某种匿名函数),而且您对作用域的控制是有限的。

你提到的另一种方法:

element.onclick = function () { /*do stuff here */ };

... 它相当于内联javascript,只不过你可以更好地控制作用域(因为你写的是脚本而不是HTML),并且可以使用匿名函数、函数引用和/或闭包。

内联事件的显著缺点是,与上面描述的事件侦听器不同,您可能只分配了一个内联事件。内联事件存储为元素[doc]的属性/属性,这意味着它可以被覆盖。

使用上面HTML中的例子<a>:

var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };

... 当你点击元素时,你只会看到“Did stuff #2”——你用第二个值覆盖了onclick属性的第一个赋值,你也覆盖了原始的HTML内嵌onclick属性。点击这里查看:http://jsfiddle.net/jpgah/。

一般来说,不要使用内联事件。它可能有特定的用例,但是如果您不能100%确定您有这个用例,那么您就不应该也不应该使用内联事件。

现代Javascript (Angular之类的)

自从这个答案最初发布以来,像Angular这样的javascript框架已经变得更加流行。你会在Angular模板中看到这样的代码:

<button (click)="doSomething()">Do Something</button>

这看起来像一个内联事件,但它不是。这种类型的模板将转换为更复杂的代码,在幕后使用事件侦听器。我在这里所写的关于事件的所有内容仍然适用,但你至少从本质上被移除了一层。您应该了解具体细节,但如果您的现代JS框架最佳实践涉及在模板中编写这类代码,不要觉得您在使用内联事件——您不是。

哪个是最好的?

The question is a matter of browser compatibility and necessity. Do you need to attach more than one event to an element? Will you in the future? Odds are, you will. attachEvent and addEventListener are necessary. If not, an inline event may seem like they'd do the trick, but you're much better served preparing for a future that, though it may seem unlikely, is predictable at least. There is a chance you'll have to move to JS-based event listeners, so you may as well just start there. Don't use inline events.

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/

考虑到所有这些因素,除非您正在查看的脚本以其他方式考虑了浏览器差异(在您的问题中没有显示的代码中),否则使用addEventListener的部分将无法在小于9的IE版本中工作。

文档及相关阅读

W3 HTML规范,元素事件处理程序属性 元素。addEventListener在MDN 元素。attachEvent在MSDN上 Jquery.on quirksmode博客“事件介绍” cdn托管的javascript库在谷歌

一个细节还没有被注意到:现代桌面浏览器将不同的按钮按下视为AddEventListener的“单击”(默认为“单击”和“onclick”)。

在Chrome 42和IE11上,onclick和AddEventListener都单击左边的fire和中间的click。 在Firefox 38上,onclick只在左键点击时触发,但AddEventListener点击在左、中、右击时触发。

此外,当使用滚动游标时,中键点击行为在浏览器中非常不一致:

在Firefox上,中键单击事件总是会触发。 在Chrome浏览器上,如果中间点击打开或关闭滚动光标,它们不会触发。 在IE上,它们会在滚动光标关闭时触发,但不会在打开时触发。

同样值得注意的是,任何键盘可选择的HTML元素(如input)的“click”事件也会在元素被选中时触发空格或enter。

也可以通过对侦听器进行原型化(如果我们有对它的引用,而且它不是匿名函数)来扩展侦听器,或者使onclick调用成为对函数库的调用(函数调用其他函数)。

如:

elm.onclick = myFunctionList;
function myFunctionList(){
    myFunc1();
    myFunc2();
}

这意味着我们永远不需要改变onclick调用,只需要改变myFunctionList()函数来做任何我们想做的事情,但这让我们无法控制冒泡/捕获阶段,所以应该避免在新浏览器中使用。

使用内联处理程序与内容安全策略不兼容,因此addEventListener方法从这个角度来看更安全。当然,您可以使用unsafe-inline来启用内联处理程序,但是,顾名思义,它并不安全,因为它会带来CSP阻止的大量JavaScript漏洞。