Stop Event Propagation for a Specific Handler - javascript

tl;dr Summary
Write a function that—when registered to handle a specific event on multiple elements in a hierarchy—executes on the first element reached during bubbling but does not execute (or returns early) when bubbling further up the hierarchy. (Without actually stopping propagation of the event.)
Simple Example
Given this HTML…
<!DOCTYPE html>
<body>
<div><button>click</button></div>
<script>
var d = document.querySelector('div'),
b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
b.addEventListener('mousedown',startDrag,false);
function clickPick(){ console.log('click',this.tagName); }
function startDrag(){ console.log('drag',this.tagName); }
</script>
…clicking on the button yields this output:
drag BUTTON
click DIV
drag DIV
However, I don't want to drag the div. Specifically, I want startDrag to only be processed once, on the <button>, the leaf-most element for which it is registered in a particular propagation chain.
"Working" solution
The following JavaScript code has been tested to work on IE9, Chrome18, Firefox11, Safari5, and Opera11.
function startDrag(evt){
if (seenHandler(evt,startDrag)) return;
console.log('drag',this.tagName);
}
function seenHandler(evt,f){
if (!evt.handlers) evt.handlers=[];
for (var i=evt.handlers.length;i--;) if (evt.handlers[i]==f) return true;
evt.handlers.push(f);
return false;
}
The above does not work with IE8, even if you use the global window.event object; the same event object is not reused during bubbling.
The Question(s)
However, I'm not sure if the above is guaranteed to work…nor if it's as elegant as it could be.
Is the same event object guaranteed to be passed up the chain during propagation?
Is the event object guaranteed to never be re-used for different event dispatches?
Is the event object guaranteed to support having an expando property added to it?
Can you think of a more elegant way to detect if the same event handler function has been seen already on the current dispatch of an event?
Real World Application
Imagine this SVG hierarchy…
<g>
<rect … />
<rect … />
<circle … />
</g>
…and a user who wants to be able to drag the whole group by dragging on any rect within it, and also to be able to drag just the circle by dragging on it.
Now mix in a generic dragging library that handles most of the details for you. I'm proposing to modify this library such that if the user adds a drag handler to both the <g> and the <circle> then dragging on the circle automatically prevents the dragging from initiating on the group, but without stopping propagation of all mousedown events (since the user might have a high level handler for other purposes that is desirable).

Here's a generic solution that works in the current versions of all major browsers...but not in IE8 and may not be guaranteed to work in future versions:
// Solution supporting arbitrary number of handlers
function seenFunction(evt,f){
var s=evt.__seenFuncs;
if (!s) s=evt.__seenFuncs=[];
for (var i=s.length;i--;) if (s[i]===f) return true;
evt.handlers.push(f);
return false;
}
// Used like so:
function myHandler(evt){
if (seenHandler(evt,myHandler)) return;
// ...otherwise, run code normally
}
Alternatively, here is a less-generic solution that is slightly-but-probably-not-noticeably faster (again, not in IE8 and maybe not guaranteed to work in the future):
// Implemented per handler; small chance of error with a conflicting name
function myHandler(evt){
if (evt.__ranMyHandler) return;
// ...otherwise, run code normally
evt.__ranMyHandler = true;
}
I will happily switch the acceptance to another answer with proper specs provided.

This looks good in Firefox, and can probably be adapted to non-standard browsers, since jQuery essentially does this with delegate and live. It uses stopPropagation and target , both of which are standard.
var d = document.querySelector('div'),
b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
function clickPick(evt){
console.log('click', this.tagName);
}
function startDrag(evt){
console.log('drag', evt.target.tagName);
evt.stopPropagation();
}
For me, the order when you click on the button is reversed. Yours has "drag BUTTON" then "click DIV", while mine is the opposite. However, I think it's undefined in the original.
EDIT: Changed to use target. IE before 9 uses a non-standard srcElement property that means the same thing.

Related

HTML events VS Javascript events [duplicate]

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.
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
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
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.
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);
Try clicking this link.
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();
Try clicking this link.
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:
Try clicking this link.
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
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
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.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
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.
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.
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.
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.
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")
})
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.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
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:
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.
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:
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).
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!
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!");
}
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.
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.
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.
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.
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.
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.
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
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

SVG element loses event handlers if moved around the DOM

I use this D3 snippet to move SVG g elements to top of the rest element as SVG render order depends on the order of elements in DOM, and there is no z index:
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
I run it like:
d3.select(el).moveToFront()
My issue is that if I add a D3 event listener, like d3.select(el).on('mouseleave',function(){}), then move the element to front of DOM tree using the code above, all event listeners are lost in Internet Explorer 11, still working fine in other browsers.
How can I workaround it?
Single event listener on parent element, or higher DOM ancestor:
There is a relatively easy solution which I did not originally mention because I had assumed you had dismissed it as not feasible in your situation. That solution is that instead of multiple listeners each on a single child element, you have a single listener on an ancestor element which gets called for all events of a type on its children. It can be designed to quickly choose to further process based on the event.target, event.target.id, or, better, event.target.className (with a specific class of your creation assigned if the element is a valid target for the event handler). Depending on what your event handlers are doing and the percentage of elements under the ancestor on which you already are using listeners, a single event handler is arguably the better solution. Having a single listener potentially reduces the overhead of event handling. However, any actual performance difference depending on what you are doing in the event handlers and on what percentage of the ancestor's children on which you would have otherwise placed listeners.
Event listeners on elements actually interested in
Your question asks about listeners which your code has placed on the element being moved. Given that you do not appear concerned about listeners placed on the element by code which you do not control, then the brute-force method of working around this is for you to keep a list of listeners and the elements upon which you have placed them.
The best way to implement this brute-force workaround depends greatly on the way in which you place listeners on the elements, the variety that you use, etc. This is all information which is not available to us from the question. Without that information it is not possible to make a known-good choice of how to implement this.
Using only single listeners of each type/namespace all added through selection.on():
If you have a single listener of each type.namespace, and you have added them all through the d3.selection.on() method, and you are not using Capture type listeners, then it is actually relatively easy.
When using only a single listener of each type, the selection.on() method allows you to read the listener which is assigned to the element and type.
Thus, your moveToFront() method could become:
var isIE = /*#cc_on!#*/false || !!document.documentMode; // At least IE6
var typesOfListenersUsed = [ "click", "command", "mouseover", "mouseleave", ...];
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
var currentListeners={};
if(isIE) {
var element = this;
typesOfListenersUsed.forEach(function(value){
currentListeners[value] = element.selection.on(value);
});
}
this.parentNode.appendChild(this);
if(isIE) {
typesOfListenersUsed.forEach(function(value){
if(currentListeners[value]) {
element.selection.on(value, currentListeners[value]);
}
});
}
});
};
You do not necessarily need to check for IE, as it should not hurt to re-place the listeners in other browsers. However, it would cost time, and is better not to do it.
You should be able to use this even if you are using multiple listeners of the same type by just specifying a namespace in the list of listeners. For example:
var typesOfListenersUsed = [ "click", "click.foo", "click.bar"
, "command", "mouseover", "mouseleave", ...];
General, multiple listeners of same type:
If you are using listeners which you are adding not through d3, then you would need to implement a general method of recording the listeners added to an element.
How to record the function being added as a listener, you can just add a method to the prototype which records the event you are adding as a listener. For example:
d3.selection.prototype.recOn = function (type, func) {
recordEventListener(this, type, func);
d3.select(this).on(type,func);
};
Then use d3.select(el).recOn('mouseleave',function(){}) instead of d3.select(el).on('mouseleave',function(){}).
Given that you are using a general solution because you are adding some listeners not through d3, you will need to add functions to wrap the calls to however you are adding the listener (e.g. addEventListener()).
You would then need a function which you call after the appendChild in your moveToFront(). It could contain the if statement to only restore the listeners if the browser is IE11, or IE.
d3.selection.prototype.restoreRecordedListeners = function () {
if(isIE) {
...
}
};
You will need to chose how to store the recorded listener information. This depends greatly on how you have implemented other areas of your code of which we have no knowledge. Probably the easiest way to record which listeners are on an element is to create an index into the list of listeners which is then recorded as a class. If the number of actual different listener functions you use is small, this could be a statically defined list. If the number and variety is large, then it could be a dynamic list.
I can expand on this, but how robust to make it really depends on your code. It could be as simple as keeping tack of only 5-10 actually different functions which you use as listeners. It might need to be as robust as to be a complete general solution to record any possible number of listeners. That depends on information we do not know about your code.
My hope is that someone else will be able to provide you with a simple and easy fix for IE11 where you just set some property, or call some method to get IE to not drop the listeners. However, the brute-force method will solve the problem.
One solution could be to use event delegation. This fairly simple paradigm is commonplace in jQuery (which gave me the idea to try it here.)
By extending the d3.selection prototype with a delegated event listener we can listen for events on a parent element but only apply the handler if the event's target is also our desired target.
So instead of:
d3.select('#targetElement').on('mouseout',function(){})
You would use:
d3.select('#targetElementParent').delegate('mouseout','#targetElement',function(){})
Now it doesn't matter if the events are lost when you move elements or even if you add/edit/delete elements after creating the listeners.
Here's the demo. Tested on Chrome 37, IE 11 and Firefox 31. I welcome constructive feedback but please note that I am not at all familiar with d3.js so could easily have missed something fundamental ;)
//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetid, handler) {
return this.on(event, function() {
var eventTarget = d3.event.target.parentNode,
target = d3.select(targetid)[0][0];
if (eventTarget === target) {//only perform event handler if the eventTarget and intendedTarget match
handler.call(eventTarget, eventTarget.__data__);
}
});
};
//add event listeners insead of .on()
d3.select('#svg').delegate('mouseover','#g2',function(){
console.log('mouseover #g2');
}).delegate('mouseout','#g2',function(){
console.log('mouseout #g2');
})
//initial move to front to test that the event still works
d3.select('#g2').moveToFront();
http://jsfiddle.net/f8bfw4y8/
Updated and Improved...
Following Makyen's useful feedback I have made a few improvements to allow the delegated listener to be applied to ALL matched children. EG "listen for mouseover on each g within svg"
Here's the fiddle. Snippet below.
//prototype. move to front
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetselector, handler) {
var self = this;
return this.on(event, function() {
var eventTarget = d3.event.target,
target = self.selectAll(targetselector);
target.each(function(){
//only perform event handler if the eventTarget and intendedTarget match
if (eventTarget === this) {
handler.call(eventTarget, eventTarget.__data__);
} else if (eventTarget.parentNode === this) {
handler.call(eventTarget.parentNode, eventTarget.parentNode.__data__);
}
});
});
};
var testmessage = document.getElementById("testmessage");
//add event listeners insead of .on()
//EG: onmouseover/out of ANY <g> within #svg:
d3.select('#svg').delegate('mouseover','g',function(){
console.log('mouseover',this);
testmessage.innerHTML = "mouseover #"+this.id;
}).delegate('mouseout','g',function(){
console.log('mouseout',this);
testmessage.innerHTML = "mouseout #"+this.id;
});
/* Note: Adding another .delegate listener REPLACES any existing listeners of this event on this node. Uncomment this to see.
//EG2 onmouseover of just the #g3
d3.select('#svg').delegate('mouseover','#g3',function(){
console.log('mouseover of just #g3',this);
testmessage.innerHTML = "mouseover #"+this.id;
});
//to resolve this just delegate the listening to another parent node eg:
//d3.select('body').delegate('mouseover','#g3',function(){...
*/
//initial move to front for testing. OP states that the listener is lost after the element is moved in the DOM.
d3.select('#g2').moveToFront();
svg {height:300px; width:300px;}
rect {fill: pink;}
#g2 rect {fill: green;}
#testmessage {position:absolute; top:50px; right:50px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="svg">
<g id="g1"><rect x="0px" y="0px" width="100px" height="100px" /></g>
<g id="g2"><rect x="50px" y="50px" width="100px" height="100px" /></g>
<g id="g3"><rect x="100px" y="100px" width="100px" height="100px" /></g>
</svg>
<div id="testmessage"></div>
As with all delegated listeners if you move the target element outside of the parent you have delegated the listening to then naturally the events for that child are lost. However, there is nothing to stop you delegating the event listening to the body tag as you'll never move a child outside of that. EG:
d3.select('body').delegate('mouseover','g',function(){...
This also happens in IE prior to 11. My mental model for why this error occurs is that if you're hovering over an element and then move it to the front by detaching and re-attaching it, mouseout events won't fire because IE loses the state that a mouseover occured in the past and thus doesn't fire a mouseout event.
This seems to be why it works fine if you move all other elements but the one you're hovering over. And this is what you can easily achieve by using selection.sort(comparatorFunction). See the d3 documentation on sort and the selection.sort and selection.order source code for further details.
Here's a simple example:
// myElements is a d3 selection of, for example, circles that overlap each other
myElements.on('mouseover', function(hoveredDatum) {
// On mouseover, the currently hovered element is sorted to the front by creating
// a custom comparator function that returns “1” for the hovered element and “0”
// for all other elements to not affect their sort order.
myElements.sort(function(datumA, datumB) {
return (datumA === hoveredDatum) ? 1 : 0;
});
});

Which is better and why ? element.onclick = function() Versus AddEventListener() [duplicate]

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.
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
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
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.
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);
Try clicking this link.
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();
Try clicking this link.
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:
Try clicking this link.
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
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
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.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
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.
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.
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.
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.
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")
})
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.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
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:
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.
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:
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).
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!
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!");
}
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.
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.
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.
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.
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.
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.
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
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

element.onload vs element.addEventListener("load",callbak,false) [duplicate]

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.
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
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
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.
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);
Try clicking this link.
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();
Try clicking this link.
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:
Try clicking this link.
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
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
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.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
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.
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.
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.
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.
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")
})
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.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
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:
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.
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:
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).
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!
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!");
}
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.
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.
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.
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.
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.
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.
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
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

addEventListener vs onclick

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.
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
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
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.
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);
Try clicking this link.
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();
Try clicking this link.
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:
Try clicking this link.
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
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
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.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
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.
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.
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.
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.
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")
})
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.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
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:
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.
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:
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).
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!
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!");
}
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.
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.
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.
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.
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.
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.
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
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

Categories