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:-
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.
- 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:
- it lets the user know where they are now
- 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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…