Anchor or Button in React SPA? - javascript

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.
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.

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

I would recommend you to use semantic HTML. This means using the correct HTML elements for their intended purpose as much as possible.
Easier to develop with — you can get some functionality for free, plus it is arguably easier to understand.
Better on mobile — semantic HTML is arguably lighter in file size than non-semantic spaghetti code, and easier to make responsive.
Good for SEO — search engines give more importance to keywords inside headings, links, etc., than keywords included in non-semantic
s, etc., so your documents will be more findable by customers.
There's more details

Related

"Back to Top" link accessibility

I am working on a web page where user has to scroll a lot.
For ease of use, I have added the back-to-top link at the bottom of the page, so user can navigate to the top of the page.
Now I have a html markup like this:
<a class="btn-primary" href="#top">
<span class="btn-content link-content">
<svg focusable="false"><use xlink:href="#arrow-upward"></use></svg>
</span>
</a>
How can I make my link more accessible? I have thought of adding aria-label, but other than that nothing crosses my mind
First of all, render your component usable, for example by following NNg’s Back-to-Top Button Design Guidelines. This is a solid basis. Thanks to #outis for the hint.
Here are some accessibility concerns that come to mind with regards to such a Back to top component.
Alternative text/accessible name
Every interactive element needs an accessible name. This text is not only what gets read by a screen reader when focussing the element, but also how voice control can identify the button, as in “Click on back to top”.
Accompanying text for sighted users
Adding a tooltip for sighted users via the title attribute is always a good idea as well, but you should never rely on that attribute to provide an accessible name, as implementation in screen readers is not good.
Even better would be having the text always visible for everyone. Not everyone gets icons correctly.
That way you also provide a very visible hint to voice command users.
The visible part needs to be included in the accessible name (alt text) for this reason. You should add role="presentation" to the SVG if you have accessible text next to it.
This is an example from the GOV.UK Design System Guidelines, who care a great deal about accessibility. They add it just before the footer on long pages.
Contrast
Since you didn’t share your CSS, we don’t know what it does with colours. You need to make sure that the icon and text’s contrast with the background are still 4.5:1 or above.
Avoid icon-fonts
You already got that right. (:
Since users with reading disorders often make their browser use a font they can read well, icon-fonts will break and remove icons that—ironically—would help them most.
Be careful with animated scroll
Often developers (or designers?) like to fly back to the top instead of the instant jump. This can help users orientate.
Nowadays we can simply specify this with scroll-behaviour: smooth and let the browser do the work.
However you implement this, you should be careful to not cause vertigo in people who are sensitive to these zoom animations. Only apply this if the user can and did not set prefers-reduced-motion
Focus
While it is important to include the button in the tab order, it should probably come right before the footer, even when it’s visible all the time.
Focus needs to be visible, of course, as for all interactive elements.
Keyboard users can easily jump back to top by using their Home key, so it’s less important to have the button in early in focus order or after each chapter or something.
When the button disappears when arriving on top, focus needs to be put somewhere else, it must not be “lost” (going to body or html).
Refer to the WCAG
I might have missed some criteria in this answer.
If you want to be sure, you can open up the WCAG-EM Report Tool and start creating a report for your component.
All criteria of the Web Content Accessibility Criteria will need to be audited by you. This is the industry standard for accessible web applications, and legally binding in a lot of states.
You can also add the Title attribute in your anchor tag
<a class="btn-primary" href="#top" title="go to top of page">
When working on the in-page navigation links, we should not forget the screen reader users, so we must ensure they know the purpose of the link. A common way is an aria-label attribute specifying a meaningful value, such as 'Go to top' or 'Back to top'. Once the aria-label is specified, the content of the link should be hidden from the screen reader (Exceptions exist) i.e.: via aria-hidden='true' .
Another detail that should not be forgotten is the focus handling. By default, most of the browsers just scroll the page when using an #Element's_ID for the href if the id does not exist, and SR/Keyboard users will be affected in such a way that after the page is scrolled to top and Tab key is pressed the focus will move to the next focusable element before the page was scrolled up, so, the result will be, an undesired page scroll down.
There is a working implementation in W3C patterns page https://www.w3.org/WAI/ARIA/apg/patterns/.
Hope this helps in your accessibility journey. The a11y world needs lot of people interested on it.
<body id="top">
...
<a class="btn-primary" aria-label="Back to top" href="#top">...
</a>
...
</body>

HTML Semantics - Button acting as an anchor

I'm mainly interested in the a11y aspects
So as you might be aware, sometimes you might wish to have a button that acts as an anchor.
These are 4 ways (I can think of) of approaching this issue:
1. Button inside an anchor element
<button>Button</button>
2. Anchor inside button element
<button>Button</button>
<!-- Also should it be a href or button onclick (?) -->
3. Anchor styled as a button (without a button element)
<a class="buttonLike" href="//redirection">Button</a>
4. Button acting as a redirection (without an anchor element):
const button = document.getElementById('button')
button.addEventListener('click', () => {
window.location.href = "//redirection"
})
<button id="button">Button</button>
I've tried to find an answer to this myself. Closest I could find was this excerpt:
According to the MDN anchor spec, which states the following:
Anchor elements are often abused as fake buttons by setting their href to # or javascript:void(0) to prevent the page from refreshing, then listening for their click events .
These bogus href values cause unexpected behavior when copying/dragging links, opening links in a new tab/window, bookmarking, or when JavaScript is loading, errors, or is disabled. They also convey incorrect semantics to assistive technologies, like screen readers.
Use a <button> instead. In general, you should only use a hyperlink for navigation to a real URL.
Unfortunately this doesn't help me too much. Basically all it states is you should not use the third approach (anchor styled as a button) if you don't mean to provide a real link to it, which is not what this question is about.
Is there any official WCAG on this subject matter that I was unable to find or?
Option 1 is not valid HTML.
Option 2 is not valid HTML.
Option 3 is the correct choice here.
Option 4 is semantically incorrect.
Semantics are one of if not the most important aspects of accessibility.
There are two things at play which dictate option 3.
The first is that an anchor should be used only to jump to sections and to navigate between pages.
The second is that a button should perform actions on the same page.
Now if you want to style a call to action link to look like a button that is fine but make sure you use the correct semantics, but make sure that CTA leads to a new page or it isn't semantically correct.
And although it is swearing on StackOverflow to mention SEO, a hyperlink rather than a JavaScript redirection will be far better.
The first and second rules of ARIA say:
1st rule : If you can use a native HTML element [...] then do so
2nd rule : Do not change native semantics, unless you really have to.
Interactive elements like a and button can't contain other interactive elements:
The a element may be wrapped around entire paragraphs, lists, tables, and so forth, even entire sections, so long as there is no interactive content within (e.g. buttons or other links).
So, as what you want to do is linking to a page, your third solution is obviously the only one correct.
I think you might have confused the "bogus" stagement which refers to your 4th example.
From my little experience with Accessibility and semantics there is no "one size fits all". It really depends on your priorities and the user experience you are aiming for.
A <button> gets all the accessibility goodies from the browser automatically: Being selected or pressed using the tab or spacebar/enter keys.
A <a> element is a link, links are meant to be used as links or anchors within a page.
Anchors are not as important in comparison to a button within a page. From a user experience point of view; a button is used by people to interact with a UI, either to confirm or make the UI do something. Pressing a button provides a different feedback compared to a link. Anchor links on the other hand help a user with finding content within a page.
Again, it really depends on what you are trying to do:
Is this a terms page or an article? Then list your anchor links without any button-like styling
Does this a link that has to look as a button so users find it easier to spot or interact? Then style it as a button without it being actually a <button>.

Accessibility - Javascript only links with href="#" an antipattern?

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:
using hash vs javascript:void(0)
Which "href" value should I use for JavaScript links, "#" or "javascript:void(0)"?
other workarounds
https://weblog.west-wind.com/posts/2019/Jan/21/NonNavigating-Links-for-JavaScript-Handling
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="#"
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>
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.

Create multi page design with single URL

My goal is to create multi screens in one single page.Depending upon the action the user will be able to navigate from one screen to another screen.I have shared the images below
When the user clicks on any of the categories ,it will navigate to a second screen.
While clicking back it will again comeback to the first screen without change in URL.I have tried creating a full page modal and could not achieve this kind of functionality.I am not sure whether it should be done as a modal with multiple screens.
Please suggest me any method I can achieve this.
What you are likely referring to is creating an SPA or Single Page Application. This can be done through 'Vanilla' JavaScript at great effort or via one of many JavaScript Libraries or Frameworks.
Reactjs, Angular and Vuejs are probably the most common.
IF you were to use Reactjs then you could use what's called React Router. React Router would do what you want to do very easily. Doing it in Vanilla JavaScript would require a great deal of work or it would be very ugly.
However you did ask, so one way of doing would be to use JavaScript to load an iFrame or to make a top level parent element display: none and another to then display:...
Also if you are thinking of something less hacky, but not something as sophisticated as React or it's peers, then check this link out for a relevant article. Perhaps it's a path forward that you would prefer.
https://dev.to/rishavs/making-a-single-page-app-in-ye-good-olde-js-es6-3eng
To help rookies like me, you can make a single page app or SPA, or a dynamic page that updates based on user actions with a single URL, in vanilla Javascript. You don't have to use a framework.
There are 3 concepts you need to understand:
The server doesn't see past the # in the URL
You need to tell your code what screen you want to display. Normally you would have URL.com/page-you-are-on and click a link to go to URL.com/page-you-want
However, in a single page app, you don't go to different URLs. So how does it work? You use a fragment identifier or a pound symbol. #
The # in the URL doesn't get recognized by the server. So URL.com/page#page1 and URL.com/page#page2 to the server is the exact same URL.com/page.
So you can use the URL to indicate to the server what page you want, in your single page app.
A Router can decide what to show based on the # URL fragment
So your page loads at URL.com/page#page-you-want. You need to inspect the URL and get the piece past the #. You inspect the URL, and split it on the #. That means you get page-you-want. Your code then uses that to decide what content to display. The function or file that does this is commonly called a router because it routes to the file or function you want displayed.
Once you know what to show, dynamically update the DOM
This is where the magic happens. Your website looks at the URL, gets everything past the #, sends it to function that decides what to display. You now need to display it.
The DOM has lots of functions and methods that help it update and create various things. It could be as simple as this:
function displayPageAbout() {
// the router calls this if the URL is URL.com/page#about
let pageSection = document.getElementById('pageSection') //this is where the page will be displayed
//create the div and give it content
let page = document.createElement('div');
page.textContent = 'This is the About Page'
//add the div to the spot on the page where the content should go
pageSection.appendChild(page);
}
That is basically it.
If found these two examples and tutorials useful in understanding what it is, and how it could work.
https://blog.jeremylikness.com/blog/build-a-spa-site-with-vanillajs/
https://dev.to/rishavs/making-a-single-page-app-in-ye-good-olde-js-es6-3eng
Good luck!

AngularJs is preventing link to be clicked twice

I'm using AngularJS not to build SPA but just for certain features in the system. I'm having a trouble to find out why Angular is preventing page from being refreshed after it get's there or how to disable/workaround it.
So I have simple menu:
<ul>
<li>Homepage</li>
<li>Profile</li>
</ul>
And when I'm already on the http://example.com/admin page and I'm clicking in first link (/admin) then nothing happens.
I could detect if a certain a element has a href and then use window.location but unfortunately I'm using AJAX in some other parts of the system so that won't fit.
Please advise.
You're working with a Single Page Application. The router takes care of navigating between different application routes, but the browser never fetches an entirely new page.
When you click on a link for a page that you're already on, there is no where to navigate to.
EDIT: You could write a directive that will detect clicks on anchor tags, and if the route is the same, it could trigger a page refresh. That will give you the behaviour you're looking for. However, I struggle to where this would be desirable.
EDIT 2: This is what stops your navigation: https://github.com/angular/angular.js/blob/v1.5.x/src/ng/location.js#L889.
As far as I can see, there is nothing you can do to disable that behaviour, or if there is, it will be an uglier and more fragile solution that creating your own directive that will trigger a page refresh.
you need to be using ng-href and http://plnkr.co/edit/stdkjN?p=preview

Categories