Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anchor or Button in React SPA?

Say there is piece of text (regardless of whether it is styled as a "traditional" link or button) on a page that when clicked leads to a new page with a new page/URL. But the navigation happens programmatically (e.g. via react-router using the History web API) instead of a hard HTTP refresh.

In this case, should it be a traditional anchor link with href attribute such as # or a button?

Option 1:

<a href="#" onClick={navigateToNextPage}>Link</a>

The downside is that you have a junk href attribute. You could remove that but then it is not in the tab order and doesn't get default link styling (though these could be overcome with one-off styling). Also if you copy the link it will copy as # which is incorrect and be interpreted incorrectly by screen readers.

Option 2:

<button onClick={navigateToNextPage}>Link</a>

The downside here is that if you want it to look like a traditional link you need to apply custom styling. Also in some ways it is really acting like a traditional link in my view. But this would be better for screen readers.

like image 969
Adam Thompson Avatar asked Dec 23 '22 18:12

Adam Thompson


2 Answers

I can't comment on React but here is the correct method for SPAs, implementing it should be trivial in React.

Short Answer

Use a hyperlink (<a>) to navigate between pages and when loading additional content if no other option is available.

More simply: if the URL changes or you add large amounts of information to the page, use a hyperlink.

Long Answer

In your question you mentioned the History API and react-router.

For this reason I assume that the function navigateToNextPage changes the URL.

I also assume that I could access that page directly if I desired by entering that URL into my browser.

With those assumptions in mind you should use:-

<a href="new-page-url" onClick={navigateToNextPage}>Link</a>

Obviously you would stop the default action (e.preventDefault() or React equivalent).

A couple of points on why to use the format described above:-

  1. Accessibility - when I encounter a hyperlink with a screen reader I am able to ask my screen reader where that link will take me, this is reassuring, I can't do the same with a button. This is why I didn't use # for the hyperlink but instead added the actual destination. If you see href="#" it is nearly always a sign that the wrong element is being used or it is being used incorrectly. After reading your comments about performing an action before navigating this is still perfectly valid, perform your action and then redirect, it is still navigation at the end of the day.
  2. Accessibility - when I am navigating a site via a screen reader I may decide to cycle through all the hyperlinks on the page to get a feeling for the page structure. (NVDA modifier + K to get next link for example). I am very unlikely to loop through all the buttons on a page to look for navigation.
  3. Accessibility - If I encounter a link I expect the page to change (even via AJAX). If I encounter a button I expect it to perform an action on the current page. Expected behaviour is a key part of accessibility.
  4. Accessibility - hyperlinks have some important states. 'visited' is a key one on pages with lots of links as I may want to review something I read earlier and being able to navigate via visited links (e.g. NVDA modifier + K for all unvisited links). Buttons do not expose this information. An important point here is that you also can't style a button with button:visited in your CSS so you miss out on the visual clue for everybody there.
  5. Accessibility - Space key vs the Enter key. If I land on a link I am expecting to press space to navigate, a <button> only works with the Enter key and so I may be confused as to why the page isn't changing. (I am assuming at this point you have used a load of aria to convince me this button is a hyperlink).
  6. Robustness - If your site has limited functionality when JavaScript fails a hyperlink is far better than a button. It will still work when JavaScript fails and this is especially useful when a JavaScript failure may only be a temporary load problem with one page, allowing a user to get to another functioning page.
  7. SEO - I dare to speak of SEO on Stack Overflow? Shame! Shame! Shame! :-P - but seriously although Google is pretty darned smart in what it can do on JS powered sites it does still struggle to work out where a JavaScript only link will take it. If SEO matters for you then use a hyperlink with a valid destination so Google can map information correctly.

Probably other reasons I have forgotten to mention but I think I have made the point.

What do you have to consider when using AJAX to navigate between pages?

Although not part of your question I thought I would quickly add a couple of points for completeness.

You need to signal to a user that a page is loading if you are using a SPA pattern (and therefore interrupting normal navigation). e.g. I click your link you need to let me know that an action is being performed (loading.....) as you intercept the normal browser behaviour with e.preventDefault() or equivalent.

The simplest way is to use aria-live=assertive on a region that explains the page is loading. You can Google how to implement that correctly.

Additionally when the new page loads you need to manage focus.

The best way to do this is to add a level 1 heading (<h1>) to each page that has tabindex="-1".

Once the page loads the last action you perform in your JavaScript navigation function is to place the focus onto this heading.

This has two benefits:

  1. it lets the user know where they are now
  2. it also lets them know when the page load is complete (as AJAX navigation doesn't announce when the page is loaded in most screen readers).

By using tabindex="-1" it means that the heading won't be focusable by anything other than your JavaScript so won't interfere with the normal document flow.

like image 93
Graham Ritchie Avatar answered Jan 03 '23 09:01

Graham Ritchie


You can just do e.preventDefault() when clicked on <NavLink> do your thing. Then navigate. NavLink generates <a> tag in HTML and provide active class by default when route is active. This allow you to style active state link.

import React from "react";
import { NavLink, withRouter } from "react-router-dom";

function Header(props) {
  const handleClick = e => {
    e.preventDefault();
    console.log("DO SOMETHING");
    props.history.push(e.target.pathname);
  };
  return (
    <ul>
      <li>
        <NavLink exact to="/" onClick={handleClick}>
          Home
        </NavLink>
      </li>
      <li>
        <NavLink to="/about" onClick={handleClick}>
          About
        </NavLink>
      </li>
      <li>
        <NavLink to="/topics" onClick={handleClick}>
          NavLink
        </NavLink>
      </li>
    </ul>
  );
}

export const HeaderNav = withRouter(Header);

Wokring example: https://codesandbox.io/s/react-router-5-rqzqq

like image 34
MonteCristo Avatar answered Jan 03 '23 10:01

MonteCristo