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.
Related
I find myself having to interact with a web page that hides state in various places so that one cannot easily share it as a URL, for example this page which allows users to look up information from city zoning applications:
https://aca.cityofberkeley.info/community/Default.aspx
You can interact with the page all you want, but the URL in the location bar will remain the same as the above.
Currently, city staff provide users with instructions like "Load this URL, click on the 'Zoning' tab, enter DRCP2020-0010 under the 'Permit Number' field, click 'Search', then when the records come up, click 'Record Info' and then select 'Attachments' from the dropdown menu, then click on the PDF document that says '2020-10-21_DRCP_APP_PCKT_2801 Adeline.pdf'". I would like to be able to replace these instructions with a URL.
Another example is the website where video from city council meetings is archived:
http://berkeley.granicus.com/MediaPlayer.php?publish_id=cbebb4e6-5b83-11eb-920e-0050569183fa
It would be nice to be able to produce a link which brings up one of the meeting videos, and seeks to a certain timestamp like 53:40, so that I can refer to something specific that was said at a meeting.
Looking at the pages that are loaded when I follow the instructions in each case, I can see that there are some POST forms, cookies, hidden input fields, and so on.
Is there some kind of tool that I can use to create "deep links" to pages like these, that were generated using non-URL hidden state, which will allow me to quickly share what I'm looking at with another user?
What I'm seeking is similar to the frmget "bookmarklet", which changes the forms on a page to use GET instead of POST. Sometimes this succeeds in producing a URL which captures form submission query parameters. However, it doesn't work for these applications, for whatever reason.
This question is possibly related to the idea of capturing a web page's DOM state using "browser screenshots" and a script called html2canvas. A possible solution might involve getting and setting cookies in a bookmarklet. Ideally something that produces a normal "https://" URL would be ideal, but if it is impossible to solve the problem except by outputting a "javascript:" URL (bookmarklet) then that is acceptable to me (in spite of the security implications). Thanks.
That seems like not a programming matter. It seems like the site has some security issues as well.
QUESTION A: About Zoning
Here are some links you can use
Direct link to Zoning (I've found it via Advanced search from the site):
https://aca.cityofberkeley.info/CitizenAccess/Cap/CapHome.aspx?module=Planning&TabName=Planning&TabList=Home%7C0%7CBuilding%7C1%7CHousing%7C2%7CPlanning%7C3%7CFire%7C4%7CLicenses%7C5%7CPublicWorks%7C6%7CCurrentTabIndex%7C3
A strange link to the list of files (I've found it via downloading a file, then going to chrome://downloads, then right-clicking the file I've download. The link has been the following):
https://aca.cityofberkeley.info/CitizenAccess/FileUpload/AttachmentsList.aspx?iframeid=ctl00_PlaceHolderMain_attachmentEdit&module=Planning&isInConfirm=False&isdetail=True&isaccountmanager=False&isAdmin=True&isPeopleDocument=&agencyCode=BERKELEY&isForConditionDocument=N
It still doesn't give the direct link to the file, but it it gives the list of attachement of the previously opened Zoning record.
Currently I have no idea what file is triggered by javascipt:__doPostBack('attachmentList$gdvAttachmentList$ctl02$lnkFileName','').
In any case, based on what we have, step one, and then step two seems like minimize the path to download the file. I guess there could be a way to download the file directly, but I currently don't see any easy way. Maybe someone else could figure it out.
QUESTION B: About video
I've used an embed link that shows all the attributes that can be used.
There is a pretty strange but working way to give the exact timestamp. Change starttime from the link below:
https://berkeley.granicus.com/MediaPlayer.php?publish_id=cbebb4e6-5b83-11eb-920e-0050569183fa&starttime=0&stoptime=undefined&autostart=1
So replacing 0 for 3600 will rewind the video forward by one hour (3600 seconds):
https://berkeley.granicus.com/MediaPlayer.php?publish_id=cbebb4e6-5b83-11eb-920e-0050569183fa&starttime=3600&stoptime=undefined&autostart=1
The problem here is that ... you cannot rewind back manually that particular hour (it just gets kind cropped out). But it works to show the exact episode.
That's a pretty strange site.
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>.
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
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
According to the The Accessibility Project screen reads make use of JavaScript.
A common misconception among web developers is that screen readers only read the non-JavaScript page. Due to this misconception, we sometimes assume it’s unnecessary to make JavaScript apps and other functionality accessible. This is categorically false.
Source: MYTH: Screen readers don’t use JavaScript
The question for me is still, in which way?
I place emails similar to this using JavaScript. A small function decrypts the value and stores it in the href-attribute as well as it replaces the placeholder text:
<a data-value="[encrypted email]">This E-Mail is being protected against bots. […]</a>
Which becomes something like this on document ready:
somebody#somedmaincom
My questions are:
Is this way accessible?
Do I have to apply an aria-role in addition to make clear, that the content is replaced with the real values using JavaScript?
Is it accessible? It depends on your JavaScript function. It would be easier to look at a page where this was implemented.
A few comments:
In general, as far as a screen reader is concerned, a link is a link and the screen reader will just read the link text ("This email is being protected... ") and the fact that it's a link. It's up to the browser to do something when the link is activated (but see comments below).
Your sample code doesn't have the href attribute. This usually means that the "link" (it's not treated as a link without href) cannot be reached with a keyboard, which would be an accessibility problem. Fix this by adding href (with any value), e.g. add href="#".
How do you trigger your script? A screen reader user will use a keyboard, so you'll get an enter key press to activate the link. Do you watch for mouse click events? Links (<a href...>) are a special case, pressing enter on a link will trigger the onclick event, so you should be OK here when following the link.
You mention placeholder text: this is a link and not an input field, so I assume you change the link text? How do you do this? If it happens on mouse hover then it would not be triggered for keyboard users (including screen reader). Add e.g. an onfocus event that triggers the same function when the keyboard focus reaches that field.
When you change the link text using JavaScript then the screensaver should "see" the change and read the new text (if it still reads the old text try a different method to change the link text, there are several ways to do this in JavaScript). I'm not sure though if the screensaver will read the new text immediately or if you need to move the screen reader focus back onto the text again yourself. You can force the screensaver to read the new text (no matter where the focus is) by adding the attribute role="alert" to the container element that contains the text.
If you have any other mouse events you also need to add corresponding keyboard events. But I think this is everything. Post a link to an example page then I can comment further.