addEventListener vs onclick
Asked Answered
D

21

988

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.

Despotism answered 14/6, 2011 at 18:49 Comment(0)
I
1245

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]:

const 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 avoid 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:

const 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 JavaScript 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 JavaScript-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

Illbehaved answered 14/6, 2011 at 18:59 Comment(18)
Thanks. I checked yours as an answer and for future reference. Thanks for mentioning jquery, 'coz that's the SOLUTION to my problemDespotism
there's an older script for event-handling that handles the difference between older versions of IE and other browsers: dean.edwards.name/weblog/2005/10/add-event2Diatom
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)); }Leith
That's a good minified version to use in production code, thanks. I posted a long form for clarity of reading, but yeah, ideally you'd want to use a trim version such as the one you posted. Thanks again :)Illbehaved
@ChrisBaker question: when using <button onClick="doSomething()"> we are exposing the name of the function in the html. Is there some concern of security in this case ?Bisset
@Bisset 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.Illbehaved
@Bisset it used to be the case that in Firefox you cannot easily debug or detect functions attached with addEventListener, and therefore could "hide" functions there (for DRM for example) because they don't show up in javascript representation of object properties, but since v33 there are some changes related to that. Still more complicated than in Chrome though (F12 -> Elements -> Event listeners).Cordero
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!Nautch
@Nautch Out of curiosity, why !!0? Why not !1 or just 0?Illbehaved
@ChrisBaker Good point. I'm use to using ! and !! because of also using the === and !== operators, so that's a force of habit; but you're right, I could have at least done !1, and in this case, 0 would be just fine. Two more characters!Nautch
I would rather use inlline onclick similar to how angular does (click)="fooBar()". This makes it far more easy to quickly asses what code is executed (declarative programming). I enjoy using inline event handlers in web components the same way I use them in React or Angular. It improves so much the workflow. The only drawback is that I cannot call them from my local scope of the web component. I have to namespace them in global objects (Yuk!). How to bind this to inline event handlerMonopteros
@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.Illbehaved
I know the last edit is from 2013, but the far worst suggestion is to use HTML inline JS events. And you don't need to be an expert to realize that when an element starts to gain multiple listeners the markup become a mess, your eyes start to flow over unreadable HTML spaghetti of classes, listeners and "w3schools suggestions" - instead of simply place JS where is meant to be. SOC. Not to talk about the great ability to off listeners and other cool stuff. developer.mozilla.org/en-US/docs/Learn/JavaScript/…Lasandralasater
I disagree, inline event handler are not only easier to spot and maintain - the fact that you do not ever get any event bubbling issues is just another thing which makes is easier to work with. Not very hard to actually check for an existing handler and just wrap/decorate it if you really need more than one for same element and same event. Now in the enlightened era where it's more or less forbidden - you have stuff like classes named js-click or attributes named data-whatever="draggable" and so on. Thats even worse in my opinion, and event bubbling has only ever caused me grief....Meimeibers
I do like Chris Baker's @ChrisBaker explanation here. However perhaps he could elaborate a bit more on the Angular stuff. Even if Angular and other frameworks turn what appear to be inline events into addeventlisteners, that isn't the way it's presented to the coder. And if Mozilla (as Roko says above) councils against using inline events becuz they are hard to maintain (even in small files) wouldn't this also suggest that they shouldn't be used in Angular/Vue? It's not solely about what's happening behind the scenes. It's about how it's presented to the coder...Schnurr
Another point... using onclick is more likely to cause a memory leak. If you use addEventListener then, when the node is removed from the DOM, the handler function is automatically removed from the node. But if using onclick the handler is not removed from the node. You can verify by looking at the elem.onclick in the removed nodes array of the MutationObserverLienlienhard
There's also a weird thing with onclick. The link <a href="valid-url" onclick="return false">...</a> will not work with this onclick event, but I haven't been able to reproduce this behavior with an event listener.Cradling
@Cradling The return of the inline event is treated as the decision about what to do with the default behavior. Returning false from an inline click event is saying "do not do the default thing." I have been thinking about updating this answer for 2022 javascript, I might elaborate on that return value a bit too.Illbehaved
Z
295

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.

Zombie answered 14/6, 2011 at 18:52 Comment(13)
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.Despotism
2, 3, and 4 should be named dosomething. 1 gets overridden by 2 and is never called.Reeding
Indeed a very clear and to-the-point explanation. Be it that it would indeed make much more sense to name the functions 'doThing_1', etc. (If you still want to cater for IE<9, see Chris' answer.)Kendakendal
WHY WHY JS has multiple ways to do same thing ??Misdemean
I know it's been forever, but I just want to quickly ask a follow-up Q if that's okay. Do inline events get stored in memory without being garbage collected like eventListeners? Or do they get removed when the element gets removed?Roundsman
@Roundsman 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.Illbehaved
Events added with addEventListener can, in some circumstances, also need to be cleaned up.Illbehaved
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?Roundsman
@Roundsman Yes, I think you're doing it a better way -- using "event delegation" as they call it. That way there's only ever one listener. It's much more efficient and doesn't have the GC problems. I once built a visualization thing that had hundreds of thousands of html elements (it was kind of an experiment, lol), the weight of the elements alone was a lot for the browser, having event listeners on all of them would have broken it. One event listener at the top, though, worked great.Illbehaved
@ChrisBaker Just out of curiosity, did your single event handler's function include a decision tree such as a switch statement to check every one of those hundreds of thousands of elements, and, say, call a dummy function to simulate the delegation behavior of an event handler? It seems to me that any accurate performance comparison would require that sort of simulation, and I'm skeptical that a 100,000-item switch statement, each case with a function call, outperforms 100,000 event handlers. Are you saying that you did all that and the performance worked great instead of breaking the app?Claudy
@Claudy I used data attributes on the element to store the information needed to do the work that was to be done upon clicking or hovering a given element. The handler reads the data attributes from event.target and does what it needs to do. For click, it was showing an expander UI below the clicked element with additional details, on hover it was changing the content of a :before css rule to drive a tool tip. A 100,000 item switch statement would be a bad idea in any circumstance.Illbehaved
@Claudy I would also describe that particular use-case as novel -- 100k nodes with css applied isn't a good time. I visited the page just now (it's somehow still online) and Chrome gave it the college try, but gave me an Aw Snap error page eventually. 10 years ago, it seems that Chrome tried a little harder; it worked, but had occasional noticeable UI lag. Anyway, there are plenty of benchmarks available online evaluating more practical uses of event delegation.Illbehaved
@ChrisBaker Yes, that makes more sense. I didn't think of the data attributes storing function calls, and so couldn't figure out what you were up to. Thanks for explaining.Claudy
W
84

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="//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="//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="//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

Weiland answered 29/1, 2016 at 21:1 Comment(0)
I
32

While onclick works in all browsers, addEventListener does not work in older versions of Internet Explorer, which uses attachEvent instead.

The downside of onclick is that there can only be one event handler, while the other two will fire all registered callbacks.

Immediately answered 14/6, 2011 at 18:52 Comment(0)
R
28

Summary:

  1. addEventListener can add multiple events, whereas with onclick this cannot be done.
  2. onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
  3. addEventListener can take a third argument which can stop the event propagation.

Both can be used to handle events. However, addEventListener should be the preferred choice since it can do everything onclick does and more. Don't use inline onclick as HTML attributes as this mixes up the javascript and the HTML which is a bad practice. It makes the code less maintainable.

Rebound answered 25/8, 2018 at 9:48 Comment(4)
And how is thee element targeting mostly done? I mean, I'm not personally gonna use inline onclick handlrs in fear of being laughed out the room - but usually events are bound in much worse and less maintanle ways in recent years. Classes like js-link, js-form-validation or data attributes with data-jspackage="init" is in no way any better... And how ofteen do you actually use event bubbling? Me personally would love to be able to write a handler without checking if the target is actually a match to my element - or having to stop propagation in several places due to random bugs.Meimeibers
@ChristofferBubach I use event bubbling all the time. One simple example is a menu made up of an unordered list. You can put a single event listener on the ul tag. Clicking on any of the contained li elements bubbles up to this ul event listener. Then you use event.target to determine which li element was clicked, and go from there. If you don't use bubbling, you have to put a separate event listener for each of the li elements.Claudy
@Claudy ye fair point, and good example. might be one of few useful examples of event bubbleing though, and i could counter with five others where it’s just causing random bugs. not sure how much more efficient it really is, have to be one hell of a menu and pretty crappy browser/js interpreter for you to gain anything compared to handler connected directly on the LI? ;)Meimeibers
@ChristofferBubach Easier to maintain (i.e. add or remove items from) one handler. Not a hard and fast truth, but IMO.Claudy
E
12

As far as I know, the DOM "load" event still does only work very limited. That means it'll only fire for the window object, images and <script> elements for instance. The same goes for the direct onload assignment. There is no technical difference between those two. Probably .onload = has a better cross-browser availabilty.

However, you cannot assign a load event to a <div> or <span> element or whatnot.

Elaelaborate answered 1/8, 2011 at 17:27 Comment(0)
H
8

An element can have only one event handler attached per event type, but can have multiple event listeners.


So, how does it look in action?

Only the last event handler assigned gets run:

const button = document.querySelector(".btn")
button.onclick = () => {
  console.log("Hello World");
};
button.onclick = () => {
  console.log("How are you?");
};
button.click() // "How are you?" 

All event listeners will be triggered:

const button = document.querySelector(".btn")
button.addEventListener("click", event => {
  console.log("Hello World");
})
button.addEventListener("click", event => {
  console.log("How are you?");
})
button.click() 
// "Hello World"
// "How are you?"

IE Note: attachEvent is no longer supported. Starting with IE 11, use addEventListener: docs.

Haileyhailfellowwellmet answered 23/11, 2020 at 14:12 Comment(0)
K
6

One detail hasn't been noted yet: modern desktop browsers consider different button presses to be "clicks" for AddEventListener('click' and onclick by default.

  • On Chrome 42 and IE11, both onclick and AddEventListener click fire on left and middle click.
  • On Firefox 38, onclick fires only on left click, but AddEventListener click fires on left, middle and right clicks.

Also, middle-click behavior is very inconsistent across browsers when scroll cursors are involved:

  • On Firefox, middle-click events always fire.
  • On Chrome, they won't fire if the middleclick opens or closes a scroll cursor.
  • On IE, they fire when scroll cursor closes, but not when it opens.

It is also worth noting that "click" events for any keyboard-selectable HTML element such as input also fire on space or enter when the element is selected.

Kutchins answered 30/6, 2015 at 19:30 Comment(0)
S
5

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

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

They apparently do the same thing: listen for the click event and execute a callback function. Nevertheless, they’re not equivalent. If you ever need to choose between the two, this could help you to figure out which one is the best for you.

The main difference is that onclick is just a property, and like all object properties, if you write on more than once, it will be overwritten. With addEventListener() instead, we can simply bind an event handler to the element, and we can call it each time we need it without being worried of any overwritten properties. Example is shown here,

Try it: https://jsfiddle.net/fjets5z4/5/

In first place I was tempted to keep using onclick, because it’s shorter and looks simpler… and in fact it is. But I don’t recommend using it anymore. It’s just like using inline JavaScript. Using something like – that’s inline JavaScript – is highly discouraged nowadays (inline CSS is discouraged too, but that’s another topic).

However, the addEventListener() function, despite it’s the standard, just doesn’t work in old browsers (Internet Explorer below version 9), and this is another big difference. If you need to support these ancient browsers, you should follow the onclick way. But you could also use jQuery (or one of its alternatives): it basically simplifies your work and reduces the differences between browsers, therefore can save you a lot of time.

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("1 is not called, 2 is called")
}

EventListener.addEventListener("click",function(){
    window.alert("1 is called")
})
EventListener.addEventListener("click",function(){
    window.alert("2 is also called")
})
Salta answered 10/2, 2020 at 5:22 Comment(2)
@agiopnl my friend, this is all the difference that I have shown, you can use any, based on your requirement. Please speak politely, even if you disagree with the solution provided by other people.Salta
I don't understand... Why would you ever need to worry about overwriting your properties? Rather than a worry I would see it as improved functionality. If you are worried about hazardous code, you can also remove with removeEventListener() too. You say it's discouraged, but you provide no reason.Septenary
G
5

You should also consider EventDelegation for that! For that reason I prefer the addEventListener and foremost using it carefully and consciously!

FACTS:

  1. EventListeners are heavy .... (memory allocation at the client side)
  2. The Events propagate IN and then OUT again in relation to the DOM tree. Also known as trickling-in and bubbling-out , give it a read in case you don't know.

So imagine an easy example: a simple button INSIDE a div INSIDE body ... if you click on the button, an Event will ANYWAY trickle in to BUTTON and then OUT again, like this:

window-document-div-button-div-document-window

In the browser background (lets say the software periphery of the JS engine) the browser can ONLY possibly react to a click, if it checks for each click done where it was targeted.

And to make sure that each possible event listener on the way is triggered, it kinda has to send the "click event signal" all the way from document level down into the element ... and back out again. This behavior can then made use of by attaching EventListeners using e.g.:

document.getElementById("exampleID").addEventListener("click",(event) => {doThis}, true/false);

Just note for reference that the true/false as the last argument of the addEventListener method controls the behavior in terms of when is the event recognized - when trickling in or when bubbling out.

TRUE means, the event is recognized while trickling-in FALSE means, the event is recognized on its way bubbling out

Implementing the following 2 helpful concepts also turns out much more intuitive using the above stated approach to handle:

  1. You can also use event.stopPropagation() within the function (example ref. "doThis") to prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed.
  2. If you want to stop those behaviors, you could use event.preventDefault() within the function (example ref. "doThis"). With that you could for example tell the Browser that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

Also just note here for reference again: the last argument of the addEventListener method (true/false) also controls at which phase (trickling-in TRUE or bubbling out FALSE) the eventual effect of ".stopPropagation()" kicks in. So ... in case you apply an EventListener with flag TRUE to an element, and combine that with the .stopPropagation() method, the event would not even get through to potential inner children of the element

To wrap it up: If you use the onClick variant in HTML ... there are 2 downsides for me:

  1. With addEventListener, you can attach multiple onClick-events to the same, respectively one single element, but thats not possible using onClick (at least thats what I strongly believe up to now, correct me if I am wrong).
  2. Also the following aspect is truly remarkable here ... especially the code maintenance part (didn't elaborate on this so far):

In regards to event delegation, it really boils down to this. If some other JavaScript code needs to respond to a click event, using addEventListener ensures you both can respond to it. If you both try using onclick, then one stomps on the other. You both can't respond if you want an onclick on the same element. Furthermore, you want to keep your behavior as separate as you can from the HTML in case you need to change it later. It would suck to have 50 HTML files to update instead of one JavaScript file. (credit to Greg Burghardt, addEventListener vs onclick with regards to event delegation )

  • This is also known by the term "Unobtrusive JavaScript" ... give it a read!
Gambier answered 3/10, 2021 at 19:53 Comment(2)
a bit wordy... I would rewrite this. He didn't ask about technical details of events. I'd just cut this down to the last paragraph.Appreciate
This is a great topic though Timo, and is important for people to understand about modern JS. We're doing more than ever in the front-end, and memory efficiency is critical. Every listener adds to the total weight of the page.Illbehaved
L
4

Javascript tends to blend everything into objects and that can make it confusing. All into one is the JavaScript way.

Essentially onclick is a HTML attribute. Conversely addEventListener is a method on the DOM object representing a HTML element.

In JavaScript objects, a method is merely a property that has a function as a value and that works against the object it is attached to (using this for example).

In JavaScript as HTML element represented by DOM will have it's attributes mapped onto its properties.

This is where people get confused because JavaScript melds everything into a single container or namespace with no layer of indirection.

In a normal OO layout (which does at least merge the namespace of properties/methods) you would might have something like:

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

There are variations like it could use a getter/setter for onload or HashMap for attributes but ultimately that's how it would look. JavaScript eliminated that layer of indirection at the expect of knowing what's what among other things. It merged domElement and attributes together.

Barring compatibility you should as a best practice use addEventListener. As other answers talk about the differences in that regard rather than the fundamental programmatic differences I will forgo it. Essentially, in an ideal world you're really only meant to use on* from HTML but in an even more ideal world you shouldn't be doing anything like that from HTML.

Why is it dominant today? It's quicker to write, easier to learn and tends to just work.

The whole point of onload in HTML is to give access to the addEventListener method or functionality in the first place. By using it in JS you're going through HTML when you could be applying it directly.

Hypothetically you can make your own attributes:

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

What JS does with is a bit different to that.

You can equate it to something like (for every element created):

element.addEventListener('click', function() {
    switch(typeof element.onclick) {
          case 'string':eval(element.onclick);break;
          case 'function':element.onclick();break;
     }
});

The actual implementation details will likely differ with a range of subtle variations making the two slightly different in some cases but that's the gist of it.

It's arguably a compatibility hack that you can pin a function to an on attribute since by default attributes are all strings.

Labefaction answered 7/3, 2017 at 21:12 Comment(0)
R
3

According to MDN, the difference is as below:

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.

There is also a syntax difference in use as you see in the below codes:

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!");
}
Rheotropism answered 27/4, 2017 at 3:41 Comment(0)
B
2

I guess Chris Baker pretty much summed it up in an excellent answer but I would like to add to that with addEventListener() you can also use options parameter which gives you more control over your events. For example - If you just want to run your event once then you can use { once: true } as an option parameter when adding your event to only call it once.

    function greet() {
    console.log("Hello");
    }   
    document.querySelector("button").addEventListener('click', greet, { once: true })

The above function will only print "Hello" once. Also, if you want to cleanup your events then there is also the option to removeEventListener(). Although there are advantages of using addEventListener() but you should still be careful if your targeting audience is using Internet Explorer then this method might not work in all situation. You can also read about addEventListener on MDN, they gave quite a good explanation on how to use them.

Bevel answered 7/5, 2021 at 12:35 Comment(1)
This is great info! The options parameter did not exist at the time of my answer. I was thinking about giving my answer a little update, I will link to this answer to call out this newer information. Good answer :)Illbehaved
G
1

If you are not too worried about browser support, there is a way to rebind the 'this' reference in the function called by the event. It will normally point to the element that generated the event when the function is executed, which is not always what you want. The tricky part is to at the same time be able to remove the very same event listener, as shown in this example: 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);           
    }
}

The code above works well in Chrome, and there's probably some shim around making "bind" compatible with other browsers.

Goggleeyed answered 14/3, 2014 at 9:36 Comment(0)
F
1

Using inline handlers is incompatible with Content Security Policy so the addEventListener approach is more secure from that point of view. Of course you can enable the inline handlers with unsafe-inline but, as the name suggests, it's not safe as it brings back the whole hordes of JavaScript exploits that CSP prevents.

Flagwaving answered 20/4, 2015 at 16:40 Comment(1)
NB: this security restriction only applies to extension development, and the security justifications offered in the linked document largely only apply to browser extension development. The one point made in the linked document which is also true for web development generally, though, is separating content from behavior. That's good practice across the board.Illbehaved
S
1

It should also be possible to either extend the listener by prototyping it (if we have a reference to it and its not an anonymous function) -or make the onclick call a call to a function library (a function calling other functions).

Like:

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

This means we never have to change the onclick call just alter the function myFunctionList() to do whatever we want, but this leaves us without control of bubbling/catching phases so should be avoided for newer browsers.

Susurrus answered 25/7, 2015 at 20:49 Comment(0)
S
0

addEventListener lets you set multiple handlers, but isn't supported in IE8 or lower.

IE does have attachEvent, but it's not exactly the same.

Shimmery answered 14/6, 2011 at 18:52 Comment(0)
A
0

onclick is basically an addEventListener that specifically performs a function when the element is clicked. So, useful when you have a button that does simple operations, like a calculator button. addEventlistener can be used for a multitude of things like performing an operation when DOM or all content is loaded, akin to window.onload but with more control.

Note, You can actually use more than one event with inline, or at least by using onclick by seperating each function with a semi-colon, like this....

I wouldn't write a function with inline, as you could potentially have problems later and it would be messy imo. Just use it to call functions already done in your script file.

Which one you use I suppose would depend on what you want. addEventListener for complex operations and onclick for simple. I've seen some projects not attach a specific one to elements and would instead implement a more global eventlistener that would determine if a tap was on a button and perform certain tasks depending on what was pressed. Imo that could potentially lead to problems I'd think, and albeit small, probably, a resource waste if that eventlistener had to handle each and every click

Academician answered 1/3, 2020 at 20:33 Comment(0)
P
-1

The context referenced by 'this' keyword in JavasSript is different.

look at the following code:

<!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>

What it does is really simple. when you click the button, the button will be disabled automatically.

First when you try to hook up the events in this way button.onclick = function(), onclick event will be triggered by clicking the button, however, the button will not be disabled because there's no explicit binding between button.onclick and onclick event handler. If you debug see the 'this' object, you can see it refers to 'window' object.

Secondly, if you comment btnSubmit.onclick = disable(); and uncomment //btnSubmit.addEventListener('click', disable, false); you can see that the button is disabled because with this way there's explicit binding between button.onclick event and onclick event handler. If you debug into disable function, you can see 'this' refers to the button control rather than the window.

This is something I don't like about JavaScript which is inconsistency. Btw, if you are using jQuery($('#btnSubmit').on('click', disable);), it uses explicit binding.

Pritchard answered 13/1, 2014 at 1:2 Comment(1)
You need to write btnSubmit.onclick = disable; (assign function, not call it). Then in both cases this will refer to the button element.Phalan
S
-1

in my Visual Studio Code, addEventListener has Real Intellisense on event

enter image description here

but onclick does not, only fake ones

enter image description here

Saliva answered 17/12, 2020 at 4:46 Comment(0)
S
-2
let element = document.queryselector('id or classname'); 
element.addeventlistiner('click',()=>{
  do work
})

<button onclick="click()">click</click>`
function click(){
  do work
};
Styles answered 19/2, 2021 at 19:41 Comment(1)
The community encourages adding explanations alongisde code, rather than purely code-based answers (see here). Also, please check out the formatting help page to improve your formatting.Dextrad

© 2022 - 2024 — McMap. All rights reserved.