2

I have a react SPA where I have some links which require javascript to change the page url. Semantically these should be links, but the link destination is computed at runtime on click, or it requires javascript to move to that location (history api)

I have seen it mentioned at a few places that href="#" is an antipattern or something that should be avoided. For example this article from webaim https://webaim.org/techniques/hypertext/ mentions that:

One of the most serious barriers is to create links that go nowhere. Developers sometimes use JavaScript to create dynamic menus that drop down when the user hovers over certain links with the mouse. In some cases, the link itself goes nowhere at all, and its only purpose is to expose the links in the drop-down menu, which do have real destinations. Links like this often have a pound sign as the link destination, which means that the link destination is the same page; clicking on the link accomplishes nothing. Both keyboard users and mouse users will experience nothing at all when attempting to activate the link.

and it gives an example of href="#"

Now i get that using href="#" for links which do nothing or do not take the user anywhere might semantically be incorrect. In my usage though, the link does have a destination, just that the destination can't be specified in href and may not be decided unless the link is clicked. So my usage is to have href="#" and e.preventDefault() onClick, so that it does not go to top of the page and create another history entry.

I have seen other working solutions/workarounds for this:

But i would like to understand, if using href="#" for links that do have a destination (which is figured out after clicking on the link, or applied using javascript) is an antipattern.

This is a follow up to this question related to javascript only links accessibility Accessibility: Using javascript only links with href="#"

gaurav5430
  • 12,934
  • 6
  • 54
  • 111
  • 4
    If the href doesn't matter, I don't see how they're "semantically links". Making them ` – Pointy Aug 17 '20 at 13:28
  • Personally as a user, my biggest problem with links that use `#` or `javascript:void()` or otherwise something that leads nowhere is the fact that opening them in a new tab is useless. I frequently do a middle-click to open a bunch of links and then go through them. If that just opens nothing or the same page without even linking me to a specific section, then it's immensely frustrating. Now I have to go through the page *again* and maybe others to find what I wanted to see and then click on those one by one then back then the next one. It's not good UX for me. – VLAZ Aug 17 '20 at 13:32
  • Use links for navigation (the user should be able to open links in new tabs, copy and distribute them). Use buttons for actions/commands. Personally I'd say that href="#" *is* an antipattern. – Emaro Aug 17 '20 at 14:02
  • @Pointy the link has a destination just that it isn't specified in the href attribute. On click it actually changes the url to another page, isn't that semantically a link then? Or for that matter the href might be determined programmatically after some action, till that time shouldn't they be rendered as links? – gaurav5430 Aug 17 '20 at 15:21
  • You could compute and update the `href` value on "mouseenter" instead of "click". *edit* and "focus" I guess in case the element is tabbed into via the keyboard. Probably need a touch/pointer solution too. Just a thought. HTML was originally designed in a world that did not include JavaScript, so the semantics of our time represent a significantly different scene. – Pointy Aug 17 '20 at 15:28
  • @Pointy in some cases, I am using the React router to pass along some state to the destination. In this case, even though i know the destination, I can't use href because I have to pass on some extra state using router. And hence I can't get the href on mouseenter – gaurav5430 Aug 18 '20 at 05:59

2 Answers2

1

If you are using a screen reader software, e.g., NVDA on Windows (Browser mode, specifically), it could intercept certain keys (e.g., Space or Enter key) to a <a> tag and bind certain behavior to that, meaning that it bypasses the Javascript code that generates dynamic URLs). So your app could be broken for NVDA users.

You can actually try testing your app on NVDA / JAWS on Windows, and see if <a href="#" /> + Javascript works.

If you are building a dropdown menus, generally, it's hard to implement make it 100% accessible for hover-and-expand menus. We should avoid using this pattern if possible. You are better off using other designs, e.g., table of content.

Machavity
  • 30,841
  • 27
  • 92
  • 100
Junji Zhi
  • 1,382
  • 1
  • 14
  • 22
1

If it navigates to a new URL, the href property should be set to that new URL. You can then use event.preventDefault() to prevent the page from reloading at the new location.

// mock out history for demo purposes
const history = {
  pushState: (_data, _title, newUrl) => {
    alert(`navigated to ${newUrl}!`)
  }
}

document.querySelector('#spa-link').addEventListener('click', e => {
  e.preventDefault()
  
  history.pushState({}, '', '/new/path')
})
<a id="spa-link" href="/new/path">Go to new path</a>
Lionel Rowe
  • 5,164
  • 1
  • 14
  • 27