JavaScript turned static documents into software. Single-page apps feel instant. No full page reloads, no flickering white screens. But that speed comes with a cost that many teams ignore: the basic machinery of the web starts to rot. Navigation becomes brittle. Search engines struggle to follow paths. Screen readers get lost. And users find themselves trapped in interfaces that look like websites but behave like broken desktop applications.
The culprit is usually a div with an onClick handler.
Stop Using Divs as Links
A div carries no semantic meaning. It is a box. When you strap a click handler onto it and use it to route users to a new view, you are asking the browser to treat a cardboard box like a door. The browser refuses. So does every tool built around the browser.
Screen readers do not announce a div as a link or a button. They skip over it, or they read it as plain text. A user navigating by voice cannot target it. A search engine crawler, scanning your page for discoverable URLs, sees nothing to follow. Your route might as well not exist.
Worse, you lose the behaviors users already know. A real link lets someone right-click to open in a new tab, bookmark the destination, or copy the address to share. Keyboard users expect to hit Tab to reach it and Enter to open it. A div offers none of this. Even if you bolt on tabIndex and role="link" and keyboard listeners, you are rebuilding, poorly, what the browser gives you for free. And you will forget an edge case. You always do.
Use Anchors for Destinations, Buttons for Actions
HTML already solved this. The confusion starts because both elements look clickable, so developers treat them as interchangeable. They are not.
Use an <a> tag when you want to move the user to a new URL. Not a simulated view swap, not a state change, but an actual location. The href attribute should contain a real address:
<a href="/docs">Documentation</a>
That is it. If the user is going somewhere, use a link.
Use a <button> when something happens on the current page. Buttons are for actions such as:
- Opening a modal
- Submitting a form
- Saving settings
- Toggling a menu
Links are for destinations. Buttons are for actions. Mixing the two muddles your interface and breaks user expectations.
Let the Browser Do Its Job
Modern browsers are the result of decades of evolution and standardization. They handle security, history, prefetching, and accessibility better than your hand-rolled JavaScript ever will.
A genuine anchor tag automatically feeds the browser history stack. It works with the native context menu. It participates in the browser's built-in prefetching algorithms when the user hovers or focuses it, making your app feel faster without you writing a line of code. It respects the user's preferences for opening links. It cooperates with password managers, with translation tools, with reader modes.
When you replace that with a JavaScript navigation function, you opt out of all of it. You are not just losing features; you are forcing users to abandon habits they have built across every other site on the internet. That is not a technical decision. It is a hostile user experience.
Check What Your Framework Actually Renders
React Router, Vue Router, Next.js Link components, SvelteKit. These tools make client-side routing feel effortless. But abstraction breeds mistakes.
Inspect your DOM. Open the browser's developer tools and look at the elements your framework emits. A <Link> component should render as a real <a> tag with a valid href attribute in the final HTML. If it renders as a span, a div, or anything else without a proper href, your abstraction has failed you. Fix the component. Override the default. Use the passHref prop or the framework equivalent. Do not trust the framework to get it right without verification.
This matters for hydration mismatches too. If the server renders a link and the client hydrates it into a non-link, you create accessibility bugs that are hard to trace because the HTML looks correct in your source code but wrong in the live DOM.
Ban Fake Destinations
There is a pattern that refuses to die: href="javascript:void(0)". Developers use it when they want something that looks like a link but acts like a button, usually because they do not want to style a button or because an older codebase demands it.
Arrêtez. Ce n'est pas une URL. Elle n'offre aucune destination au navigateur. Elle pollue la pile d'historique avec des états inutilisables. Elle casse l'historique du navigateur et l'accessibilité. C'est un piège. Si vous avez besoin d'un comportement de clic sans navigation, vous avez besoin d'un <button>. Donnez-lui le style que vous voulez. Le CSS se moque de savoir si l'élément est un bouton ou un lien. Vos utilisateurs, eux, s'en soucient.
Rédigez un texte qui explique où l'utilisateur se rend
Les mots contenus dans votre lien sont importants. Les utilisateurs de lecteurs d'écran affichent souvent une liste de tous les liens de la page pour parcourir rapidement le contenu. Si tous vos liens indiquent « Read more » ou « Click here », cette liste devient un bruit inutile.
Soyez spécifique. Comparez ceci :
- Mauvais :
<a href="/security/api-guide">Read more</a> - Bon :
<a href="/security/api-guide">Read the API security guide</a>
Le second indique précisément à l'utilisateur ce qu'il va trouver. Il donne du contexte aux moteurs de recherche concernant la page de destination. Il rend votre liste de liens navigable. Un texte de lien descriptif est l'un des gains d'accessibilité les plus simples et les moins coûteux que vous puissiez obtenir.
Testez sérieusement
L'architecture ne signifie rien si vous ne la vérifiez pas.
Premièrement, testez votre navigation au clavier. Débranchez votre souris. Parcourez chaque élément interactif de votre site avec la touche Tab. Chaque lien véritable doit afficher un contour de focus visible ; pas une lueur subtile qui disparaît sur votre arrière-plan, mais un anneau clair qu'un œil fatigué peut repérer. Appuyez sur Entrée. Cela doit activer le lien. Si Tab saute un élément, ou si Entrée ne fait rien, vous avez un bug.
Deuxièmement, testez vos routes au niveau du serveur. Le routage côté client n'est qu'un mince vernis. Si un utilisateur met /dashboard/reports en favori et revient le lendemain, ou s'il actualise la page, votre serveur doit savoir comment servir cette page. Configurez votre reverse proxy ou votre framework serveur pour rediriger vers votre application shell en cas de chemins inconnus, ou pour servir directement le bon HTML. Une erreur 404 lors d'un rafraîchissement n'est pas un bug mineur. C'est une promesse rompue.
JavaScript est une couche puissante
