addEventListener与onclick


704

addEventListener和之间有什么区别onclick

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

上面的代码一起驻留在单独的.js文件中,并且它们都可以正常工作。

Answers:


955

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

事件监听器(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版本。

文档和相关阅读


14
很抱歉碰到,但只想给出函数的精简版本(提琴:jsfiddle.net/bmArj/153function addEvent(element, myEvent, fnc) { return ((element.attachEvent) ? element.attachEvent('on' + myEvent, fnc) : element.addEventListener(myEvent, fnc, false)); }
Deryck

10
@Gaurav_soni否。该函数的名称及其包含的所有代码已经在纯文本的javascript文件中公开。任何人都可以打开Web控制台并执行或操纵任何JavaScript。如果您的JavaScript包含任何可能公开的安全隐患,那么您就遇到了一个主要问题,因为它已经公开了。
克里斯·贝克

4
只要我们精简这种算法,我们就可以一直进行下去:function addEvent(e,n,f){return e.attachEvent?e.attachEvent('on'+n,f):e.addEventListener(n,f,!!0)}<<在98个字符处,这个字符要小40%以上!
Trevor

3
@Trevor出于好奇,为什么!! 0?为什么不!1或仅0?
克里斯·贝克

2
@AdrianMoisa这个答案是在AngularJS兴起的时候写的,而通常的做法仍然是“渐进式增强”-也就是说,以一种可以使用或不使用javascript的方式编写HTML文档。从这个角度来看,从javascript绑定事件将是最佳实践。如今,我认为没有太多人担心渐进式增强,尤其是不考虑Angular之类的东西的普遍性。关于内联事件(不使用Angular),仍然存在一些关注点分离的论点,但是这比实质更重要。
克里斯·贝克

193

如果还有其他几个功能,则可以看到的区别:

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(语法稍有不同)。


18
这是一个非常清楚的解释!正确点。因此,如果我需要为一个事件使用多个功能,那么我会陷入addEventListener的困境,而且我不得不为attachEvent编写更多代码以适应IE。
William Sham

4
2、3和4应该命名为dosomething。1被2覆盖并且从不被调用。
DragonLord

确实,这是一个非常清楚和切题的解释。确实,将函数命名为“ doThing_1”等确实更加有意义。(如果您仍然想满足IE <9,请参见Chris的回答。)
Frank Conijn

61

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

element.addEventListener()

代码示例:

element.addEventListener() 具有多个优点:

  • 允许您注册无限的事件处理程序并使用删除它们element.removeEventListener()
  • 具有useCapture参数,该参数指示您是要在捕获阶段还是冒泡阶段中处理事件。请参阅:无法理解addEventListener中的useCapture属性
  • 关心语义。基本上,它使注册事件处理程序更加明确。对于初学者来说,函数调用可以使事情变得显而易见,而将事件分配给DOM元素的某些属性至少是不直观的。
  • 允许您分离文档结构(HTML)和逻辑(JavaScript)。在小型Web应用程序中,这似乎无关紧要,但是对于任何更大的项目而言,它至关重要。维护一个将结构和逻辑分开的项目要比不保留一个项目的方法容易得多。
  • 消除混淆与正确的事件名称。由于使用内联事件侦听器或将事件侦听器分配给.oneventDOM元素的属性,因此许多经验不足的JavaScript程序员都认为事件名称是例如onclickonloadon不是事件名称的一部分。正确的事件名称是clickload,这就是将事件名称传递给的方式.addEventListener()
  • 几乎所有浏览器均可使用。如果仍然必须支持IE <= 8,则可以使用MDN中的polyfill

element.onevent = function() {}(例如onclickonload

代码示例:

这是在DOM 0中注册事件处理程序的方法。现在不建议这样做,因为它:

  • 允许您注册一个事件处理程序。同样,删除分配的处理程序也不是直观的,因为要删除使用此方法分配的事件处理程序,您必须将onevent属性还原回其初始状态(即null)。
  • 对错误的响应不正确。例如,如果您错误地将字符串分配给window.onload,例如:window.onload = "test";,则不会引发任何错误。您的代码将无法正常工作,并且很难找出原因。.addEventListener()但是,将引发错误(至少在Firefox中): TypeError:EventTarget.addEventListener的参数2不是object
  • 没有提供选择要在捕获或冒泡阶段处理事件的方法。

内联事件处理程序(oneventHTML属性)

代码示例:

与相似element.onevent,现在不建议使用。除了存在的问题外element.onevent,它:

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

也可以看看


22

尽管onclick在所有浏览器中都可以使用,addEventListener但在旧版本的Internet Explorer中却不能使用,而使用它attachEvent

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


12

据我所知,DOM“加载”事件仍然只在非常有限的范围内起作用。这意味着它只会火了window objectimages<script>例如元素。直接onload分配也是如此。两者之间没有技术上的区别。可能.onload =具有更好的跨浏览器可用性。

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


9

摘要:

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

两者都可以用来处理事件。但是,它addEventListener应该是首选,因为它可以做所有事情onclick 以及更多。不要将内联onclick用作HTML属性,因为这会将javascript和HTML混合在一起,这是一种不好的做法。它使代码的可维护性降低。


您主要如何完成目标定位?我的意思是,我个人不会onclick因为担心被人嘲笑而使用内联处理程序-但近年来,事件通常以更糟糕,更少维护的方式进行绑定。类,如js-linkjs-form-validation或数据属性与data-jspackage="init"不以任何方式更好?怎么ofteen你真的使用事件冒泡?我个人希望能够编写一个处理程序,而无需检查目标是否确实与我的元素匹配-或由于随机错误而不得不停止在多个位置进行传播。
Christoffer Bubach '19

3

有一个细节没有注意到尚:现代桌面浏览器考虑不同的按键操作是“点击”了AddEventListener('click',并onclick在默认情况下。

  • 在Chrome 42和IE11,双方onclickAddEventListener单击火左边和中间点击。
  • 在Firefox 38,onclick触发在左侧点击,但AddEventListener点击左,中火右点击。

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

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

还值得注意的是,任何键盘可选择的HTML元素的“ click”事件,例如input在选定元素时也会在空间上触发或进入。


2

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


2

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

1

如果您不太担心浏览器的支持,则可以使用一种方法在事件调用的函数中重新绑定“ 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中运行良好,并且可能使“绑定”与其他浏览器兼容。


1

使用内联处理程序与内容安全策略不兼容,因此addEventListener从该角度来看,此方法更加安全。当然,您可以使用启用内联处理程序,unsafe-inline但是,顾名思义,这样做并不安全,因为它会带回CSP阻止的所有JavaScript开发。


1
注意:此安全限制仅适用于扩展程序开发,链接文档中提供的安全性说明仅适用于浏览器扩展程序。但是,链接文档中提出的这一点通常也适用于Web开发,这是将内容与行为分开。这是全面的好习惯。
克里斯·贝克

1

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

喜欢

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

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

以防万一将来有人找到这个线程...


1

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

0

addEventListener 可让您设置多个处理程序,但IE8或更低版本不支持。

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


0

'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);),它将使用显式绑定。


8
您需要编写btnSubmit.onclick = disable;(分配函数,而不是调用它)。然后在两种情况下this都将引用button元素。
帕夏

-1

onclick本质上是一个addEventListener,它在单击元素时专门执行一个功能。因此,当您具有执行简单操作的按钮(例如计算器按钮)时很有用。addEventlistener可用于多种操作,例如在加载DOM或所有内容时执行操作,类似于window.onload,但具有更多控制权。

注意,实际上,您可以内联使用多个事件,或者至少通过使用分号(用分号分隔每个函数)来使用onclick,如下所示。

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

我想您使用哪一种取决于您的需求。addEventListener用于复杂操作,onclick用于简单操作。我已经看到一些项目没有将特定项目附加到元素上,而是实现了一个更具全局性的事件侦听器,该事件侦听器将确定是否在按钮上轻按,并根据所按的内容执行某些任务。Imo可能会引发我认为的问题,而且如果事件侦听器必须处理每一次点击,虽然可能很小,但可能会浪费资源

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.