Is there an accessible way of telling that some part of UI can redirect to other page if some asynchronous condition is resolved after clicking link?

Viewed 561

Let's say I have the following component

function ClickButton(props) {
  const history = useHistory();

  const onClick = () => {
    history.push('/next-page');
  };

  return <button onClick={onClick}>Go to next page</button>;
}

More accessible version would be to use Link from react-router as the following

function ClickButton(props) {
  return (
    <Link to="/next-page">
      <span>Go to next page</span>
    </Link>
  );
}

But what if redirection depends on some http call success, like

function ClickButton(props) {
  const history = useHistory();

  const onClick = async () => {
    try {
      await axios.get('https://google.com')
      history.push('/next-page');
    } catch(err) {
      setAlertMessage('Google can't be reached - check your internet connection');
    }
  };

  return <button onClick={onClick}>Go to next page</button>;
}

What is equivalent (and is there some semantically meaningful way) of using Link (or something else that e.g. screen readers would treat as link-alike) here, in case of async handler of onClick event?

2 Answers

I'd probably opt to use the Link component since semantically it makes the most sense to. You can use button element, but then you would need to add all the appropriate accessibility attributes, i.e. role="link" and correct click and keypress event handlers, etc...

You can add an onClick handler to the Link and do the same check. The key is preventing initially the default link behavior.

const ClickButton = props => {
  const history = useHistory();

  const onClick = async e => {
    e.preventDefault();
    try {
      await axios.get("https://google.com"); // NOTE *
      history.push("/next-page");
    } catch (err) {
      alert("Google can't be reached - check your internet connection");
    }
  };

  return (
    <Link to="/next-page" onClick={onClick}>
      <span>Go to next page</span>
    </Link>
  );
};

Edit link async check GET request

* NOTE: this GET request seems to just fail in codesandbox.

The simplest way to do this is with a hyperlink. (<link>) as @Drew Reese pointed out.

There is nothing wrong with intercepting the hyperlink action via JavaScript and reporting back if there is an error.

To do this you would just use a standard e.preventDefault(), make your AJAX call and redirect if it works. If it fails then create an alert that explains that the call failed.

Why use an anchor / hyperlink?

  1. Semantics - accessibility is all about expected behaviour. If I change page then a hyperlink makes a lot more sense than a button. It also means I understand that the page will change from the element in case your description does not make sense.
  2. JS Fallback - it could be your SPA does not function at all without JS. However if it does work in some limited fashion without JS then using a hyperlink means that if a user has JS disabled (around 2% of screen reader users don't use JS) or an error causes your JS to fail then navigation is still possible with a hyperlink.
  3. SEO - I dare to swear on Stack Overflow and mention the forbidden term! Although search engines are very good at understanding JS they still aren't perfect. They understand hyperlinks perfectly. If SEO matters use a hyperlink.
  4. Styling - hyperlinks have :visited and :active whereas buttons do not.

How to handle async loading.

When you click on the element you need to let the screen reader know an action is being performed.

The easiest way to do this is to have a visually hidden div located on the page that has the aria-live="polite" attribute. (You can use the same div for all actions).

Then when a function is called that contains an async action you set the contents of that div to "loading" or "waiting" (whatever is appropriate).

This will then get announced in the screen reader (you could argue aria-live="assertive" is better but I will leave that to you).

If the action fails then set your alert message (but make sure that your alert has the appropriate WAI-ARIA roles and behaviour. I would suggest role="alert" but without seeing your application that is a best guess. It could equally be role="alertdialog" if you want a confirmation from the user.

Final thought

When navigation occurs in a AJAX powered SPA, don't forget to set the focus to something on the new page. I normally recommend adding a <h1> with tabindex="-1" (so you can focus it) and setting the focus on that (as your <h1> should obviously explain what the page is about).

Related