I have an iframe, inside which arbitrary content is served. I want to ensure content within the iframe is not accessible to screen readers, and specify what screen readers should read out when the iframe is selected.
My expectation is when tabbing through different elements on the page, myDiv is selected as a single unit, and it reads out Unknown content.
I tried the following
<div id="myDiv" title="Unknown content">
<iframe id="myIframe" aria-hidden="true">
{arbitraryContent}
</html>
</div>
Problems I face with this are
Not all content within the iframe are being ignored. Perhaps that is because aria-hidden does not work with focusable child elements.
Unknown text is not always being read, especially if the child elements are focusable.
I also tried setting tabIndex=-1 on the iframe. This however causes all the elements (including myDiv) to not be selectable by keyboard.
What is the right approach to achieve what I am looking for?
If you prevent the screen reader from reading the entire iframe and if it still has focusable elements, yes, you have a fatal accessibility problem:
you can focus something but the screen reader isn't allowed to tell what is focused. So what to do ? Should it say nothing, or go against what you have defined ?
Technically, it means that there shouldn't be any focusable element inside aria-hidden=true.
You must ensure that it never happens.
However, the good question you have to ask yourself is why do you want to completely hide what is in the iframe or in other words, explicitely make it totally unaccessible.
IF your fear is that contents out of your control could break the accessibility of your page, then
Administratively speaking, certifications based on WCAG complience know this problem and usually accept exceptions, i.e. don't take into account something you haven't control in.
Pratically speaking as a screen reader user, the badly accessible iframe will probably indeed give the impression that the whole accessibility of your site is bad in general, but it's still certainly better to have some partially accessible content rather than nothing at all.
You'd better remove the aria-hidden=true. Even if you know that it isn't very accessible, leave a chance to access a little of it no matter what, instead of blocking it definitely. Perhaps the accessibility of the content will improve over time.
The exception to this is if the content of the iframe is objectionnable, decorative, or doesn't bring any real information in the context of your page. Typical examples include ads and various kinds of widgets (weather, clock, social network share, etc.)
For those, go ahead, leave the aria-hidden=true and remove all focusable elements of the iframe. They are anyway just useless noise, without any regret if they are completely skipped.
Related
I'm trying to figure out if altering the DOM of a website will present any accessibility problems. I am placing all of the jQuery in a $(document).ready() function. Will this cause any accessibility issues with the altered elements?
We don't have access to the theme template HTML files, only CSS and JS files. So for example I'm adding a div into our theme using$('[element name]').before('<div>[div content here]</div>') will this content be as accessible as the rest of the DOM as long as I include all the appropriate aria attributes etc.?
In theory, you shouldn't rely on JavaScript to produce the whole HTML code of your site, it's basically a bad practice.
However, it's exactly how big frameworks like angular and react work.
Given that 99% of browsers support JavaScript, it's in fact no longer a problem nowadays.
The true answer is in fact both yes and no, it depends. It depends on the actual HTML code injected.
The key point is that, you must have the same care with generated code as with the code directly written in HTML by hand, i.e. be careful on headings, form labels, alt texts, ARIA attributes if you need them, etc. all the time and in particular each time you add/remove something in the DOM. Additionally, you must pay attention to where the focus is or might be and a few other things.
It's often overlooked precisely because some people assume that it's anyway not accessible, what isn't true.
In order to be accessible, a site with dynamic contents must be accessible at any moment. If it isn't always the case, then you will lose users in need of accessibility at some point. In practice the loss of accessibility often happens at the most critical moment: checkout or paiement, maybe not because of your fault if the paiement site isn't accessible.
You might even improve accessibility by manipulating the DOM via JavaScript (JS). So no, per se, manipulating the DOM does not pose accessibility issues.
If you cannot control the HTML, and the theme is badly accessible, all you can do to improve that is using JavaScript. Think adding role attributes to generic <div> elements. Also, CSS-only solutions seem appealing, but are often not exposing the appropriate state via ARIA-attributes to assistive technology, which needs to be corrected via JS.
Whether your manipulations produce problems or improve accessibility, therefore depends strongly on your implementation.
Here are some examples.
Adding or Removing Content
When adding content, like in your example, it depends on where that content is added in the document, and at which moment.
If it’s being added on DOM Ready, there should be no issue (One exception might be live regions added after the DOM loaded). But if it’s being added at arbitrary moments, it’s problematic.
The Web Content Accessibility Guidelines (WCAG) refer to this as a Change of Context, which must not happen on focus, on input and on user request
See A change of content is not always a change of context. Changes in content, such as an expanding outline, dynamic menu, or a tab control do not necessarily change the context, unless they also change one of the above (e.g., focus).
If it’s being added after DOM Ready, it should happen on user request, or must be a status message and carry the appropriate role to be announced.
For example, in a Disclosure pattern, the aria-expanded of the trigger indicates that new content will become accessible right after the trigger on pressing it. It might just be added to the DOM, depending on the implementation.
Lazy Loading Content
Another, very valid use case would be content that’s added asynchronously. This is tricky to get right, but basically aria-busy can render this more accessible.
I can't work out how to get Chromevox read an element once you have changed focus to it via Javascript. Take the example found at OpenAjax. When you click on the Show topic 1 button the controlled element is expanded and focus is moved to it. However in Chromevox it does not read the content of the element just the aria-labelledby.
To get Chromevox to read the content of the element after it has been expand I have to move out and back in again. Not a very good user experience.
Is this an issue to do with Chromevox or is there a better way to approach this?
Note: ignore the other three examples they have typos in their aria-labelledby attributes and thus don't function correctly.
The ajax example is not a good one to mimic. In addition to the t2/m2 typo you mentioned, they have competing labels. There's a (hidden) <div> that contains text ("Topic 1 is all about being...") and there's an aria-labelledby.
According to the accessible name computation rules, https://www.w3.org/TR/accname-1.1/#step2, the aria-labelledby is processed in step 2.B.first-bullet and then the process is done. The text inside the <div> should be ignored.
If there were not an aria-labelledby, then you would get to step 2.G and the inner text would be the accessible name (and read by a screen reader).
So, you are seeing/hearing a conflict and it may depend on the how the browser interprets these rules as to what text is read by the screen reader.
NVDA reads both the aria-labelledby and the text.
JAWS reads only the aria-labelledby, which is technically correct.
It sounds like Chromevox is doing the same thing as JAWS, which is correct.
I have a section on my website, which I would like to have users see depending on whether the user is logged in or not.
I was wondering if it would be more efficient to create/remove the elements conditionally (or even dynamically write) with jQuery, or to simply hide/show the elements.
Which would be the best practice method that you would use? jQuery tutorials that I've done always makes me add and remove the elements.
Further(I hope I do not anger the meta gods with this), what is the general guidelines on when I should be creating/removing elements and when I should be showing/hiding them?
Thank you in advance!
At one point, long ago, the recommendation was that you never put elements on the document that you weren't using, and you removed the element once it was done.
You have to realize, however, that this recommendation was at a time when browsers didn't always handle CSS and layout control was horrible. This was an era of text-based browsers such as lynx.
The world is much different now and even most blind users [citation needed] use full-fledged browsers with screen readers and these screen readers are smart enough to understand hidden elements.
Today, it is much easier to simply put everything that isn't sensitive in the document and style sheet rules or JavaScript to hide, unhide and re-arrange as needed.
That said, sensitive information should be restricted. If a end user isn't allowed to see something, then don't give it to them, even hidden.
Remember, the client can never be trusted.
EDIT PER COMMENT
Lets imagine that your system has a sensitive field that we'll call coolness. Coolness is something that regular users can't ever see, but the web admins get to set on folks they like and don't like.
If you simply say:
<div id="coolness_rating" style="display:none">
<label>Coolness
<input name="coolness" value="4">
</label>
</div>
then even though it is hidden on the screen, a user can still look at the HTML markup and see that value. Its hidden, but easily findable.
Continuing on the same though, the back end code also needs a guard around that coolness value, so that someone can't tweak what is being sent and add that in.
The back end code needs to look something like this:
if (loggedInuser.AccessLevel === AccessLevel.Admin) {
// Only let Admins change the level
referencedAccount.Coolness = formData.coolness;
}
Imagine you have a simple, single-page application - regardless of whether it was written using Backbone, Angular, Ember or anything else.
How can you tell a screen reader that we've changed 'page' when a route is followed?
In a classic application, when I navigate from /index.html to /about.html the screen reader obviously detects the page change, and re-reads as you'd expect.
In my Backbone application though, when I follow a route I cannot work out how to trigger a 're-read'. I've tried triggering a focus event which I'd seen somewhere, but that doesn't seem to work.
Note: I'm currently testing with NVDA/Chrome.
Overall, you should not need to trigger a 're-read', and depending on your UI that might not be a good thing anyway.
My experience has been with angular.js (as an accessibility person rather than the developer), and our overall approach was to manage the focus rather than trigger a re-read. (We do extensive accessibility testing.)
The key thing from a UI point of view (primarily for screen reader users) is that selecting a link (e.g. about.html) should take you somewhere.
In this case the appropriate place to put the focus would be the top of the content area of the about 'page', hopefully an <h1>.
In order for that to work the target element should be focusable via a script, so probably needs tabindex unless it is a link or form control:
<h1 id="test" tabindex="-1">
The -1 means it is not in the default tab order, but is focusable via a script. See more at WAI-ARIA authoring practices.
Then at the end of the function that loads the new content, including the tabindex attribute, add something like:
$('#test').attr('tabindex', '-1').css('outline', 'none');
$('#test').focus();
When adding tabindex dynamically it is best to do so in a line before the focus() function otherwise it may not work (I remember that from testing with JAWS).
To test this I would use either:
NVDA & Firefox, Windows
Jaws & IE, Windows
It is also good to test with VoiceOver on Safari/OSX, but that works differently and may not hit the same issues as a windows based screen reader.
You will hit a lot of issues with Chrome/NVDA as that is not supported very well, and end-users are very unlikely to use that. IE is ok with NVDA, but Firefox is best.
Overall, it is worth getting to know the WAI-ARIA authoring practices, particularly Using ARIA in HTML. Even though you are using a JS app, the browser (and therefore screen reader) is interpreting the resulting HTML so that advice is still relevant.
Lastly, if you are updating page content without the user pressing space/enter to activate something, you might find that JAWS/NVDA do not know about the new content as their 'virtual buffer' has not updated. In that case, you might need to add a JS shim to make them update, but only do that if you run into problems in testing, it should not be a global patch.
Taking the answer from #AlastairC, and the comments below it. I've taken this a bit further now and am going with this as my solution going forward:
My go-forward solution
I found that just reading out the first heading wasn't really that useful. Especially if the last page you were on was a loading sequence. You can hear that there something new has been focused, but it's certainly not clear that this forms the part of a whole now page.
Add some useful, descriptive text to the page
As such I now have a single paragraph at the top of my page layout template. This includes a screen-reader friendly message, along with a very rough overview of what the page.
<p class="screenreader-summary" tabindex="-1">
The <strong>Dashboard</strong> page is now on-screen.
It contains several widgets for summarizing your data.
</p>
Note that the tabindex value allows us to focus this element with JavaScript. You might not want to use a p element for this, you can use anything you like really.
Hide it off-screen (optional, only required if it would break your design/readability)
This is then coupled with CSS to move this element off-screen:
.screenreader-summary {
position: absolute;
left:-10000px;
top:auto;
width:1px;
height:1px;
overflow:hidden;
outline: none; /* Important, don't show an outline on-focus */
}
Focus this element, when a new page is shown on-screen
Finally, in the JavaScript code that shows your page on screen (e.g. in MarionetteJS using onShow or the show event):
$yourViewEl.find('.screenreader-summary').focus();
The result
I'm a sighted person, so take what I say with a pinch of salt - however I found that reading out a short description is so much more useful.
For angular the url changes, If someone REALLY needs to reread everything, (like me because of requirements), what did the trick for us was this:
$(window).on('hashchange', function () {
location.reload();
});
and we just added extra code to handle pages like "success" were no reloads are supposed to happen.
this simulates the actual page loading, and screen readers will read it normally like a page change.
kind of defeats the purpose of angular in a way, but this will help people like me who already have the application, and want a quick fix.
i want some code to move the cursor position in an IFrame via Javascript or jquery.
Really it will help me a lot.
Not possible. To answer why that's impossible, imagine:
I include an iframe to some very important business (let's suppose for a moment this business does not have frame-busting code)
When the user reaches my page, it begins manually controlling the cursor's position to highlight the "Delete Account" button, and simulates a click.
User's account is deleted on a completely different site, through none of their input.
Javascript allows you many UI-coding capabilities, but ultimately the user is in control. Even events like the "onpageunload" are very much restricted in what they can do, and browsers will often include 'escape' options even there. Furthermore, even in the instance that you CAN find a way around these chains, it will frustrate and quite possibly even panic many of your users. I try to warn people that any instance in which you're "re-coding the browser" may lead to all sorts of unpredictable issues, and may even prevent handicapped accessibility to your site.
It might help us to know if there's some specific reason you'd like to do this - possibly the solution is not what you think it is. For instance, if you are trying to make an FPS using WebGL, I seem to remember Chrome including some function to allow for mouse control inside of a window (possibly taking a browser confirmation dialog)
You should check out
http://jqueryui.com/draggable/
You "could" put make the content in the iframe draggable, if you host the src for your frame.
If you don't host the src for your Iframe, you "could" put have an inner iframe that is draggable, and an outer iframe that displays the inner frame.
It is a very messy solution, I hope there is something better for you.