38

Which of the following ways is a better way to call a js function from an a tag?

<a href="javascript:someFunction()">LINK</a>

OR

<a href="#" onclick="someFunction();" return false;">LINK</a>

I have seen this question here, but it says <span onclick="someFunction()"> is a better option. But due to some reasons I have to use <a> links.

EDIT: I am looking for a cross browser & cross platform solution which should work on androids & iPads too.

Community
  • 1
  • 1
gopi1410
  • 6,567
  • 9
  • 41
  • 75
  • Using the `javascript:` "protocol" is a code smell. – Lucero Jun 04 '12 at 07:07
  • Instead of `href="javascript:void 0;"` you can just use `href="#"` .. just make sure to `return false` in your function :) – Ja͢ck Jun 04 '12 at 07:14
  • @Jack yeah, but it takes the user to the top of the page. – gopi1410 Jun 04 '12 at 07:15
  • @gopi1410 not if you return `false` from the function – Ja͢ck Jun 04 '12 at 07:16
  • 3
    The span option is _worse_ because it doesn't work from the keyboard like an anchor does, so I would recommend against that regardless of what your other reasons are for wanting to use anchors. How about `LINK` - so that if the user has JS disabled and clicks the link it takes 'em to a page that says the functionality won't work without JS? – nnnnnn Jun 04 '12 at 07:16
  • possible duplicate of [Href tag for JavaScript links: "#" or "javascript:void(0)"?](http://stackoverflow.com/questions/134845/href-tag-for-javascript-links-or-javascriptvoid0) –  Jun 05 '12 at 12:22
  • @Lucero Using JavaScript is a code smell – Hal50000 Feb 06 '16 at 00:31
  • I want to do what the question actually asks: call a JavaScript function from a div or other element when the browser renders that div. No button click or other event created by the visitor. This seems quite a challenge to do briefly, without Script tags. – David Spector May 29 '21 at 22:30

4 Answers4

34

Neither is good.

Behaviour should be configured independent of the actual markup. For instance, in jQuery you might do something like

$('#the-element').click(function () { /* perform action here */ });

in a separate <script> block.

The advantage of this is that it

  1. Separates markup and behaviour in the same way that CSS separates markup and style
  2. Centralises configuration (this is somewhat a corollary of 1).
  3. Is trivially extensible to include more than one argument using jQuery’s powerful selector syntax

Furthermore, it degrades gracefully (but so would using the onclick event) since you can provide the link tags with a href in case the user doesn’t have JavaScript enabled.

Of course, these arguments still count if you’re not using jQuery or another JavaScript library (but why do that?).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • +1, but also see my answer. Both of the options in the question introduce an XSS risk that CSP disables. The biggest advantage of your way of doing it (by a long way) is that it is secure and can be locked down by CSP. – Keith Jun 05 '15 at 09:52
  • This is just sad. So many real programming challenges. Thinking you're doing anything but obfuscating your own code here is fantasy. Look at the anchor tag, see the JavaScript event, you know what it does, the end. Attaching the event somewhere else is...just somewhere else. Now you have to look around for where. – Hal50000 Feb 06 '16 at 00:19
  • 1
    I don't think this answer is valid any longer and now is incorrect. Most frameworks do a binding in the html for the JavaScript code, which is the same logic as on-click. – dman Jan 29 '17 at 20:50
  • @dman True. For what it’s worth, I think this is a regression and will be another temporary fad in the long run. But we’ll see. Stack Overflow has issues dealing with changing best practices. I don’t know a good way of working around this: if I change my answer then I’d pretend that all these upvotes endorsed a different practice than they did. Best way is to author a new answer and let people upvote it. – Konrad Rudolph Jan 30 '17 at 11:19
9

Some advantages to the second option:

  1. You can use this inside onclick to reference the anchor itself (doing the same in option 1 will give you window instead).

  2. You can set the href to a non-JS compatible URL to support older browsers (or those that have JS disabled); browsers that support JavaScript will execute the function instead (to stay on the page you have to use onclick="return someFunction();" and return false from inside the function or onclick="return someFunction(); return false;" to prevent default action).

  3. I've seen weird stuff happen when using href="javascript:someFunction()" and the function returns a value; the whole page would get replaced by just that value.

Pitfalls

Inline code:

  1. Runs in document scope as opposed to code defined inside <script> tags which runs in window scope; therefore, symbols may be resolved based on an element's name or id attribute, causing the unintended effect of attempting to treat an element as a function.

  2. Is harder to reuse; delicate copy-paste is required to move it from one project to another.

  3. Adds weight to your pages, whereas external code files can be cached by the browser.

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
3

I’m tempted to say that both are bad practices.

The use of onclick or javascript: should be dismissed in favor of listening to events from outside scripts, allowing for a better separation between markup and logic and thus leading to less repeated code.

Note also that external scripts get cached by the browser.

Have a look at this answer.

Some good ways of implementing cross-browser event listeners here.

Community
  • 1
  • 1
Denis
  • 1,093
  • 5
  • 15
3

Modern browsers support a Content Security Policy or CSP. This is the highest level of web security and strongly recommended if you can apply it because it completely blocks all XSS attacks.

Both of your suggestions break with CSP enabled because they allow inline Javascript (which could be injected by a hacker) to execute in your page.

The best practice is to subscribe to the event in Javascript, as in Konrad Rudolph's answer.

Keith
  • 150,284
  • 78
  • 298
  • 434