addEventListener与onClick的对比

addEventListeneronclick有什么区别?

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

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

752491 次浏览

虽然onclick适用于所有浏览器,但addEventListener不适用于旧版本的Internet Explorer,它使用attachEvent代替。

onclick的缺点是只能有一个事件处理程序,而其他两个将触发所有注册的回调。

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

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之前在Internet Explorer中不起作用。您可以在IE<9中使用类似的attachEvent(具有不同的语法)。

addEventListener允许您设置多个处理程序,但在IE8或更低版本中不受支持。

IE确实有attachEvent,但不完全相同。

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

事件监听器(addEventListener和IE的attachEvent)

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

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

在大多数其他浏览器(包括IE 9及以上)中,您使用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的另一个重要特性是最后一个参数,它控制侦听器如何对冒泡事件做出反应[doc]。我在示例中一直传递false,这可能是95%用例的标准。attachEvent没有等效的参数,或者在使用内联事件时。

内联事件(超文本标记语言onClick=""属性和element.onclick)

在所有支持javascript的浏览器中,您可以将事件侦听器内联,这意味着就在超文本标记语言代码中。

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

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

你提到的另一种方法:

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

…相当于内联javascript,除了您可以更好地控制范围(因为您正在编写脚本而不是超文本标记语言)并且可以使用匿名函数、函数引用和/或闭包。

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

使用上面超文本标记语言中的示例<a>

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

…当你单击元素时,你会只有看到“做了只有”-你用第二个值覆盖了onclick属性的第一个赋值,并且你也覆盖了原始的内联超文本标记语言onclick属性。

从广义上讲,不要使用内联事件。它可能有特定的用例,但如果您不是100%确定您有那个用例,那么您就不应该也不应该使用内联事件。

现代Javascript(Angular等)

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

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

这看起来像是一个内联事件,但事实并非如此。这种类型的模板将被转译成更复杂的代码,这些代码在幕后使用事件侦听器。我在这里写的关于事件的所有内容仍然适用,但是你至少从本质上删除了一层。你应该理解坚果和螺栓,但是如果你的现代JS框架最佳实践涉及在模板中编写这种代码,不要觉得你在使用内联事件--你不是。

哪个最好?

这个问题是一个浏览器兼容性和必要性的问题。你需要向一个元素附加多个事件吗?将来会吗?很有可能,你会的。attachEvent和addEventListener是必要的。如果不是,一个内联事件可能看起来很有用,但你最好为未来做准备,尽管看起来不太可能,但至少是可预测的。你有可能不得不转向基于JS的事件侦听器,所以你不妨从那里开始。不要使用内联事件。

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);elsereturn element.addEventListener(evnt, funct, false);}
// exampleaddEvent(document.getElementById('myElement'),'click',function () { alert('hi!'); });

试试看:http://jsfiddle.net/bmArj/

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

文献及相关阅读

据我所知,DOM“load”事件仍然只能非常有限地工作。这意味着它只会触发window objectimages<script>元素。直接onload赋值也是如此。这两者之间没有技术差异。可能.onload =具有更好的跨浏览器可用性。

但是,您不能将load event分配给<div><span>元素或其他什么。

JavasSript中'this'关键字引用的上下文不同。

看看下面的代码:

<!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事件处理程序之间存在显式绑定。如果您调试到disable函数,您可以看到'this'引用button control而不是window

这是我不喜欢JavaScript的不一致之处。顺便说一句,如果你使用jQuery($('#btnSubmit').on('click', disable);),它使用显式绑定。

如果你不太担心浏览器支持,有一种方法可以在事件调用的函数中重新绑定'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 instancethis.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 removedthis.element.removeEventListener('click', this.swap, false);}}

上面的代码在Chrome中运行良好,并且可能在使“bind”与其他浏览器兼容方面存在一些问题。

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

一个细节还没有被注意到:现代桌面浏览器默认认为不同的按钮按下是AddEventListener('click'onclick的“点击”。

  • 在Chrome42和IE11上,onclickAddEventListener都在左键单击和中键单击触发。
  • 在Firefox 38上,onclick在左键单击时触发只有,但AddEventListener在左键单击时触发,中间右键单击。

此外,当涉及滚动游标时,浏览器之间的中键单击行为非常不一致:

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

还值得注意的是,任何键盘可选择的超文本标记语言元素(如input)的“单击”事件也会在空格上触发或在选择元素时回车。

还应该可以通过原型化来扩展侦听器(如果我们有对它的引用并且它不是匿名函数)-或者使onclick调用调用函数库(调用其他函数的函数)。

喜欢:

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

这意味着我们永远不必更改onclick调用,只需更改函数myFunctionList()即可执行我们想要的任何操作,但这使我们无法控制冒泡/捕获阶段,因此对于较新的浏览器应该避免。

在这个答案中,我将描述定义DOM事件处理程序的三种方法。

element.addEventListener()

代码示例:

const element = document.querySelector('a');element.addEventListener('click', event => event.preventDefault(), true);
<a href="//google.com">Try clicking this link.</a>

element.addEventListener() has multiple advantages:

  • Allows you to register unlimited events handlers and remove them with element.removeEventListener().
  • Has useCapture parameter, which indicates whether you'd like to handle event in its capturing or bubbling phase. See: Unable to understand useCapture attribute in addEventListener.
  • Cares about semantics. Basically, it makes registering event handlers more explicit. For a beginner, a function call makes it obvious that something happens, whereas assigning event to some property of DOM element is at least not intuitive.
  • Allows you to separate document structure (HTML) and logic (JavaScript). In tiny web applications it may not seem to matter, but it does matter with any bigger project. It's way much easier to maintain a project which separates structure and logic than a project which doesn't.
  • Eliminates confusion with correct event names. Due to using inline event listeners or assigning event listeners to .onevent properties of DOM elements, lots of inexperienced JavaScript programmers thinks that the event name is for example onclick or onload. on is not a part of event name. Correct event names are click and load, and that's how event names are passed to .addEventListener().
  • Works in almost all browser. If you still have to support IE <= 8, you can use a polyfill from MDN.

element.onevent = function() {} (e.g. onclick, onload)

Code example:

const element = document.querySelector('a');element.onclick = event => event.preventDefault();
<a href="//google.com">Try clicking this link.</a>

This was a way to register event handlers in DOM 0. It's now discouraged, because it:

  • Allows you to register only one event handler. Also removing the assigned handler is not intuitive, because to remove event handler assigned using this method, you have to revert onevent property back to its initial state (i.e. null).
  • Doesn't respond to errors appropriately. For example, if you by mistake assign a string to window.onload, for example: window.onload = "test";, it won't throw any errors. Your code wouldn't work and it would be really hard to find out why. .addEventListener() however, would throw error (at least in Firefox): TypeError: Argument 2 of EventTarget.addEventListener is not an object.
  • Doesn't provide a way to choose if you want to handle event in its capturing or bubbling phase.

Inline event handlers (onevent HTML attribute)

Code example:

<a href="//google.com" onclick="event.preventDefault();">Try clicking this link.</a>

element.onevent类似,现在不鼓励使用。除了element.onevent存在的问题之外,它:

  • 潜在的安全问题,因为它使XSS更加有害。现在的网站应该发送适当的Content-Security-Policy HTTP标头来阻止内联脚本,并只允许来自受信任域的外部脚本。请参阅内容安全策略如何工作?
  • 没有独立的文档结构和逻辑
  • 如果你使用服务器端脚本生成页面,例如你生成一百个链接,每个链接都有相同的内联事件处理程序,你的代码将比事件处理程序只定义一次长得多。这意味着客户端将不得不下载更多的内容,结果你的网站会更慢。

另见

JavaScript倾向于将所有内容混合到对象中,这会使它变得混乱。All into one是JavaScript的方式。

本质上,onClick是一个超文本标记语言属性。相反,addEventListener是DOM对象上的一个方法,表示一个超文本标记语言元素。

在JavaScript对象中,方法仅仅是一个具有函数作为值的属性,并且对它所附加的对象起作用(例如使用this)。

在JavaScript中,DOM表示的超文本标记语言元素将其属性映射到其属性上。

这就是人们感到困惑的地方,因为JavaScript将所有内容融合到一个没有间接层的容器或命名空间中。

在正常的OO布局中(至少合并属性/方法的命名空间),您可能会有以下内容:

domElement.addEventListener // Object(Method)domElement.attributes.onload // Object(Property(Object(Property(String))))

有一些变体,比如它可以使用getter/setter进行onload或HashMap进行属性,但最终它看起来就是这样。JavaScript消除了间接层,期望知道什么是什么。它将domElement和属性合并在一起。

除非兼容性,你应该作为最佳实践使用addEventListener。由于其他答案谈论的是这方面的差异,而不是基本的编程差异,我将放弃它。本质上,在理想的世界中,你真的只应该使用超文本标记语言的on*,但在更理想的世界中,你不应该使用超文本标记语言做类似的事情。

为什么今天它占主导地位?它写得更快,更容易学习,而且往往只是工作。

超文本标记语言中onload的全部意义在于首先允许访问addEventListener方法或功能。通过在JS中使用它,当您可以直接应用它时,您正在经历超文本标记语言。

假设你可以创建自己的属性:

$('[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属性上,因为默认情况下属性都是字符串。

根据MDN,区别如下:

获取事件监听器:

EventTarget.addEventListener()方法添加指定的事件监听器的事件监听器列表的EventListener兼容对象调用它的EventTarget上的指定事件类型。该事件目标可以是文档中的元素、文档本身、窗口或任何其他支持事件的对象(例如XMLHttpRequest)。

onClick:

onClick属性返回单击事件处理程序代码当前元素。使用单击事件触发操作时,还考虑将相同的操作添加到keydown事件中,以允许不使用鼠标或触摸的人使用相同的动作语法element.onclick=函数参考;其中函数参考是函数-通常是在其他地方声明的函数或函数的名称表达式。有关详细信息,请参阅“JavaScript指南:函数”。

在使用中也有语法差异,如您在以下代码中看到的:

添加事件监听器:

// Function to change the content of t2function modifyText() {var t2 = document.getElementById("t2");if (t2.firstChild.nodeValue == "three") {t2.firstChild.nodeValue = "two";} else {t2.firstChild.nodeValue = "three";}}
// add event listener to tablevar el = document.getElementById("outside");el.addEventListener("click", modifyText, false);

onClick:

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!");}

总结:

  1. addEventListener可以添加多个事件,而onclick不能这样做。
  2. onclick可以作为HTML属性添加,而addEventListener只能在<script>元素中添加。
  3. addEventListener可以使用第三个参数来停止事件传播。

两者都可以用来处理事件。然而,addEventListener应该是首选,因为它可以做onclick所做的一切甚至更多。不要使用内联onclick作为超文本标记语言属性,因为这会混淆javascript和超文本标记语言,这是一种不好的做法。它使代码不那么容易维护。

element.onclick=函数() { /* 做事情*/}

element.addEventListener('Click',函数(){ /* 做东西*/},false);

它们显然做了同样的事情:监听点击事件并执行回调函数。然而,它们并不等同。如果您需要在两者之间做出选择,这可以帮助您找出哪一个最适合您。

主要区别在于onClick只是一个属性,和所有对象属性一样,如果你多次写入,它将是覆盖。使用addEventListener()监听器是否必选,我们可以简单地绑定事件处理程序到元素,我们可以在每次需要时调用它,而不用担心任何覆盖的属性。示例如下:

试试看:https://jsfiddle.net/fjets5z4/5/

首先,我很想继续使用onClick,因为它更短,看起来更简单……事实上它是。但我不建议再使用它了。这就像使用内联JavaScript一样。现在非常不鼓励使用类似内联JavaScript的东西(也不鼓励使用内联CSS,但那是另一个话题)。

然而,addEventListener()函数,尽管它是标准的,但在旧浏览器(Internet Explorer低于版本9)中不起作用,这是另一个很大的区别。如果你需要支持这些古老的浏览器,你应该遵循onClick的方式。但是你也可以使用jQuery(或它的替代品之一):它基本上简化了你的工作,减少了浏览器之间的差异,因此可以节省你很多时间。

var clickEvent = document.getElementByID("onclick-eg");var EventListener = document.getElementByID("addEventListener-eg");
clickEvent.onclick = function(){window.alert("1 is not called")}clickEvent.onclick = function(){window.alert("1 is not called, 2 is called")}
EventListener.addEventListener("click",function(){window.alert("1 is called")})EventListener.addEventListener("click",function(){window.alert("2 is also called")})

onClick基本上是一个addEventListener,它在单击元素时专门执行一个函数。所以,当你有一个执行简单操作的按钮时很有用,比如计算器按钮。addEventlistener可用于许多事情,比如在加载DOM或所有内容时执行操作,类似于window.onload但具有更多控制权。

注意,您实际上可以使用多个内联事件,或者至少通过使用onClick将每个函数与分号分开,就像这样……

我不会编写内联函数,因为你以后可能会遇到问题,而且在我看来会很混乱。只需使用它来调用脚本文件中已经完成的函数。

addEventListener用于复杂操作,onClick用于简单操作。我见过一些项目没有将特定的元素附加到元素,而是实现一个更全局的事件监听器,它将确定点击是否在按钮上,并根据按下的内容执行某些任务。我认为这可能会导致问题,虽然很小,但如果事件监听器必须处理每次点击,可能会浪费资源

每个事件类型只能附加一个事件处理程序,但可以附加多个事件监听器


那么,它在行动中看起来如何?

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

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文档

在我的Visual Studio Code中,addEventListener在事件上有真正的智能感知

输入图片描述

但是onClick没有,只有假的

输入图片描述

let element = document.queryselector('id or classname');element.addeventlistiner('click',()=>{do work})

<button onclick="click()">click</click>`function click(){do work};

我想Chris Baker几乎把它总结成了一个很好的答案,但我想补充一点,使用addEventListener(),您还可以使用选项参数,它可以让您更好地控制事件。例如-如果您只想运行一次事件,那么您可以在添加事件时使用{one: true}作为选项参数,以便只调用它一次。

    function greet() {console.log("Hello");}document.querySelector("button").addEventListener('click', greet, { once: true })

上述函数只会打印“Hello”一次。此外,如果你想清理你的事件,那么还有一个选项可以删除EventListener()。虽然使用addEventListener()有很多优点,但如果你的目标受众使用Internet Explorer,那么这个方法可能不适用于所有情况。你也可以在MDN上阅读addEventListener,他们对如何使用它们给出了很好的解释。

您还应该考虑事件委托!出于这个原因,我更喜欢addEventListener,最重要的是要仔细和有意识地使用它!

事实:

  1. EventListeners很重…(客户端的内存分配)
  2. 事件相对于DOM传播IN,然后再次传播OUT树,也被称为滴入和冒泡,读一下如果你不知道

想象一个简单的例子:一个简单的按钮在div里面…如果你点击按钮,一个事件将无论如何滴入按钮,然后再次输出,像这样:

窗口-文档-div-按钮-div-文档-窗口

在浏览器后台(假设JS引擎的软件外围),浏览器只能对点击做出反应,如果它检查在目标位置完成的每次点击。

为了确保每个可能的事件侦听器都被触发,它必须将“单击事件信号”从文档级别一直发送到元素……然后再次返回。然后可以通过使用例如附加EventListeners来使用此行为:

document.getElementById("exampleID").addEventListener("click",(event) => {doThis}, true/false);

请注意,作为addEventListener方法的最后一个参数的true/false根据事件何时被识别来控制行为-当滴入或冒泡时。

TRUE表示,在滴入时识别事件FALSE表示,事件在冒泡的过程中被识别

使用上述方法来处理,实现以下两个有用的概念也变得更加直观:

  1. 您也可以在函数中使用event.stopPropagation()(示例参考“doThis”)以防止进一步传播捕获和冒泡阶段的当前事件。它不,但是,防止任何默认行为发生;例如,点击链接仍在处理中。
  2. 如果你想停止这些行为,你可以使用event.preventDefault()在函数中(示例参考。"doThis")。例如,您可以告诉浏览器,如果事件没有得到显式处理,其默认操作应不会像往常一样被接受。

还请注意这里再次参考:addEventListener方法的最后一个参数(true/false)也控制在哪个阶段(滴入TRUE或冒泡出FALSE)的最终效果。所以……如果你将一个带有标志TRUE的EventListener应用于一个元素,并将其与.停止传播()方法结合起来,事件甚至不会传递到元素的潜在内部子元素

把它包起来:如果你在超文本标记语言中使用onClick变体……我有两个缺点:

  1. 使用addEventListener,您可以将多个onClick事件附加到同一个元素,分别是一个元素,但使用onClick是不可能的(至少到目前为止我坚信这一点,如果我错了,请纠正我)。
  2. 此外,以下方面在这里确实值得注意……尤其是代码维护部分(到目前为止还没有详细说明):

关于事件委派,实际上可以归结为这个。如果一些其他JavaScript代码需要响应单击事件,使用addEventListener确保你们都可以响应它。如果你们都尝试使用onClick,然后一个踩另一个。如果你想在同一个元素上点击。Furthermore, you want to keep your behavior as separate as you can from the HTML in case you need to change it later. It would suck to have 50 HTML files to update instead of one JavaScript file.(归功于Greg Burghardt,addEventListener vs onClick关于事件委托

  • 这也被术语“不显眼的JavaScript”所知…读一读!