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.
I can't comment on React but here is the correct method for SPAs, implementing it should be trivial in React.
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.
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:-
#
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.
button:visited
in your CSS so you miss out on the visual clue for everybody there.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).Probably other reasons I have forgotten to mention but I think I have made the point.
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:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With