ARIA Live Regions for single page applications - html

I am new to accessibility of web pages. I have an application where the content of the body changes based on the header link clicks in an ajax call without page refresh. So the HTML content inside the body gets updated for each link click with different page content (table + button + information text).
My requirement here is the screen reader should announce the information text each time when the page gets loaded. Here the container is body (or an immediate div inside body) for all pages. So I have made it aria-live="polite", but every time page loads it is announcing the whole page content but I want to make it announce only the information text. Other elements of the page should be announced on focus/visit. I hope I can apply aria-live="off" for all other elements but I'm looking for any ideal solution for this. I cannot change the layout of the application.
Can some one help me on this. Thanks in advance.

SPA pattern best practices
You are essentially following a Single Page Application pattern. As such the method recommended for handling navigation is actually quite straight forward with two steps.
tell a user that navigation is about to occur (before navigation)
let a user know that loading is complete (after navigation).
before navigation (link click)
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 but essentially you would update the content of a hidden div (<div aria-live="assertive" class="visually-hidden">loading</div> with some loading message the second a link is clicked.
This should be done before any AJAX calls are made.
after navigation (new content loaded)
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". 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.
Once the page loads and the content has been populated fully 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).
At this point you may also want to clear the <div aria-live="assertive"> contents so that it is ready for further navigation.

Have you tried to wrap the "live zones" - where updates happen in div with aria-live="polite". Then screen reader will announces only these zones on updates.Don't wrap whole page or div in aria-live="polite".
<div aria-live="polite">
<p id="errorText">Announced on update</p>
</div>
<div>
other page sections..
</div>

Related

Does preventing a modal from being hidden by clicking the background violate accessibility requirements?

I'm adding a blocking modal (ie one that covers the screen and prevents interaction while an API call is processing) to my company's design library.
As part of that, I modified our modal so that clicking on the grey backdrop will NOT hide the blocking modal, but I want to make sure that doesn't violate accessibility guidelines. I haven't been able to find anything online about this. Does anyone know if this this violates accessibility requirements?
Short Answer
The answer is 'it depends'. Basically if the modal is not dismissable in any way it becomes a 'keyboard trap' and so would violate WCAG.
However if you structure it correctly a modal that blocks the page while an API loads is perfectly valid (and can't be dismissed while the page is loading), but there are a few things you need to do to make sure this is accessible.
1. Make sure that when this modal loads, nothing else on the page is focusable.
The biggest issue I see on most modals is that they allow focus outside of them.
You can't just stop users using the tab key as that is not how most screen reader users navigate the page (they use shortcuts for headings (h1-h6), hyperlinks etc.).
For this reason make sure your modal sits outside of your <main> and the hide your <main> and other major landmarks that contain information with aria-hidden="true" and by adding tabindex="-1" to them so nothing is focusable.
Obviously this depends on your document structure so you would need to test it, but a properly structured HTML document will work with the above method.
2. Make sure that a screen reader user knows that the page is busy and something is loading.
There are a couple of ways to do this. The best is to use an aria-live region
Adding aria-live="polite" and aria-busy="true" to the section you are updating is one way (if you are updating one part of the page).
However in your circumstances I would make a section within the modal aria-live="assertive" and not use the aria-busy (as you will be hiding all the content in step 1 so aria-busy would not be applicable).
I would then update the message every second or two for long loads (i.e. 'loading', 'still loading', 'nearly loaded' etc. Or better yet a loading percentage if your script allows.)
Once the page content has loaded, you do not need to say 'loaded' instead make sure you have a heading for the section or page that has a tabindex="-1" added on it that accurately describes the content that has just been loaded in.
Once the load completes, programatically focus this heading and the user will know that the load is complete.
3. Make sure that if the API call fails you feed something meaningful back to screen readers
When your API call fails (notice I said when, not if!) make sure your JavaScript can handle this in a graceful way.
Provide a meaningful message within your modal aria-live region that explains the problem. Try to avoid stating error codes (or keep them short, nothing worse than hearing a 16 digit string on a screen reader for an error code), but instead keep it simple such as 'resource busy, try again later' or 'no data received, please try again' etc.
Within that region I would also add one or two buttons that allow to retry / go back / navigate to a new page depending on what is appropriate for your needs.
4. For long load times, let the user know what is happening.
I covered this in point 2 but just to emphasise it, make sure you feedback to users that things are still loading if there is a long load time by updating your aria-live region.
Nothing worse that wondering if the page has loaded and the developers forgot to tell you.
5. Give the option to cancel an API call so it doesn't become a keyboard trap.
Obviously the big problem with a whole page modal is it is a 'keyboard trap'.
To ensure this isn't an issue make sure you provide a cancel button.
Make sure it is clear that this will cancel the loading of the page, but don't rely on JavaScript alone.
Instead make this a <a> styled like a button that either points to the current page or the previous page (yet again depending on your needs) and add role="button".
Then intercept this click with JavaScript so that it can function like a button.
The reason for this is that when your JavaScript fails (yet again - when, not if) the user still has a way to get to a meaningful page, thus avoiding a keyboard trap.
This is one of the few times you should use an anchor as a button, as a fallback!
By doing this you ensure that the user always has a way to escape the modal.
You may also consider allowing a user to use the Esc key to close / cancel but that is yet again down to you and your circumstances.

Accessibility: what's the way to force reading of span text on page load

I'm curious what's the proper way to push the screen read to read <span>read me on load</span> first (it's in the middle of the html page) when the page is loaded?
Even
role="rude"
doesn't help for some reason.
Thank you.
What you are using is not part of ARIA live regions. You want either role=alert or aria-live=assertive (though in an early draft "assertive" was originally "rude").
Now, that being said, live regions are intended for use on an already-loaded page. If you are making a dialog, then your HTML might look like this:
<div role="alert" aria-live="assertive">
Anything in here will be announced as soon as it changes.
</div>
If you want the dialog to be announced at page load, then consider hiding its contents and then using script to display its contents once the page has finished loading.
If you need to make it announce as soon as the page loads and it is not a dialog, consider moving focus to the element at page load using script. By placing focus, the browser will announce whatever it is.
For this to work, however, you cannot just place focus on a <span> as it is not interactive content. You can make it interactive by adding a tabindex attribute. If you do that, then you will need to add a role describing what the element does (you can use role=region if you are stuck) so that the screen reader can announce what it is instead making the user think she has landed on a control or otherwise expect to be able to take an action.
If you are moving focus to the element for users without screen readers, or if the very first thing the page displays at page load is an alert of some sort, then this should be fine.
However, since you have provided no context and no examples, and given all the ways this can be done that are far worse than doing nothing, please ask yourself this:
Is the thing you want to announce…
…an alert or dialog?
…a control that already can receive keyboard focus?
…going to be given focus for all users, not just those with screen readers?
If you end up saying no twice then you should not do this.
If you can provide an example URL that shows what you want to do, then I am happy to help you get it working. Otherwise I fear you may be coding something that ends up creating a trap or barrier for screen reader users.

Screen Reader not finding popUp

Currently trying to make a section of a site more readable to screen readers (Jaws, MacVoice over, ect). One of the buttons on the page generates a popup that in turn the user will need to be notified of, the problem is this popup is generated at the bottom of the DOM meaning that the user would have to navigate to the bottom of the dom (tabbing through) in order to even know the page is there. Does anyone know a possible way around this?
You can try removing it from the generated place with javascript and append it to a more convienant/logic place for the user. The code should be something like:
$('#amoreconvenientplaceforthebuttonID').append($('#mybuttonID'));
$('#mybuttonID').remove();

Overlay one iframe on top of another, scroll them together

Following up on How to rewrite URLs referenced by Javascript code? I'd like to overlay a button on top of someone else's website (e.g. overlay a Paypal button alongside Stackoverflow's bounty button) and have the two <iframe>s scroll together. The button would reside in the top layer. The website would reside in the bottom layer.
I understand that transparent <iframe>s were/are abused for clickjacking but the browser security mechanism seems to block legitimate use-cases. In my case the user is seeing the same button he/she is clicking. It's even possible that this is a browser bug.
Here is what I see under Chrome:
The top <iframe> intercepts all mouse clicks, even for areas that do not contain any components. Meaning, users cannot interact with the bottom layer at all.
If I style the top <iframe> with pointer-events: none the opposite problem occurs: users are able to see the top layer but all mouse clicks go to the bottom layer. Applying pointer-events: auto to child components does not help (clicks still pass through to the bottom layer).
If I size and position the top <iframe> so its area is equal precisely to the button I am trying to overlay, then mouse clicks go to the right layer but the top layer fails to scroll alongside the bottom layer. Meaning, the button always remains in the same absolute position as the bottom layer scrolls.
Is it possible for me to position a button in the top layer so that it always aligns with a certain position in the bottom layer? In the example where I position a Paypal button alongside the Stackoverflow bounty, I expect the Paypal button to scroll off the page as the user scrolls down the question.
https://stackoverflow.com/a/4087397/14731 leads me to believe this is not possible. Is there another way to implement this?
UPDATE: Here is a jsfiddle for you to play with. The test button is found to the right of "NEWS & VIEWS" in the middle of the page.
Update: New approach
Following discussion with Gili below, the requirement for the solution to work across multiple pages made me rethink my solution.
The new approach:
Doesn't require any code changes or specific features on the target site.
Works on every page while the user navigates (as long as they stay on the same domain)
Could be tweaked to inject any HTML/JS into any DOM element within any target page
My solution works as follows:
Send the person you want to demonstrate Widget X to an email with a link to your instructions page
That instructions page contains a bookmarklet which they add to their bookmarks bar
They visit their own site and click your bookmarklet
Your bookmarklet injects javascript into their page
That javascript creates a pop-up window with content that appears to belong to the target domain, since that domain generated the popup
That pop-up window then monitors the DOM on the target browser window (window.opener) and injects our arbitrary HTML whenever the current page doesn't contain our target node ID.
It seems to work well in my testing (perfect in Chrome, haven't tested across all browsers yet) and seems to work on every target site from StackOverflow to Twitter.
Live demo here: How to demo an web widget on a third party site without having access to their code
Sample code below, expanded for readability:
s = "<script type='text/javascript'>setInterval(function() { if(!window.opener.document.getElementById('gctrlPixelator')) {var i=document.createElement('IMG');i.src='//lorempixel.com/400/200/';i.id='gctrlPixelator';i.style.cssText='top:20;right:20;position:absolute;z-index: 9999;';window.opener.document.getElementsByTagName('body')[0].appendChild(i);}},500);</script>";
t = "<div style='text-align: center; font-family: Arial, Helvetica;'><h1 style='font-size: 18px;'>Demo running!</h1> Keep this window open and return to the main site window to continue the demo.</div>";
w = window.open('','name','height=200,width=400');
w.document.write(s);
w.document.write(t);
The above should be converted into a bookmarklet before deploying to an 'instructions' landing page for a potential client.
Original solution
First, sorry for posting this as an answer rather than as a comment. I've been thinking about this for half an hour, and only just realised I need 50 reputation to comment. So, apologies, but I wanted to share...
I agree that it's unlikely there is a cross browser way to do this double iframe trick. I read your other question about javascript URL rewriting and it lead me to an idea: Rather than trying to embed/hijack their site on another URL, how about creating a bookmarklet that allows you to inject your Javascript into their page?
It could work like this:
Direct them to your site where you host the custom bookmarklet link. Ask them to add it to their favorites.
Ask them to go to their own site, then click your bookmarklet in their bookmarks.
This would inject your JS into their page, allowing you to edit the DOM any way you wanted (e.g. changing styles, adding DOM elements, etc.)
Code something like this, converted to a bookmarklet (i.e. wrapped in a function with a javascript: at the start) could do the trick...
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://www.example.com/file.js");
document.getElementsByTagName("head")[0].appendChild(script);
I've tested that in Chrome and it seems happy to load the JS cross-domain. The only rule seems to be that the protocols must match (http or https).
Not quite what you asked for, but a possible solution.

page break in HTML

i want to use page break in html that means the reader cannot scroll down further until he select a link for it.
<SPAN id=title><A name=BdToc_1 external=yes><h1 id="BookTitle" align="center"><font color="#B90000"><b>Choose Subject</b></font></h1>
</A>
</SPAN>
<p>
Contents....
</p>
I want a page break before and after this. Please help me
Forgive me for pointing out the obvious, but page breaks are used to separate distinct pages. Each HTML document is a distinct "page". "select[ing] a link" traditionally loads a new page. So.... why don't you just load the next page when they click on this link?
You can specify where page breaks occur using CSS properties page-break-after, page-break-before. Of course, this works only when printing the web page. As far as I know, these properties are correctly implemented in all major browsers including IE6+. Additionally, you can also state that page break should not occur inside an element using page-break-inside.
If you want paging per se, you need to have HTML for each page and interlink these pages. Or you can fetch contents of each page using AJAX dynamically, which of course involves scripting.
It's not quite possible in HTML. You could try makeing something in Javascript, but anyone can dissable javascript.
Why would you want something like this?
You can use onscroll in javascript to control the scrolling. The onscroll event can determine the current position and there is a function to scroll up if the user is too far down.
Then, when the user clicks the link, you set a flag (scrollok=1). The onscroll checks the flag and now permits scrolling.
If you want to defeat people who have deactivated javascript, just make the content invisible until they click using stylesheets: visibility=none.
Then, when they click the link, you enable scrolling via the flag, and make the content visible.
If you don't know how to do these things, just leave a comment and I can be more precise.