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.
Stop. To nie jest URL. Nie wskazuje przeglądarce żadnego celu. Zanieczyszcza stos historii nieużytecznymi stanami. Psuje historię przeglądarki i dostępność. To pułapka. Jeśli potrzebujesz zachowania kliknięcia bez nawigacji, potrzebujesz elementu <button>. Stylizuj go tak, jak chcesz. CSS nie ma znaczenia, czy element jest przyciskiem, czy linkiem. Twoim użytkownikom zależy.
Pisz tekst, który wyjaśnia, dokąd zmierza użytkownik
Słowa wewnątrz linku mają znaczenie. Użytkownicy czytników ekranu często wywołują listę wszystkich linków na stronie, aby szybko je przejrzeć. Jeśli wszystkie Twoje linki brzmią „Czytaj więcej” lub „Kliknij tutaj”, lista ta staje się bezużytecznym szumem.
Bądź konkretny. Porównaj te przykłady:
- Źle:
<a href="/security/api-guide">Czytaj więcej</a> - Dobrze:
<a href="/security/api-guide">Przeczytaj przewodnik po bezpieczeństwie API</a>
Drugi przykład mówi użytkownikowi dokładnie, co znajdzie. Daje wyszukiwarkom kontekst dotyczący strony docelowej. Sprawia, że lista linków staje się nawigowalna. Opisowy tekst linku to jeden z najtańszych sposobów na poprawę dostępności.
Testuj rzetelnie
Architektura nie ma znaczenia, jeśli jej nie zweryfikujesz.
Po pierwsze, przetestuj przepływ za pomocą klawiatury. Odłącz myszkę. Przejdź klawiszem Tab przez każdy interaktywny element na swojej stronie. Każdy prawdziwy link musi wyświetlać widoczną obwódkę fokusu – nie subtelny blask, który znika na tle strony, ale wyraźny pierścień, który dostrzeże nawet zmęczone oko. Naciśnij Enter. Powinien on aktywować link. Jeśli Tab pomija element lub Enter nie robi nic, masz błąd.
Po drugie, przetestuj swoje trasy na poziomie serwera. Routing po stronie klienta to tylko cienka powłoka. Jeśli użytkownik doda /dashboard/reports do zakładek i wróci jutro lub odświeży stronę, Twój serwer musi wiedzieć, jak obsłużyć tę stronę. Skonfiguruj swój reverse proxy lub framework serwerowy tak, aby w przypadku nieznanych ścieżek przełączał się na shell aplikacji lub bezpośrednio serwował poprawny kod HTML. Błąd 404 przy odświeżeniu to nie jest drobny błąd. To złamana obietnica.
JavaScript to potężna warstwa
