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.attachEvent('onclick', function() { /* do stuff here*/ });
在大多数其他浏览器(包括 IE 9 及更高版本)中,您使用 addEventListener
[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]。在示例中,我一直在传递 false,这对于大约 95% 的用例来说是标准的。 attachEvent
或使用内联事件时没有等效参数。
内联事件(HTML onclick="" 属性和 element.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 框架最佳实践涉及在模板中编写此类代码,请不要觉得您在使用内联事件——您不是。
哪个是最好的?
问题是浏览器兼容性和必要性的问题。您是否需要将多个事件附加到一个元素?将来你会吗?很有可能,你会的。 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);
else
return element.addEventListener(evnt, funct, false);
}
// example
addEvent(
document.getElementById('myElement'),
'click',
function () { alert('hi!'); }
);
试试看:http://jsfiddle.net/bmArj/
考虑到所有这些,除非您正在查看的脚本以其他方式考虑了浏览器差异(在您的问题中未显示的代码中),否则使用 addEventListener
的部分在 IE 版本小于 9 中不起作用。
文档和相关阅读
W3 HTML 规范,元素事件处理程序属性
MDN 上的 element.addEventListener
MSDN 上的 element.attachEvent
jQuery.on
quirksmode 博客“事件简介”
Google 上 CDN 托管的 javascript 库
如果您有另外几个功能,您会看到不同之处:
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 中无效。您可以使用类似的 attachEvent
(略 IE <9 中的不同语法)。
element.onclick = myFunction
中执行此操作,则在不显示 HTML 时不会清理 THAT,实际上,您可以将事件附加到从未添加到 DOM 的元素(因此它们是页面的“一部分”) .在许多情况下,如果你附加一个这样的事件,它可以留下一个开放的引用,所以它不会被 GC 清理。
addEventListener()
添加到 <html>
元素,然后仅检查 event.target
属性以监听点击具体要素。这样我就不必担心流氓事件监听器会污染内存堆。它曾经是内联的(在 HTML 中定义),即使它与元素一起被删除,它仍然占用内存空间......对吗?
在这个答案中,我将描述定义 DOM 事件处理程序的三种方法。
element.addEventListener()
代码示例:
常量元素 = document.querySelector('a'); element.addEventListener('click', event => event.preventDefault(), true); 尝试点击此链接。
element.addEventListener()
具有多种优势:
允许您注册无限的事件处理程序并使用 element.removeEventListener() 删除它们。
具有 useCapture 参数,指示您是否希望在其捕获或冒泡阶段处理事件。请参阅:无法理解 addEventListener 中的 useCapture 属性。
关心语义。基本上,它使注册事件处理程序更加明确。对于初学者来说,函数调用很明显会发生某些事情,而将事件分配给 DOM 元素的某些属性至少不直观。
允许您分离文档结构 (HTML) 和逻辑 (JavaScript)。在小型 Web 应用程序中,这似乎无关紧要,但对于任何更大的项目来说,它确实很重要。维护一个分离结构和逻辑的项目比维护一个不分离的项目容易得多。
消除与正确事件名称的混淆。由于使用内联事件侦听器或将事件侦听器分配给 DOM 元素的 .onevent 属性,许多没有经验的 JavaScript 程序员认为事件名称是例如 onclick 或 onload。 on 不是事件名称的一部分。正确的事件名称是单击和加载,这就是将事件名称传递给 .addEventListener() 的方式。
适用于几乎所有浏览器。如果你仍然需要支持 IE <= 8,你可以使用 MDN 的 polyfill。
element.onevent = function() {} (例如 onclick, onload)
代码示例:
常量元素 = document.querySelector('a'); element.onclick = event => event.preventDefault(); 尝试点击此链接。
这是一种在 DOM 0 中注册事件处理程序的方法。现在不鼓励这样做,因为它:
允许您仅注册一个事件处理程序。删除分配的处理程序也不直观,因为要删除使用此方法分配的事件处理程序,您必须将 onevent 属性恢复为其初始状态(即 null)。
不适当地响应错误。例如,如果您错误地为 window.onload 分配了一个字符串,例如:window.onload = "test";,它不会抛出任何错误。您的代码不起作用,而且很难找出原因。 .addEventListener() 然而,会抛出错误(至少在 Firefox 中): TypeError: Argument 2 of EventTarget.addEventListener is not an object。
不提供选择是否要在其捕获或冒泡阶段处理事件的方法。
内联事件处理程序(onevent HTML 属性)
代码示例:
与 element.onevent
类似,现在不鼓励使用它。除了 element.onevent
的问题之外,它还:
是一个潜在的安全问题,因为它使 XSS 更加有害。现在的网站应该发送适当的 Content-Security-Policy HTTP 标头来阻止内联脚本并只允许来自受信任域的外部脚本。请参阅内容安全策略如何工作?
不分离文档结构和逻辑。
如果您使用服务器端脚本生成页面,例如您生成了一百个链接,每个链接都具有相同的内联事件处理程序,那么您的代码将比事件处理程序只定义一次时长得多。这意味着客户端必须下载更多内容,结果您的网站会变慢。
也可以看看
EventTarget.addEventListener() 文档 (MDN)
EventTarget.removeEventListener() 文档 (MDN)
onclick vs addEventListener
dom-events 标签维基
虽然 onclick
适用于所有浏览器,但 addEventListener
不适用于旧版本的 Internet Explorer,它使用 attachEvent
。
onclick
的缺点是只能有一个事件处理程序,而另外两个将触发所有已注册的回调。
概括:
addEventListener 可以添加多个事件,而使用 onclick 则无法做到这一点。 onclick 可以作为 HTML 属性添加,而 addEventListener 只能在
function addEvent(element, myEvent, fnc) { return ((element.attachEvent) ? element.attachEvent('on' + myEvent, fnc) : element.addEventListener(myEvent, fnc, false)); }
function addEvent(e,n,f){return e.attachEvent?e.attachEvent('on'+n,f):e.addEventListener(n,f,!!0)}
<<在 98 个字符中,这个字符小了 40% 以上!