关于javascript:addEventListener vs onclick

addEventListener vs onclick

addEventListeneronclick之间有什么区别?

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

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


两者都是正确的,但它们本身都不是"最佳",开发人员可能选择使用这两种方法。

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

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

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

在大多数其他浏览器(包括IE 9及更高版本)中,您使用addEventListener [doc],如下所示:

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

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

上面的示例表示使用匿名函数[doc]。您还可以使用函数引用[doc]或闭包[doc]添加事件侦听器:

1
2
3
4
var myFunctionReference = function() { /* do stuff here*/ }

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

addEventListener的另一个重要特性是最终参数,它控制侦听器对冒泡事件[doc]的反应。我在示例中传递了错误,这可能是95%的用例的标准。 attachEvent或使用内联事件时没有等效参数。

内联事件(HTML onclick ="property and element.onclick)

在所有支持javascript的浏览器中,您可以将事件侦听器内联,即HTML代码中的内容。你可能已经看到了这个:

1
Click me

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

你提到的另一种方法:

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

...相当于内联javascript,除了你有更多的范围控制(因为你正在编写脚本而不是HTML),并且可以使用匿名函数,函数引用和/或闭包。

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

使用上面HTML中的示例

1
2
3
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模板中看到这样的代码:

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

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

哪个最好?

问题在于浏览器的兼容性和必要性。您当前是否需要将多个事件附加到元素?你将来会吗?赔率是,你会的。 attachEvent和addEventListener是必要的。如果没有,内联事件可能看起来像他们会做的那样,但是你为未来做好准备,尽管看起来似乎不太可能,至少可以预测。有可能你必须转移到基于JS的事件监听器,所以你也可以从那里开始。不要使用内联事件。

jQuery和其他javascript框架在通用模型中封装了DOM 2级事件的不同浏览器实现,因此您可以编写跨浏览器兼容的代码,而无需担心IE作为反叛者的历史。与jQuery相同的代码,所有跨浏览器并准备好摇滚:

1
$(element).on('click', function () { /* do stuff */ });

但是,不要为了这一件事而耗尽并获得一个框架。您可以轻松地滚动自己的小实用程序来处理旧版浏览器:

1
2
3
4
5
6
7
8
9
10
11
12
13
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规范,元素事件处理程序属性
  • MDN上的element.addEventListener
  • MSDN上的element.attachEvent
  • Jquery.on
  • quirksmode博客"活动介绍"
  • 谷歌的CDN托管的JavaScript库
  • 好。


    如果您有另外两个功能,您可以看到差异:

    1
    2
    3
    4
    5
    6
    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()

    代码示例:

    1
    2
    const element = document.querySelector('a');
    element.addEventListener('click', event => event.preventDefault(), true);
    1
    Try clicking this link.

    element.addEventListener()有多个优点:

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

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

    代码示例:

    1
    2
    const element = document.querySelector('a');
    element.onclick = event => event.preventDefault();
    1
    Try clicking this link.

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

    • 允许您只注册一个事件处理程序。删除指定的处理程序也不直观,因为要删除使用此方法分配的事件处理程序,必须将onevent属性恢复为其初始状态(即null)。
    • 不适当地回应错误。例如,如果您错误地将字符串分配给window.onload,例如:window.onload ="test";,则不会抛出任何错误。你的代码不起作用,很难找到原因。 .addEventListener()然而,会抛出错误(至少在Firefox中):TypeError:EventTarget.addEventListener的参数2不是对象。
    • 如果要在捕获或冒泡阶段处理事件,则不提供选择方法。

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

    代码示例:

    1
    Try clicking this link.

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

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

    也可以看看

    • EventTarget.addEventListener()文档(MDN)
    • EventTarget.removeEventListener()文档(MDN)
    • onclick vs addEventListener
    • dom-events标签维基

    虽然onclick适用于所有浏览器,但addEventListener在旧版本的Internet Explorer中不起作用,而Internet Explorer使用的是attachEvent

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


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

    但是,您不能将load event分配给元素或诸如此类的东西。


    摘要:

  • addEventListener可以添加多个事件,而使用onclick则无法完成。
  • onclick可以添加为HTML属性,而addEventListener只能添加到元素中。
  • addEventListener可以采用可以停止事件传播的第三个参数。
  • 两者都可用于处理事件。但是,addEventListener应该是首选,因为它可以完成onclick所做的所有事情。不要使用内联onclick作为HTML属性,因为这会混淆javascript和HTML,这是一种不好的做法。它使代码不易维护。


    根据MDN,差异如下:

    的addEventListener:

    The EventTarget.addEventListener() method adds the specified
    EventListener-compatible object to the list of event listeners for the
    specified event type on the EventTarget on which it's called. The
    event target may be an Element in a document, the Document itself, a
    Window, or any other object that supports events (such as
    XMLHttpRequest).

    的onclick:

    The onclick property returns the click event handler code on the
    current element. When using the click event to trigger an action, also
    consider adding this same action to the keydown event, to allow the
    use of that same action by people who don't use a mouse or a touch
    screen. Syntax element.onclick = functionRef; where functionRef is a
    function - often a name of a function declared elsewhere or a function
    expression. See"JavaScript Guide:Functions" for details.

    如下面的代码所示,使用中还存在语法差异:
    的addEventListener:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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!");
    }

    也应该可以通过原型化来扩展监听器(如果我们有一个对它的引用而不是一个匿名函数) - 或者让'onclick'调用一个函数库(一个调用其他函数的函数)的调用

    喜欢

    1
    2
    3
    4
    5
        elm.onclick = myFunctionList
        function myFunctionList(){
          myFunc1();
          myFunc2();
        }

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

    以防有人在将来找到这个帖子...


    尚未注意到一个细节:现代桌面浏览器默认情况下将不同的按钮按下以"点击"AddEventListener('click'onclick

    • 在Chrome 42和IE11上,左键和中键点击onclickaddEventListener点击。
    • 在Firefox 38上,onclick仅在左键单击时触发,但addEventListener单击会在左键,中键和右键单击时触发。

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

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

    值得注意的是,任何键盘可选HTML元素(如input)的"单击"事件也会在空格上触发,或者在选择元素时输入。


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


    如果您不太担心浏览器支持,有一种方法可以在事件调用的函数中重新绑定"this"引用。它通常指向执行函数时生成事件的元素,这并不总是您想要的。棘手的部分是同时能够删除相同的事件监听器,如下例所示:http://jsfiddle.net/roenbaeck/vBYu3/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /*
        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中运行良好,并且可能有一些小窍门使"绑定"与其他浏览器兼容。


    Javascript倾向于将所有内容混合到对象中,这可能会让人感到困惑。一切都是JavaScript方式。

    基本上onclick是一个HTML属性。相反,addEventListener是DOM对象上表示HTML元素的方法。

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

    在JavaScript中,由DOM表示的HTML元素将其属性映射到其属性。

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

    在普通的OO布局(至少合并属性/方法的命名空间)中,您可能会有类似的东西:

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

    有些变体可能会使用getter / setter进行onload或HashMap进行属性,但最终会看起来如何。 JavaScript消除了那个间接层,期望知道什么是其他东西。它将domElement和属性合并在一起。

    除了兼容性,您应该使用addEventListener作为最佳实践。由于其他答案谈到了这方面的差异而不是基本的程序差异,我将放弃它。从本质上讲,在一个理想的世界中,你真的只是想在HTML中使用*,但在一个更理想的世界里,你不应该从HTML那样做。

    为什么它今天占主导地位?编写起来更快,更容易学习并且更容易工作。

    HTML中onload的重点是首先提供对addEventListener方法或功能的访问。通过在JS中使用它,当您可以直接应用它时,您将通过HTML。

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

    1
    2
    3
    4
    5
    $('[myclick]').each(function(i, v) {
         v.addEventListener('click', function() {
             eval(v.myclick); // eval($(v).attr('myclick'));
         });
    });

    JS所做的与这有点不同。

    你可以把它等同于(对于每个创建的元素):

    1
    2
    3
    4
    5
    6
    element.addEventListener('click', function() {
        switch(typeof element.onclick) {
              case 'string':eval(element.onclick);break;
              case 'function':element.onclick();break;
         }
    });

    实际的实现细节可能会因一系列微妙的变化而有所不同,使得两者在某些情况下略有不同,但这是它的要点。

    可以说是一个兼容性黑客,你可以将一个函数固定到一个on属性,因为默认属性都是字符串。


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

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


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

    看下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
       

    </head>
    <body>
        <input id="btnSubmit" type="button" value="Submit" />
       
            function disable() {
                this.disabled = true;
            }
            var btnSubmit = document.getElementById('btnSubmit');
            btnSubmit.onclick = disable();
            //btnSubmit.addEventListener('click', disable, false);
       
    </body>
    </html>

    它的作用非常简单。单击该按钮时,该按钮将自动禁用。

    首先,当您尝试以这种方式连接事件时button.onclick = function(),
    单击按钮将触发onclick事件,但是,按钮不会被禁用,因为button.onclick和onclick事件处理程序之间没有明确的绑定。如果你调试看到'this'对象,你可以看到它引用'window'对象。

    其次,如果您评论btnSubmit.onclick = disable();并取消注释
    你可以看到该按钮被禁用,因为这样在button.onclick事件和onclick事件处理程序之间有明确的绑定。如果调试为禁用功能,则可以看到'this'指的是button control而不是window

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