Stop using anchors as buttons!

Posted by Jason Boyer on September 2, 2014 in Accessibility, CSS/HTML/JavaScript, UX, Web

Semantic buttons and links are important for usability as well as accessibility. Hyperlinks indicate a URL change, whereas buttons are used to perform an action. I thought this post up in response to a question asked on Stack Overflow over 5 years ago.

Which one should you use?

  1. <a href="#" onclick="doSomething()">Do Something</a>
  2. <a href="javascript:void(0);" onclick="doSomething()">Do Something</a>
  3. <a href="javascript:doSomething()">Do Something</a>
  4. <a href="" ng-click="app.doSomething()">Do Something</a>

Answer: None of those!

All elements in HTML (except for <div> and <span>) have a semantic meaning. This semantic meaning is interpreted by browsers, screen readers, and SEO crawlers. Browsers use this semantic value to properly display the elements and to bind interactions. Screen readers also use this semantic value to bind additional different keyboard shortcuts and interactions. Lastly, SEO crawlers use this meaning to measure importance of content and define relationships.

The above examples are using the <a> element to solely perform a JavaScript action. This is incorrect, as the semantic meaning of an <a> element as defined by the W3C is as follows:

The <a> element represents a hyperlink.

A hyperlink is “a link from a hypertext file or document to another location or file.1“. Since those examples are not linking to another location, they are incorrect.

So what is the correct element to use when its sole purpose is to perform a JS action? That would be the <button> element.

The <button> element with a type attribute whose value is "button" represents a button with no additional semantics.

I feel that this documentation could be slightly improved to imply that a “button” means the element should be “clickable” or “interactive.” Applying these definitions, the correct answer would be marked up like so:

<button type="button" onclick="doSomething()">Do Something</button>

Semantic buttons provide a cross-browser focusable area and indicate an interaction is expected. If you read no further than this, please only use an <a> element if you can answer yes to this question:

1. Does the element change the URL?

If it does not, please use the <button> element. Now for some further explanations:


Why is it so commonplace to use the <a> element to perform JS actions?

The <a> provides some key benefits:

  1. It is styleable.
  2. It is focusable (you could tab to it)
  3. It provides cross-browser support for :hover, :active, & :focus states

Way back in the day, the <button> element only provided the latter two benefits. It was impossible to style it consistently cross-browser (just think of trying to style some of the new HTML5 inputs cross-browser). This has long been remedied. At Ancestry, we created a generic “.link” class to style a <button> exactly like a text link, when needed. This allows us to use the proper HTML markup and retain control of the appearance. The CSS is similar to this:

.link { -webkit-appearance:none; background:none; border:0; -webkit-box-shadow:none; box-shadow:none; color:#03678B; cursor:pointer; display:inline; font-size:inherit; margin:0; padding:0; text-align:left; text-decoration:none; }
.link:active,
.link:hover,
.link:focus { color:#03678B; text-decoration:underline; }

This resets the styles for all our supported browsers and gave us the flexibility we needed in the markup to use the proper <button> element rather than the <a>.

One note about using the <button> element – Don’t leave off the [type="button"] attribute. The default type of a button in most browsers is a submit. This means without the [type="button"] attribute/value pair, users could accidentally submit a form.


What about using the <a> as a named target?

Hyperlinks can link to sections of content in a document, as well as external links. Again, way back in the day, markup like <a name="myRegion"></a> was the only cross-browser way to link to a section on the page. All modern browsers (even back to at least IE7) allow you to link to any section that has the id attribute.

Old way:

<a href="#my-section">Go to my section</a>
...Content...
<a name="my-section"></a>
<div>My section content</div>

Better way:

<a href="#my-section">Go to my section</a>
...Content...
<div id="my-section">My section content</div>

Simply use an id attribute on the containing element of the region you want “linkable.” Don’t use an empty <a name="my-section"></a> element.


Are there any reasons you would bind a JS action to an <a>?

Of course! A common one is a hyperlink that also fires off a tracking pixel. Something like the following:

<a href="http://www.ancestry.com" onclick="fireTracking('clicked home link');">Home</a>

In the case of single page apps, you definitely need to prevent the browser from navigating away from your page, but you should still use an <a> element since the result would be changing the URL. For example:

<script>
function doPushStateStuff(newPage) {
	// pushState magic here
	return false
}</script>
...
<a href="page-2" onclick="doPushStateStuff('page-2');">Page 2</a>

This actually brings up the other key rule for <a> elements:

  1. Users should always be able to open links in a new tab (middle-click or right-click to select “open link in new tab”)

I loathe single page apps that do not allow opening pages in a new tab. It’s a huge usability issue as it prevents users from using your site in their normal routine.


In summary:

  1. Semantic buttons (<button type="button">) should be used when an element’s sole purpose is to perform a JavaScript action.
  2. <a> elements should only be used when changing the page URL and should always be openable in a new tab.
  3. You should never write html like this: <a href="#" onclick="doSomething()">Do Something</a> or this <a href="javascript:void(0);" onclick="doSomething()">Do Something</a>

Note: Use Unobtrusive JavaScript rather than onclick attributes. They simply shortened the examples.

Leave a comment

Past Articles

Ancestry Opens Its Doors for NewCo.SF

Posted by Melissa Garrett on September 8, 2014 in Technology Conferences

Ancestry was selected as a 2014 NewCo.SF host company. Come join us at our San Francisco office on Thursday, Sept. 11 at 4:30pm PT to hear from Eric Shoup, EVP of Product at Ancestry.com. He will provide an inside look at the unique and meaningful business of family history and the tech, science, and product… Read more

The DNA matching research and development life cycle

Posted by Julie Granka on August 19, 2014 in DNA, Science

Research into matching patterns of over a half-million AncestryDNA members translates into new DNA matching discoveries  Among over 500,000 AncestryDNA customers, more than 35 million 4th cousin relationships have been identified – a number that continues to grow rapidly at an exponential rate.  While that means millions of opportunities for personal discoveries by AncestryDNA members,… Read more

Core Web Accessibility Guidelines

Posted by Jason Boyer on August 13, 2014 in Accessibility, CSS/HTML/JavaScript

How do you ensure accessibility on a website that is worked on by several hundred web developers? That is the question we are continually asking ourselves and have made great steps towards answering. The approach we took was to document our core guidelines and deliver presentations and trainings to all involved. This included our small… Read more

Maintaining Balance by Using Feedback Loops in Software

Posted by Chad Groneman on July 29, 2014 in C#, Performance

Maintaining Balance by Using Feedback Loops in Software Feedback is an important part of daily life.  Used wisely, feedback loops can help us make better decisions, resulting in overall improvements.  Feedback loops are useful in all sorts of situations, from relationships to what we eat for dinner.  Software can also be made to take advantage… Read more

Building an Operationally Successful Component – Part 3: Robustness

Posted by Geoff Rayback on July 23, 2014 in DevOps, Uncategorized

Building an Operationally Successful Component – Part 3: Robustness My previous two posts discussed building components that are “operationally successful.”  To me, a component cannot be considered successful unless it actually operates as expected when released into the wild.  Something that, “works on my machine,” cannot be considered a success unless it also works on… Read more