Links search engines cannot follow
What is this issue?
The page has links that only work when JavaScript runs, and give a search engine no address to follow.
Search engines follow <a href="URL"> and nothing else. Two common patterns look
like links to a visitor but are not links to a crawler:
- A
javascript:href —<a href="javascript:openProduct(42)">. The href is a script, not an address. - An
onclickwith no real href —<a onclick="location.href='/pricing'">,<a href="#" onclick="go('/faq')">. The destination exists only inside the script.
For a page to pass this check:
- Every anchor that navigates somewhere has that destination in its
href.
An onclick beside a real href is fine — <a href="/pricing" onclick="track()"> —
because the href is what gets crawled.
Example: a product grid whose cards are <a href="javascript:void(0)" onclick="openProduct(42)">. Visitors reach every product; a search engine
reaches none of them from this page.
Why it matters
The destination may never be discovered. If a page is reachable only through links like these, a search engine has no route to it. It is not crawled, not indexed, and does not rank, however good it is.
No signal passes along the link. Even when the destination is found some other way, a script link passes no anchor text and no internal-linking signal to it. Your own site's vote for the page is lost.
It breaks ordinary browser behaviour. Visitors cannot open the link in a new tab, copy its address, or bookmark the destination. Middle-click does nothing.
It fails for anyone without the script. A slow connection, a blocked script, or an error elsewhere on the page leaves the link dead.
AI search and answer engines follow the same links. Assistants that read a site by following its links skip these too.
Effect on the health score
This is an important-severity issue in the Link Integrity lens. It is raised once per page, however many such links the page carries.
How to fix it
Put the destination in the href. Keep the script if it does something extra (analytics, an animation), but let the link stand on its own:
<!-- Before --> <a href="javascript:void(0)" onclick="openProduct(42)">Blue kettle</a> <a onclick="location.href='/pricing'">Pricing</a> <!-- After --> <a href="/products/blue-kettle" onclick="openProduct(42)">Blue kettle</a> <a href="/pricing">Pricing</a>In a single-page app, render real hrefs. Router link components (
<Link>,<router-link>,<NuxtLink>) output<a href="/path">and intercept the click for you. Use them instead of a click handler on a bare<a>.If it is not navigation, use a
<button>. Opening a menu, a modal or a tab is an action, not a link.<button type="button">is the correct element and is not counted here.Fix the template, not each page. When every page reports the same few anchors, they are in the header, footer or a shared component.
Examples
Example 1: A link with a script beside it
Passes because: the href is a real address. The click handler runs for visitors and a crawler follows the href.
<a href="/pricing" onclick="track('pricing')">Pricing</a>Example 2: A script pseudo-link
Fails because: javascript: is not an address.
<a href="javascript:openProduct(42)">Blue kettle</a>Corrected version:
<a href="/products/blue-kettle">Blue kettle</a>Example 3: A click handler and nothing to follow
Fails because: the destination exists only inside the script.
<a href="#" onclick="window.location='/faq'">FAQ</a>
<a onclick="router.push('/contact')">Contact</a>Corrected version:
<a href="/faq">FAQ</a>
<a href="/contact">Contact</a>Example 4: A control that is not a link
Not reported, and correct: opening a menu is an action, written as a button.
<button type="button" onclick="toggleMenu()">Menu</button>How PixyScan detects this
Reads every
<a>element on the page — not only the ones with an href, so that an anchor with just anonclickis seen at all.Reports a
javascript:href, in any letter case and with or without anonclickbeside it. Such links are counted here and nowhere else: they are not stored as external links.Reports an
onclickanchor with nothing to follow — no href, an empty href, or a barehref="#".Leaves alone:
- an
onclickbeside a real href (the href is what is crawled); - an
<a>with neither href nor onclick, such as a named target (<a name="section-2">); href="#"without an onclick.
- an
Raises one finding per page with the total, the number of each kind (
javascriptHrefCount,onclickOnlyCount) and up to five examples, each with its href, itsonclickcode and its visible text, so you can find the anchor in your template.
Links inside the page are read from the HTML the server sent. Navigation that a
script attaches with addEventListener leaves no trace in the HTML and cannot be
seen.
What we store
Storage Level
Page Level
Database Table / Prisma Model
audit_issues.details
These anchors have no address, so they are not stored in the link tables.
Stored Fields
| Field | Type | Description |
|---|---|---|
| message | String | "Found N link(s) that only work through JavaScript…" |
| uncrawlableLinkCount | Int | All such anchors on the page |
| javascriptHrefCount | Int | Of those, how many have a javascript: href |
| onclickOnlyCount | Int | Of those, how many have an onclick and no followable href |
| samples | Array | Up to five: { reason, href, onclick?, anchorText }. reason is javascript-href or onclick-without-href; href is null when the anchor had none; onclick and href are cut at 200 characters |
Detection Dependencies
- HTML Document — every
<a>element and itshref/onclickattributes