Categories
addeventlistener javascript onclick

addEventListener vs onclick

916

What’s the difference between addEventListener and onclick?

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

The code above resides together in a separate .js file, and they both work perfectly.

0

    1177

    Both are correct, but none of them are “best” per se, and there may be a reason the developer chose to use both approaches.

    Event Listeners (addEventListener and IE’s attachEvent)

    Earlier versions of Internet Explorer implement JavaScript differently from pretty much every other browser. With versions less than 9, you use the attachEvent[doc] method, like this:

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

    In most other browsers (including IE 9 and above), you use addEventListener[doc], like this:

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

    Using this approach (DOM Level 2 events), you can attach a theoretically unlimited number of events to any single element. The only practical limitation is client-side memory and other performance concerns, which are different for each browser.

    The examples above represent using an anonymous function[doc]. You can also add an event listener using a function reference[doc] or a closure[doc]:

    var myFunctionReference = function() { /* do stuff here*/ }
    
    element.attachEvent('onclick', myFunctionReference);
    element.addEventListener('click', myFunctionReference , false);
    

    Another important feature of addEventListener is the final parameter, which controls how the listener reacts to bubbling events[doc]. I’ve been passing false in the examples, which is standard for probably 95% of use cases. There is no equivalent argument for attachEvent, or when using inline events.

    Inline events (HTML onclick=”” property and element.onclick)

    In all browsers that support javascript, you can put an event listener inline, meaning right in the HTML code. You’ve probably seen this:

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

    Most experienced developers shun this method, but it does get the job done; it is simple and direct. You may not use closures or anonymous functions here (though the handler itself is an anonymous function of sorts), and your control of scope is limited.

    The other method you mention:

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

    … is the equivalent of inline javascript except that you have more control of the scope (since you’re writing a script rather than HTML) and can use anonymous functions, function references, and/or closures.

    The significant drawback with inline events is that unlike event listeners described above, you may only have one inline event assigned. Inline events are stored as an attribute/property of the element[doc], meaning that it can be overwritten.

    Using the example <a> from the HTML above:

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

    … when you clicked the element, you’d only see “Did stuff #2” – you overwrote the first assigned of the onclick property with the second value, and you overwrote the original inline HTML onclick property too. Check it out here: http://jsfiddle.net/jpgah/.

    Broadly speaking, do not use inline events. There may be specific use cases for it, but if you are not 100% sure you have that use case, then you do not and should not use inline events.

    Modern Javascript (Angular and the like)

    Since this answer was originally posted, javascript frameworks like Angular have become far more popular. You will see code like this in an Angular template:

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

    This looks like an inline event, but it isn’t. This type of template will be transpiled into more complex code which uses event listeners behind the scenes. Everything I’ve written about events here still applies, but you are removed from the nitty gritty by at least one layer. You should understand the nuts and bolts, but if your modern JS framework best practices involve writing this kind of code in a template, don’t feel like you’re using an inline event — you aren’t.

    Which is Best?

    The question is a matter of browser compatibility and necessity. Do you need to attach more than one event to an element? Will you in the future? Odds are, you will. attachEvent and addEventListener are necessary. If not, an inline event may seem like they’d do the trick, but you’re much better served preparing for a future that, though it may seem unlikely, is predictable at least. There is a chance you’ll have to move to JS-based event listeners, so you may as well just start there. Don’t use inline events.

    jQuery and other javascript frameworks encapsulate the different browser implementations of DOM level 2 events in generic models so you can write cross-browser compliant code without having to worry about IE’s history as a rebel. Same code with jQuery, all cross-browser and ready to rock:

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

    Don’t run out and get a framework just for this one thing, though. You can easily roll your own little utility to take care of the older browsers:

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

    Try it: http://jsfiddle.net/bmArj/

    Taking all of that into consideration, unless the script you’re looking at took the browser differences into account some other way (in code not shown in your question), the part using addEventListener would not work in IE versions less than 9.

    Documentation and Related Reading

    19

    • 15

      sorry to bump but just wanted to give a condensed version of your function (fiddle: jsfiddle.net/bmArj/153) – function addEvent(element, myEvent, fnc) { return ((element.attachEvent) ? element.attachEvent('on' + myEvent, fnc) : element.addEventListener(myEvent, fnc, false)); }

      – Deryck

      Nov 5, 2013 at 0:25


    • 10

      @Gaurav_soni No. The name of the function and all the code it contains are already exposed in the javascript file, which is in plaintext. Anyone can open a web console and execute or manipulate any javascript. If your javascript contains anything that could be a security risk if it is exposed to the public, then you’ve got a major problem because it is already exposed to the public.

      Sep 18, 2015 at 5:21

    • 4

      As long as we’re condensing this algorithm, we might as well go all the way: function addEvent(e,n,f){return e.attachEvent?e.attachEvent('on'+n,f):e.addEventListener(n,f,!!0)} << At 98 characters, this one is more than 40% smaller!

      – Trevor

      Dec 2, 2016 at 23:58

    • 5

      @Trevor Out of curiosity, why !!0? Why not !1 or just 0?

      Dec 3, 2016 at 4:32


    • 4

      @AdrianMoisa This answer was written at a time when AngularJS was a new thing on the rise, and the common practice was still “progressive enhancement” — that is, writing an HTML document in a way that would work with or without javascript. In that perspective, binding events from javascript would be best practice. Nowadays, I don’t think many people worry too much about progressive enhancement, especially not considering the prevalence of stuff like Angular. There’s still some separation of concerns arguments about inline events (not using Angular), but that’s more style than substance.

      Oct 23, 2017 at 19:13

    273

    The difference you could see if you had another couple of functions:

    var h = document.getElementById('a');
    h.onclick = doThing_1;
    h.onclick = doThing_2;
    
    h.addEventListener('click', doThing_3);
    h.addEventListener('click', doThing_4);
    

    Functions 2, 3 and 4 work, but 1 does not. This is because addEventListener does not overwrite existing event handlers, whereas onclick overrides any existing onclick = fn event handlers.

    The other significant difference, of course, is that onclick will always work, whereas addEventListener does not work in Internet Explorer before version 9. You can use the analogous attachEvent (which has slightly different syntax) in IE <9.

    13

    • 27

      That’s a very clear explanation! Right to the point. So if I need multiple functions for one event, I am stuck with addEventListener, and I have to write more code for attachEvent just to accomodate IE.

      Jun 14, 2011 at 18:58

    • 6

      2, 3, and 4 should be named dosomething. 1 gets overridden by 2 and is never called.

      Jan 7, 2013 at 22:12

    • 1

      @Ludolfyn I want to be clear on that — if the inline event is defined in the HTML, it is cleaned up when the HTML leaves the browser view, in most cases. If you are doing it in code element.onclick = myFunction, THAT will not be cleaned up when the HTML is not displayed, in fact, you can attach events to elements that are never added to DOM (so they are “part” of the page). In many instances, if you attach an event like that it can leave an open reference, so it won’t be cleaned up by GC.

      Nov 24, 2020 at 1:35

    • 1

      Events added with addEventListener can, in some circumstances, also need to be cleaned up.

      Nov 24, 2020 at 1:36

    • 1

      Thanks so much @ChrisBaker! I’m still working on the app where this is relevant, so this is very helpful. I’m dynamically generating and removing elements in and out of the DOM, so I’ve chosen to followed React’s recipe in adding one addEventListener() to the <html> element and then just checking the event.target property to listen for clicks on specific elements. This way I don’t have to worry about polluting the memory heap with rogue event listeners. It used to be inline (defined in the HTML) and even though it gets removed with the element, it still took up space in memory… right?

      – Ludolfyn

      Nov 24, 2020 at 8:55


    79

    In this answer I will describe the three methods of defining DOM event handlers.

    element.addEventListener()

    Code example:

    const element = document.querySelector('a');
    element.addEventListener('click', event => event.preventDefault(), true);
    <a href="https://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="https://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="https://google.com" onclick="event.preventDefault();">Try clicking this link.</a>

    Similarly to element.onevent, it’s now discouraged. Besides the issues that element.onevent has, it:

    • Is a potential security issue, because it makes XSS much more harmful. Nowadays websites should send proper Content-Security-Policy HTTP header to block inline scripts and allow external scripts only from trusted domains. See How does Content Security Policy work?
    • Doesn’t separate document structure and logic.
    • If you generate your page with a server-side script, and for example you generate a hundred links, each with the same inline event handler, your code would be much longer than if the event handler was defined only once. That means the client would have to download more content, and in result your website would be slower.

    See also

    0