138

I know this is bad practice. Don't write code like this if at all possible.

Of course, we'll always find ourselves in situations where a clever snippet of inline Javascript can address an issue quickly.

I am pursuing this query in the interest of fully understanding what happens (and the potential pitfalls) when something like this is written:

<a href="#" onclick="alert('Hi')">Click Me</a>

As far as I can tell this is functionally the same as

<script type="text/javascript">
   $(function(){ // I use jQuery in this example
       document.getElementById('click_me').onclick = 
           function () { alert('Hi'); };
   });
</script>
<a href="#" id="click_me">Click Me</a>

Extrapolating from this it seems that the string assigned to attribute onclick is inserted within an anonymous function which is assigned to the element's click handler. Is this actually the case?

Because I'm starting to do things like this:

<a href="#" onclick="$(this).next().fadeIn(); return false;">Display my next sibling</a> <!-- Return false in handler so as not to scroll to top of page! --> 

Which works. But I don't know how much of a hack this is. It looks suspicious because there is no apparent function that is being returned from!

You might ask, why are you doing this, Steve? Inline JS is bad practice!

Well to be quite honest I'm tired of editing three different sections of code just to modify one section of a page, especially when I'm just prototyping something to see if it will work at all. It is so much easier and sometimes even makes sense for the code specifically related to this HTML element to be defined right within the element: When I decide 2 minutes later that this was a terrible, terrible idea I can nuke the entire div (or whatever) and I don't have a bunch of mysterious JS and CSS cruft hanging around in the rest of the page, slowing down rendering ever so slightly. This is similar to the concept of locality of reference but instead of cache misses we're looking at bugs and code bloat.

Steven Lu
  • 41,389
  • 58
  • 210
  • 364

7 Answers7

104

You've got it nearly correct, but you haven't accounted for the this value supplied to the inline code.

<a href="#" onclick="alert(this)">Click Me</a>

is actually closer to:

<a href="#" id="click_me">Click Me</a>
<script type="text/javascript">
document.getElementById('click_me').addEventListener("click", function(event) {
    (function(event) {
        alert(this);
    }).call(document.getElementById('click_me'), event);
});
</script>

Inline event handlers set this equal to the target of the event. You can also use anonymous function in inline script

<a href="#" onclick="(function(){alert(this);})()">Click Me</a>
habib
  • 2,366
  • 5
  • 25
  • 41
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • 7
    The script must come after the tag, otherwise the tag does not exist when it is executed -- I suggest changing your answer to reflect this. – undefined Jun 14 '13 at 22:20
  • how do i propagate the `event` with an inline `onclick='myFunction(event)'`?? – oldboy Feb 08 '20 at 04:16
9

What the browser does when you've got

<a onclick="alert('Hi');" ... >

is to set the actual value of "onclick" to something effectively like:

new Function("event", "alert('Hi');");

That is, it creates a function that expects an "event" parameter. (Well, IE doesn't; it's more like a plain simple anonymous function.)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 2
    Ah. So I can actually use the variable `event` to retrieve the event (and the originating element from it via `event.target`), from my inline JS snippet! Cool. – Steven Lu May 15 '12 at 20:03
  • 1
    IE really doesn't pass the `event` parameter, but if you use `event` inside this anonymous function IE will understand it as the actual Event Object. As the anonymous function is set as a property of `window` object in IE, it will see this as `window.event`. – rdleal May 15 '12 at 20:04
9

There seems to be a lot of bad practice being thrown around Event Handler Attributes. Bad practice is not knowing and using available features where it is most appropriate. The Event Attributes are fully W3C Documented standards and there is nothing bad practice about them. It's no different than placing inline styles, which is also W3C Documented and can be useful in times. Whether you place it wrapped in script tags or not, it's gonna be interpreted the same way.

https://www.w3.org/TR/html5/webappapis.html#event-handler-idl-attributes

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
Daniel B
  • 1,205
  • 14
  • 18
  • 3
    Yeah I'm inclined to agree, and I did even provide reasonable "reasons" for why this particular kind of use of inline code might be justified in certain situations. But the reality is that when an app (web app or otherwise) gets to be a certain complexity (and this doesn't even usually take very much time or work to reach) having little snippets of *code* strewn about the HTML *layout* is likely to be architecturally unsound from a maintainability perspective. By even beginning to directly code JS inside an HTML file, for instance, one is tempting the slippery slope of spaghettification. – Steven Lu Feb 15 '16 at 08:29
  • 1
    And that's a proper valid argument against, one that I agree on. I don't normally add inline scripting, nor styling for that mater, myself. But people have different tastes. Many for example like to add script and style tags in the body section, I can't stand it. But it's valid and some people like it. What seams like `bad practice/spaghettification` for some, is `good practice/structure` for others. – Daniel B Feb 15 '16 at 22:30
  • I've been looking at https://github.com/styled-components/styled-components and if you combine this with react, for example, we now have everything associated with a component of your app actually living together (hopefully peacefully) in one place. And it actually doesn't look terrible either. Feels like progress. In this light, jamming inline styles and code into HTML isn't the right way (so far it seems to be the jamming of HTML and styles into the JS code). – Steven Lu Jun 27 '18 at 17:27
6

The best way to answer your question is to see it in action.

<a id="test" onclick="alert('test')"> test </a> ​

In the js

var test = document.getElementById('test');
console.log( test.onclick ); 

As you see in the console, if you're using chrome it prints an anonymous function with the event object passed in, although it's a little different in IE.

function onclick(event) {
   alert('test')
}

I agree with some of your points about inline event handlers. Yes they are easy to write, but i don't agree with your point about having to change code in multiple places, if you structure your code well, you shouldn't need to do this.

aziz punjani
  • 25,586
  • 9
  • 47
  • 56
  • I think something like `` is perfectly fine for prototyping. You want to test something that requires user interaction right now, and you're just going to delete it later anyway. Why write extra code all over the place? – Dagg Nabbit May 15 '12 at 20:57
  • It's not extra code, just an extra anonymous function. And it's not all over the place, it's actually all in one place, in the script tags. – aziz punjani May 15 '12 at 21:04
  • I'm not sure we share the same idea of "structuring your code well." If you bind the UI to your "core functions" in the place where your core functions actually live, this seems to me like a worse coupling arrangement than just binding them in the UI. I usually keep all the UI binding stuff separate from the core stuff, and I have the feeling the OP might also... – Dagg Nabbit May 15 '12 at 21:13
3

It looks suspicious because there is no apparent function that is being returned from!

It is an anonymous function that has been attached to the click event of the object.

why are you doing this, Steve?

Why on earth are you doi.....Ah nevermind, as you've mentioned, it really is widely adopted bad practice :)

Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
3

Try this in the console:

var div = document.createElement('div');

div.setAttribute('onclick', 'alert(event)');

div.onclick

In Chrome, it shows this:

function onclick(event) {
  alert(event)
}

...and the non-standard name property of div.onclick is "onclick".

So, whether or not this is anonymous depends your definition of "anonymous." Compare with something like var foo = new Function(), where foo.name is an empty string, and foo.toString() will produce something like

function anonymous() {

}
Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • This began as a comment on Pointy's answer, but really didn't work as a comment. His is really the best answer here, I think. – Dagg Nabbit May 15 '12 at 20:25
1

using javascript:

here input element is used

<input type="text" id="fname" onkeyup="javascript:console.log(window.event.key)">

if you want to use multiline code use curly braces after javascript:

<input type="text" id="fname" onkeyup="javascript:{ console.log(window.event.key); alert('hello'); }">
Tushar Saha
  • 647
  • 6
  • 13