Answers:
两种方法都是正确的,但它们本身都不是“最佳”的,并且开发人员选择使用这两种方法可能是有原因的。
事件监听器(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 2级事件),您可以将理论上不受限制的事件数量附加到任何单个元素。唯一实际的限制是客户端内存和其他性能问题,这对于每个浏览器都是不同的。
上面的示例表示使用匿名函数[ doc ]。您还可以使用函数引用[ doc ]或闭包[ doc ] 添加事件侦听器:
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
的另一个重要功能addEventListener
是final参数,该参数控制侦听器对冒泡事件的反应[ doc ]。我在示例中传递了错误,这可能是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 */ };
除了可以更好地控制范围(因为您正在编写脚本而不是HTML),并且可以使用匿名函数,函数引用和/或闭包之外,...等效于内联javascript。
内联事件的主要缺点是,与上述事件侦听器不同,您只能分配一个内联事件。内联事件存储为element [ 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/。
广义上讲,不要使用内联事件。可能有特定的用例,但是如果您不是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
否则使用的部分将不适用于低于9的IE版本。
文档和相关阅读
function addEvent(e,n,f){return e.attachEvent?e.attachEvent('on'+n,f):e.addEventListener(n,f,!!0)}
<<在98个字符处,这个字符要小40%以上!
如果还有其他几个功能,则可以看到的区别:
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
(语法稍有不同)。
在这个答案中,我将描述定义DOM事件处理程序的三种方法。
element.addEventListener()
代码示例:
element.addEventListener()
具有多个优点:
element.removeEventListener()
。useCapture
参数,该参数指示您是要在捕获阶段还是冒泡阶段中处理事件。请参阅:无法理解addEventListener中的useCapture属性。.onevent
DOM元素的属性,因此许多经验不足的JavaScript程序员都认为事件名称是例如onclick
或onload
。on
是不是事件名称的一部分。正确的事件名称是click
和load
,这就是将事件名称传递给的方式.addEventListener()
。element.onevent = function() {}
(例如onclick
,onload
)代码示例:
这是在DOM 0中注册事件处理程序的方法。现在不建议这样做,因为它:
onevent
属性还原回其初始状态(即null
)。window.onload
,例如:window.onload = "test";
,则不会引发任何错误。您的代码将无法正常工作,并且很难找出原因。.addEventListener()
但是,将引发错误(至少在Firefox中): TypeError:EventTarget.addEventListener的参数2不是object。onevent
HTML属性)代码示例:
与相似element.onevent
,现在不建议使用。除了存在的问题外element.onevent
,它:
Content-Security-Policy
HTTP标头以阻止内联脚本,并且仅允许来自受信任域的外部脚本。请参阅内容安全策略如何工作?addEventListener
可以添加多个事件,而onclick
不能这样做。onclick
可以作为HTML
属性添加,而addEventListener
只能在<script>
元素内添加。addEventListener
可以采用第三个参数来停止事件传播。两者都可以用来处理事件。但是,它addEventListener
应该是首选,因为它可以做所有事情onclick
以及更多。不要将内联onclick
用作HTML属性,因为这会将javascript和HTML混合在一起,这是一种不好的做法。它使代码的可维护性降低。
onclick
因为担心被人嘲笑而使用内联处理程序-但近年来,事件通常以更糟糕,更少维护的方式进行绑定。类,如js-link
,js-form-validation
或数据属性与data-jspackage="init"
不以任何方式更好?怎么ofteen你真的使用事件冒泡?我个人希望能够编写一个处理程序,而无需检查目标是否确实与我的元素匹配-或由于随机错误而不得不停止在多个位置进行传播。
有一个细节没有注意到尚:现代桌面浏览器考虑不同的按键操作是“点击”了AddEventListener('click'
,并onclick
在默认情况下。
onclick
并AddEventListener
单击火左边和中间点击。onclick
触发仅在左侧点击,但AddEventListener
点击左,中火和右点击。此外,当涉及滚动游标时,跨浏览器的中键点击行为也非常不一致:
还值得注意的是,任何键盘可选择的HTML元素的“ click”事件,例如input
在选定元素时也会在空间上触发或进入。
Javascript倾向于将所有内容混合到对象中,这可能会造成混淆。JavaScript方式合而为一。
本质上,onclick是HTML属性。相反,addEventListener是DOM对象上表示HTML元素的方法。
在JavaScript对象中,方法仅仅是一个具有作为值的函数的属性,并且该属性与它所附加的对象一起工作(例如,使用此方法)。
在JavaScript中,以DOM表示的HTML元素会将其属性映射到其属性。
这是人们感到困惑的地方,因为JavaScript将所有内容融合到一个没有间接层的单个容器或名称空间中。
在普通的OO布局中(至少会合并属性/方法的命名空间),您可能会遇到以下情况:
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
有一些变化,例如可以使用getter / setter进行onload或使用HashMap进行属性转换,但最终看起来就是这样。JavaScript期望知道什么是其他东西,从而消除了该间接层。它将domElement和属性合并在一起。
除非兼容性,否则最佳做法是使用addEventListener。当其他答案谈论这方面的差异而不是基本的程序差异时,我将放弃它。本质上,在理想的世界中,您实际上只打算在HTML上使用on *,但在更理想的世界中,您不应该在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属性,因为默认情况下,属性都是字符串。
根据MDN,差异如下:
addEventListener:
EventTarget.addEventListener()方法将指定的EventListener兼容对象添加到事件侦听器列表中,以在其调用的EventTarget上指定事件类型。事件目标可以是文档中的Element,文档本身,Window或支持事件的任何其他对象(例如XMLHttpRequest)。
onclick:
onclick属性返回当前元素上的click事件处理程序代码。使用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);
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!");
}
如果您不太担心浏览器的支持,则可以使用一种方法在事件调用的函数中重新绑定“ 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中运行良好,并且可能使“绑定”与其他浏览器兼容。
element.onclick = function(){/ *做东西* /}
element.addEventListener('click',function(){/ *做东西* /},false);
他们显然在做同样的事情:监听click事件并执行回调函数。但是,它们并不等效。如果您需要在两者之间进行选择,这可以帮助您确定哪一个最适合您。
主要区别在于onclick只是一个属性,并且像所有对象属性一样,如果您多次写入,它将被覆盖。使用addEventListener()代替,我们可以简单地将事件处理程序绑定到元素,并且可以在每次需要它时调用它,而不必担心任何被覆盖的属性。这里显示示例,
试试看:https : //jsfiddle.net/fjets5z4/5/
首先,我很想继续使用onclick,因为它更短,看起来更简单……实际上是这样。但我不建议再使用它。就像使用内联JavaScript。如今,强烈建议不要使用内联JavaScript之类的东西(内联CSS也不鼓励,但这是另一个主题)。
但是,尽管是标准功能,但addEventListener()函数在旧的浏览器(版本9以下的Internet Explorer)中不起作用,这是另一个很大的不同。如果需要支持这些古老的浏览器,则应遵循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("2 is not called")
}
EventListener.addEventListener("click",function(){
window.alert("1 is called")
})
EventListener.addEventListener("click",function(){
window.alert("2 is also called")
})
'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的不一致之处。顺便说一句,如果您使用的是jQuery($('#btnSubmit').on('click', disable);
),它将使用显式绑定。
btnSubmit.onclick = disable;
(分配函数,而不是调用它)。然后在两种情况下this
都将引用button元素。
onclick本质上是一个addEventListener,它在单击元素时专门执行一个功能。因此,当您具有执行简单操作的按钮(例如计算器按钮)时很有用。addEventlistener可用于多种操作,例如在加载DOM或所有内容时执行操作,类似于window.onload,但具有更多控制权。
注意,实际上,您可以内联使用多个事件,或者至少通过使用分号(用分号分隔每个函数)来使用onclick,如下所示。
我不会用内联编写函数,因为您以后可能会遇到问题,而且imo会很乱。只需使用它来调用脚本文件中已完成的功能即可。
我想您使用哪一种取决于您的需求。addEventListener用于复杂操作,onclick用于简单操作。我已经看到一些项目没有将特定项目附加到元素上,而是实现了一个更具全局性的事件侦听器,该事件侦听器将确定是否在按钮上轻按,并根据所按的内容执行某些任务。Imo可能会引发我认为的问题,而且如果事件侦听器必须处理每一次点击,虽然可能很小,但可能会浪费资源
function addEvent(element, myEvent, fnc) { return ((element.attachEvent) ? element.attachEvent('on' + myEvent, fnc) : element.addEventListener(myEvent, fnc, false)); }