Button vs. Link: Native Semantics Before Custom Click Handlers

In Brief
Use an a element with an href when activating the control goes to another location. Use a button when it performs an action on the current page, and set its type explicitly. A click handler or ARIA role on a div does not recreate the native element's keyboard behaviour, focusability, or other browser features.
It's easy to make almost any element respond to a mouse click. Give a div an onclick handler, make the cursor look like a pointer, and the result can appear to work exactly like a control.
The trouble is that a click handler provides only a click handler. It doesn't give the element the meaning, focus behaviour, keyboard interaction, or form behaviour of a link or a button. Those are separate parts of the control, and browsers already provide them when we choose the right HTML element.
Start with the Result of the Interaction
The simplest distinction is the destination.
A link takes the visitor somewhere. That might be another page, a file, or a location within the current document:
<a href="/account/settings/">Account settings</a>The href is not an optional implementation detail. It is the destination represented by the link, and it allows the browser to offer features such as opening it in a new window or tab, copying its address, and showing that address before it is followed. If JavaScript fails, the navigation can still work.
A button performs an action. It might reveal help, dismiss a message, or change part of the current page:
<button type="button" id="show-help">Show help</button>The HTML5 working draft's definition of button describes it as an element activated by the user, with behaviour determined by its type. The word "button" is not merely a visual suggestion; it identifies an interactive control to the browser and to assistive technology.
Native Behaviour Comes with the Element
A link with an href participates in the page's tab order and can be followed from the keyboard with Enter. It also has link‑specific browser behaviour such as its destination menu and visited state.
A native button participates in the tab order and can be activated from the keyboard, including with the Space bar. It can expose a disabled state and is understood as a button without an extra role. None of that has to be recreated by a click handler.
Buttons do have one detail which regularly catches people out. Within a form, a button defaults to submitting that form unless another type is specified. If the control only opens help or changes part of the interface, use type="button":
<form action="/profile/save/" method="post"> <button type="button" id="preview">Preview</button> <button type="submit">Save profile</button></form>That is a more accurate model than using a link with href="#" and cancelling the navigation. The placeholder address can move the page to its top, add a fragment to the URL, and become useless when the script does not run. If there is no destination, it is not a link.
A Role Does Not Add Behaviour
There are cases where an existing interface cannot immediately be changed to use the correct element. WAI‑ARIA 1.0, which is currently a Candidate Recommendation, provides a button role which can communicate the intended meaning:
<div id="save" role="button" tabindex="0">Save changes</div>role="button" gives assistive technology useful semantic information, and tabindex="0" puts the element into the normal tab order. They do not make the div operate like a button. A developer still has to provide activation for mouse and keyboard users, manage focus where necessary, and implement any disabled state correctly.
In 2012‑compatible JavaScript, even a simplified substitute starts to become more involved:
var saveControl = document.getElementById('save');function saveChanges() { alert('Changes saved');}saveControl.onclick = saveChanges;saveControl.onkeydown = function (event) { event = event || window.event; var key = event.which || event.keyCode; if (key === 13 || key === 32) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } saveChanges(); }};The Enter and Space keys both need attention for a control claiming to be a button, and cancelling the default action prevents Space from scrolling the page during activation. This still represents only a small part of the work required by a more complicated widget. The WAI‑ARIA authoring practices available at the time are explicit that custom widgets have no inherent keyboard support.
ARIA is valuable when native HTML cannot express a widget, but it is not a shortcut around native HTML when button already describes the job exactly.
Do Not Nest One Control Inside Another
An interface can also become ambiguous when a large linked area contains another control. The HTML5 author guidance for the a element does not permit interactive content as a descendant, and the same content‑model restriction applies to button.
This means a button should not be placed inside a link to make part of a linked card perform another action. The browser then has two interactive elements competing for the same click and keyboard context. Keep the destinations and actions as separate sibling controls, each with its own accessible name.
Choose Semantics Before Wiring Events
The event handler should be the last part of the decision rather than the first. Ask whether the visitor is following a destination or operating the current page, choose a or button, and then add the behaviour the feature needs.
That small choice leaves the browser doing work it already knows how to do. It also leaves less custom JavaScript to test across keyboards, pointing devices, forms, and assistive technology.
Postscript
Aug 2026: This article forms part of an archive restored from a previous version of my website. Its original publication date is accurate. During the restoration, I reviewed and updated it where appropriate for formatting, imagery, broken links, code correctness, and current internal references, whilst preserving the original technical context and intent. HTML and ARIA have continued to develop, but the central recommendation has not changed: use a native link for navigation and a native button for an action whenever possible. Accessibility in Reusable Front‑End Components expands on the modern component‑level responsibilities which begin with that semantic choice.