onclick="" vs event handler
Asked Answered
E

9

42

If I want a function to be executed, I prefer doing inline js:

<p id="element" onclick="doSomething();">Click me</p>

because it is easier to debug.

However, I hear people saying not to use inline js, and do:

document.getElementById('element').onclick = doSomething;

Why is the js event listener recommended?

Epistaxis answered 4/8, 2011 at 12:34 Comment(6)
Separation of concerns and composability are lost when going inline. That is, you are mixing display with behaviour and if you want to change the behaviour (add to it, for example), you need to change it in multiple places.Byrom
Also called unobtrusive JS. Another concern is <body onload="..."> which is so easily overwritten with a window.onload so keep the event handlers in one placeVolcanic
I'm currently working on a small one-page application and my JavaScript file has exceeded 700 lines. If I had to search for all my event handler bindings in the HTML code, I would probably lose my mind :)Rind
See this question for a more practical reason never to use inline event handler attributes: you never know what object properties are going to get dropped into your scope, potentially hiding globals and making your code break in some browser or some future version of a browser.Ratline
related: "unobstrusive javascript" explained #4479295Thomsen
related: https://mcmap.net/q/41323/-why-is-using-onclick-in-html-a-bad-practiceGoodson
R
15

Basically it has to do with the whole keep everything separate I believe. So keep HTML/CSS/JS all separate. It makes your HTML tidier and, I think, easier to navigate without.

Then when/if you need to make large changes, you have ample space with having to shift the inline JS to an external file anyway OR if you want to apply the same function to more than one button, then it's less code. And less code is a happier place

If you have your JS files properly, and thoroughly documented then navigating them by an outside person is made eaiser

Ron answered 4/8, 2011 at 12:39 Comment(2)
Agreed, if you have your javascript in a logical, understandable way, the advantage of inline js becomes very low. Still it does not add an advantage to the external handling. Also, since you are just adding a function name, possible with parameters, you can still keep everything seperateEpistaxis
I don't know if it's an advantage as such, just a practice is keep everything separate I think. So I guess to answer your question, there isn't really an advantage of either of them. As someone pointed out above, if you have a lot of dynamic content, sometimes inline can be easier, but if not, I personally prefer external file. I THINK it could just be preference. Someone please correct me if I'm wrong though.Ron
M
48

One big argument against inline event handlers, and the argument that is addressed by the other answers here is the separation of presentation and logic.

However, there is actually a bigger problem IMO: The somehow elusive way how inline event handlers are evaluated.

As you may know, the content of the on* attributes will be used as body of the event handler function. But what characteristics does this function have?

One of the surprising ones is that properties of some ancestor elements and of the element itself are in the scope of the inline event handler.

<form>
    <input name="foo" />
    <button type="button" onclick="console.log(foo); console.log(window.foo);">
        Click me
    </button>
    <div onclick="console.log(foo);">Click me as well!</div>
</form>

Clicking the button logs

<input name="foo"></input>
undefined

in the console. The fact that window.foo is undefined tells you that there is no global variable foo. So where does the variable foo come from? Why does console.log(foo) log the input element and not throw a reference error?
Because the properties of the form element are in the scope of the event handler and the form element has a property for each named form control element it contains. You can easily test this with console.log(document.querySelector('form').foo).

Now, clicking the div element actually throws a reference error:

ReferenceError: foo is not defined

So apparently the form element is only in scope of form control elements, not any descendant. How confusing is that?

Similarly, the properties of the document object are also in the scope of inline event handlers, which can lead to some surprising bugs (did you know that document has a property plugins?).

How exactly inline event handlers are evaluated is formalized in the HTML5 spec. Have a loop at step 10 in particular where he scope chain creation is described.


Conclusion:

Because of this implicit connection between elements and inline event handlers, bugs can be really hard to track. It's of course fine to use inline event handlers if you just want to test something. But using them in production code comes with a higher maintenance cost.

The articles at quirksmode.org explain the different ways of binding event handlers and their (dis)advantages very well.

Meanly answered 23/2, 2014 at 22:37 Comment(4)
related article about the scope: jibbering.com/faq/names/event_handler.htmlFingerprint
not to mention that inline click-handlers necessitate events to be registrered in the global scopeDelve
Ancient question, I realize, but it came up in a question just a while ago. It seems that at least some modern browsers don't do that weird with-like scope wrapper now; do you know if that's really true?Salt
ah it's only for elements with a form owner; apparently some WebKit versions give that to <a> elements, or at least that's what the new question claimed. Never mind.Salt
R
15

Basically it has to do with the whole keep everything separate I believe. So keep HTML/CSS/JS all separate. It makes your HTML tidier and, I think, easier to navigate without.

Then when/if you need to make large changes, you have ample space with having to shift the inline JS to an external file anyway OR if you want to apply the same function to more than one button, then it's less code. And less code is a happier place

If you have your JS files properly, and thoroughly documented then navigating them by an outside person is made eaiser

Ron answered 4/8, 2011 at 12:39 Comment(2)
Agreed, if you have your javascript in a logical, understandable way, the advantage of inline js becomes very low. Still it does not add an advantage to the external handling. Also, since you are just adding a function name, possible with parameters, you can still keep everything seperateEpistaxis
I don't know if it's an advantage as such, just a practice is keep everything separate I think. So I guess to answer your question, there isn't really an advantage of either of them. As someone pointed out above, if you have a lot of dynamic content, sometimes inline can be easier, but if not, I personally prefer external file. I THINK it could just be preference. Someone please correct me if I'm wrong though.Ron
M
5

There are a lot of reasons to avoid inline JavaScript and one of the perhaps most important is code maintainability.

A quick example (I am using jQuery simply for demonstration purposes).

<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>

What if suddenly you get a request to change all your paragraphs to execute another function? In your example you would have to change everything manually in your HTML code. However if you choose to separate HTML from JavaScript you could simply do it like this.

<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>

$('.element').bind('click', doSomethingElse);

The HTML code is also cleaner which allows the designers to focus exclusively on design without fear that they might actually break something while working on a project which also involves other people.

EDIT: Providing example for my comment bellow.

Project = {
    // All the variables/constants/objects that need to be globally accessible inside the Project object.

    init : function(){
        // Main entry point...
        this.MainMenu.init();

        // Rest of the code which should execute the moment Project is initiated.
    }
}

Project.MainMenu = {
    // All the variables/constants/objects that need to be accessible only to MainMenu.

    init : function(){ // Is run immediatelly by Project.init()
        // Event handlers relevant to the main menu are bound here

        // Rest of the initialization code
    }
}

Project.SlideShow = {
    // All the variables/constants/objects that need to be accessible only to SlideShow.

    init : function(){ // Is run only on pages that really require it.
        // Event handlers for the slideshow.
    }
}
Mir answered 4/8, 2011 at 12:46 Comment(9)
and now I want to add a parameter and have to wrap DoSomethingElse in a different function, function(){ doSomethingElse(para) } this usualy makes things harder to read while having it inline, you just add the parameter. Also, I have not expierenced needing the same function in alot of different elements, usualy just one, maybe a fewEpistaxis
@Epistaxis What parameter? You usually get all information from the event object which is the default first parameter of event handlers.Rind
Event handlers usually make use of the object they are attached to to perform what they are supposed to. Even if you do require additional parameters which are not bound to them (which you could also collect unobtrusively) its still much cleaner to do all your event handling stuff externally in a separate JavaScript file. Bottom line, you might not see the advantages of separating your JavaScript code from HTML in small projects however when you start doing much larger projects you will quickly see all the benefits it provides :) Separation is salvation in this case :)Mir
@holodoc, I thought of that, but seeing how I currently work in a rather large project, we keep getting problems from events being over-written by other things. Sure, the problem is not just the event-registration but it is hard to tell wether or not an element already has an event attached to it. Especialy on larger projects event registration can become quite the mazeEpistaxis
Well that's a completely different story :) You might want to adopt some conventions for your team in order to avoid those kind of problems. I personally have my own conventions which usually include the usage of object literals to avoid these kind of problems. Object literals do have their downsides (i.e. they are initialized as soon as they are defined, they can't be reused etc.) however they provide some useful stuff like scope protection, namespacing etc. I edited my post above to provide an example how I usually organize my projects.Mir
we actualy use a convention like that. But the problem persists, I agree that the convention became sloppy over time, when kept strict the problem will probably won't show. However, when using the inline method, the problem simply will not show because everybody who wants to use that element, will notice it already has a function and thus, keep that in mind.Epistaxis
Well what can I say. If you find the inline style more useful and less prone to abuse by your teammates then by all means use it :) There is no rule which prevents you from doing that. Just keep in mind that there has to be some weight on words coming from so many people claiming that unobtrusive JavaScript is the way to go :)Mir
Agreed, that is exactly why I posted this question, to hear 'the other side' arguments :) There are always specific cases where inline javascript is better and other cases where event listening is better. But for the avarage use? So far I'm still favouring inline but oh well,Epistaxis
I'm not sure this is really a realistic example of code someone would write unless it's being generated in a loop.Danziger
A
5

Despite what other people may think, I think there are redeeming value to inlining listeners in markup. Specifically, it gives you a lot more freedom to modifying the DOM nodes. If you're adding listeners though JavaScript, the listeners are lost when you replace the innerHTML of any parent nodes. If you're inlining the listeners in markup, you can clone the node, make modifications to it, then replace the original node with the node you just cloned and modified.

Perhaps it's better to describe it in a specific use case. I want to make changes to multiple part of the document without triggering reflow multiple times. So in this case, I can clone the node, make any change to it (no reflow since its detached), then replace the previous node with the modified one (triggering one reflow). With inlined listeners, this prevents any listeners from getting lost during the replacement.

Astrakhan answered 24/10, 2013 at 19:19 Comment(1)
Leveraging HTML for its declarative form (in this way) allows me to add a dom element inspector of my own, that can live-edit the functionality associated with an element, basically for free. If instead there were all event listeners, all of that binding is invisible unless methodically tracking and exposing it elsewhere, which does not come free.Gantry
M
3

I see some saying there needs to be a separation of concerns regarding Presentation and Business Logic.

OP in his example does indeed show this separation! There is no logic in the inline event handler, but merely a function reference/call that will get executed on the "click" event... the logic itself can be separately maintained elsewhere.

I personally prefer this approach due to logical flow discoverability. If I have never seen an application before... the first place I'm going to begin my code traversal is in the DOM, and right there it will be clear which event handlers are in play and which functions are providing the logic to the handlers. With using, say a "JQuery.On" eventing approach, by just glancing through the html you would have no idea which handlers are actually wired up and providing functionality.

Inline event handlers that simply provide pointers to functions is simply wiring up the events and not leaking logic into presentation.

Macassar answered 1/11, 2017 at 12:40 Comment(1)
Agreed, and I would actually even make a stronger statement - the OP's approach promotes a separation of concerns in abstracting away implementation details. In this way, the JS code is then only concerned with the "how", and doesn't have to concern itself with "where" it would be used. It's up to the DOM to specify what logic should apply to a particular element, making editing the DOM more flexible and avoiding HTML id's in the JS whenever possible.Lately
M
0

You could say the same about CSS and including inline styles. It would be easy to not have to wade through a css file to find the current class/id, or parent/child elements your working with.

Is it really better to bind a click to say 10 different items inline? Or just one event handler targeting a class? Even if you don't need 10 click handlers its always a better idea to set your code up for maintainability, and upgrades down the road.

Mana answered 4/8, 2011 at 12:38 Comment(5)
but in way, you ARE including inline styles, aren't you putting class and id attributes so you can style them in your css? Sure they have other uses aswell but stillEpistaxis
@Epistaxis ID's and class-names are just names (which are used by JavaScript code too, not just CSS code). Including inline styles means having CSS code hard-coded directly into the HTML tag.Rind
Exactly, I don't suggest putting the whole function code in the onclick, just a name. Just as you put only a name in a class or ID, and onclicks can be used by CSS aswell, (though that is usualy not good practice)Epistaxis
@johan the difference is now you have to update a name in places if you change the function name or signature at all. I shouldn't have to look in your markup to find the JS your using. Also including those click events in the markup makes your markup dependent on that specific js now.Mana
@Epistaxis But now you have one additional name which is a drawback. On the other hand, when you bind your event handlers with JavaScript, the handler functions themselves are anonymous - you bind an anonymous function to one or more elements which you selected using the ID's and class-names that are already defined on those elements. What you, on the other hand, have is ID's and class-names for CSS selection, plus an additional set of names for all your event handler functions.Rind
H
0

I'm not aware of and advantage of using inline handler vs attach the handler later. In my experience I prefer to use the inline method when I have to deal with dynamic elements, for example if I want to add an handler to each image and the number of images changes according to other data (ex images in db). In this case using inline allow me to add an handler to each image, otherwise i have to recalculate the number of images in the javascript file to attach and handler to each image.

Of course, when you can use libraries like jQuery where you can easily get the list of elements inline aren't useful

Hyatt answered 4/8, 2011 at 12:40 Comment(0)
D
0

Apart from the coding standards perspective

Using Attributes:

<p id="element" onclick="doSomething();">Click me</p>

if you add the same attribute again (see below) -the latest one is considered.

<p id="element" onclick="doSomethingMore();">Click me</p>

Using Event Handler:

document.getElementById('element').onclick = doSomething;

and lets say you added the below line

document.getElementById('element').onclick = doSomethingMore; Both the handlers are invoked.

Duala answered 27/2, 2018 at 11:17 Comment(0)
L
0

I'm surprised that nobody has mentioned content security policy (CSP) as a reason to use event properties in place of event attributes. I think that trumps any of the personal preferences given so far.

When properly implemented (i.e. not adding "unsafe-inline", which is utter nonsense imho) CSP1.0 basically blocks all forms of inline script. Making inline event attributes impossible. While CSP2.0 attempts to remedy this by providing a "nonce" for inline script, it does not provide such a thing for inline event attributes. CSP3.0 adds "unsafe-hashes" for inline events, but still no sign of a nonce.

So, in the absence of a nonce for inline events, event properties (or event listeners) is very much the way to go from a CSP standpoint.

Louvre answered 16/8, 2021 at 20:18 Comment(1)
When linking to your own site or content (or content that you are affiliated with), you must disclose your affiliation in the answer in order for it not to be considered spam. Having the same text in your username as the URL or mentioning it in your profile is not considered sufficient disclosure under Stack Exchange policy.Helgahelge

© 2022 - 2024 — McMap. All rights reserved.