A resource menu can respond to a click while exposing no anchor destination. Record the element and its actual href before counting it as a web-address reference.
This small classifier operates on DOM attributes. It does not click anything or make requests:
function inspectTarget(tagName, href, base) {
if (tagName !== 'A') return { kind: 'non-anchor-control' };
if (href === null) return { kind: 'missing-href' };
if (href.trim() === '') return { kind: 'empty-href' };
try {
const url = new URL(href, base);
if (!['http:', 'https:'].includes(url.protocol)) {
return { kind: 'non-web-scheme', scheme: url.protocol };
}
return { kind: 'web-address', href: url.href };
} catch {
return { kind: 'invalid-url' };
}
}
const scope = document.querySelector('.resource-menu');
const audit = scope ? [...scope.querySelectorAll('a, button, [role="link"]')]
.map(el => inspectTarget(el.tagName, el.getAttribute('href'), document.baseURI)) : [];
A button or a span with role="link" remains a non-anchor control. That may be correct for an action; a resource-navigation card should expose an appropriate destination. mailto: is a valid navigation scheme, but it is outside this example's HTTP/HTTPS inventory. An empty href is recorded separately rather than silently counted as a new resource.
JavaScript-inserted <a href> elements are still anchors. Google's crawlable-link guidance makes this distinction; “dynamic” alone is not a reason to reject a link.
Eleven local pure-function fixtures passed: button, span, missing/empty/blank href, malformed URL, relative URL, same-page fragment, absolute HTTPS, mailto and JavaScript scheme. The relative-URL fixture retains its query string and fragment after resolution. This does not test a browser adapter, fetch a destination, validate security, inspect robots/canonical settings, or prove crawling/indexing. A same-page fragment also does not establish a second content page.
Author context: I build AGProLabs. This original note was prepared with AI assistance; the fixture check is local evidence for the classifier only.
Top comments (0)