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.
Related
I'm trying to debug the integration between my app and Stripe's Elements component library. Everything works fine in sandbox mode, but we ran into a problem on production in the 3D Secure authentication process. This involves loading an iframe, into our app, that contains a form from the credit card's issuer (usually via a technology partner, like Arcot).
The form loads correctly and its buttons are working as expected, but the element (for a SMS one time code) is not behaving. Every time I click on the input, something is immediately pushing the focus back to the element of the iframe. This makes it impossible to type anything in, since by the time I touch a key, the input is not in focus. For reference, it is possible to change the input's value using document.getElementById('enterPIN').value = '123456';
I'm not sure if my app is triggering focus() calls (I don't think so) or if it is some part of the iframe code or even Stripe's. Is there a good way to monitor DOM events and do a stack trace for the trigger of each one?
I tried two tactics. Neither gave an obvious answer, but they did point my search in the right direction.
I opened the Event Listeners panel (in the Elements tab of my browser's developer tools) and removed everything I could find, but it seems that this doesn't actually change the behavior of the page- focus kept being stolen away. Luckily, I also noticed some listeners that were defined by the Material UI library.
I used monitorEvents() to get a few more details, but the src & target values were not much help and event.relatedTarget was always null.
In the end, I found this discussion and realized that my MUI Dialog component was stealing focus whenever I clicked on the iframe triggered by its content. This was easily fixed by adding the disableEnforceFocus attribute.
I have a single page application. There is a page with products and on the bottom a link to go to the next page. Upon clicking this "next" link the list of products in the DOM is replaced. While the link clicked has stayed the same and maintains focus from the click. Is it better for me to do something like document.activeElement.blur() is that case? Or should I just leave focus on that as is?
I'm unsure what best practice is as with normal server routed pages the focus would be reset with the page load. But I haven't seen any information indicating manually resetting focus on client routing would be the best experience from an accessibility point of view.
Never ever use blur. It's just bad, random and possibly frustrating.
IN a native application, you should always exactly know where the focus is, and the focus should always be at a precise place; otherwise keyboard accessibility is broken.
If you consider your web application as being a true application, you should observe the same rigour.
So, never use blur, since you don't know at all where the focus is going to move afterwards. If you are going to remove something from the DOM that currently has focus, you should first place it in another place that make sense.
IN your case: clicking on a link, you have two reasonable options:
Leave the focus on the link (reasonable as long as you don't move, hide or remove it from the DOM)
Move the focus at the beginning of the new content that just appeared / has just been replaced
You may ask users of your application which solution they think is the best, or deduce the answer by observing them during a test session.
Let's summarize quickly: whether you are making an old-style website with different pages, and you don't have to matter much about focus, or you are making a real application and in that case you should be as rigourous as if you were developing a native app.
Terribly simple.
I am working on a HTML5 application framework, run by a SAP JEE application server, build for companies and their intranet and extranet sites. It is based on the grid framework "Semantic UI" and besides that contains a lot of (also third party) Javascript.
I am currently examining a bug, where clicking a specific icon in a menu, when the page is scrolled down, makes the page somehow scroll back again upwards.
Since there is this great amount of Javascript around, I am currently struggling to find the JS code snippet, which is causing this odd behaviour.
I've read this post here, and got to know event logging in Firebug and inspecting event handlers in Chrome, but that didn't really help me.
I found out that using:
$(<my Elem>).on('click', function(event){
event.preventDefault();
})
I can prevent the scrolling, but I still didn't discover the cause of it.
Has anybody some more advice on how to find the real cause of this?
This behaviour might be caused by several different reasons. One of them that is often overlooked is links like Some JavaScript Handler.
When the JavaScript handler does not properly handle the event (e.g. by calling event.preventDefault(), the HTML link will be followed in addition to the JavaScript handler. Most browsers handle a link to an empty anchor tag # by going to the top of the page. This can easily be avoided when using an empty href attribute like <a href>Some JavaScript Handler</a>.
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.
I've got a pretty big complicated HTML5 app I'm working on (backbone, marionette, jQuery, underscore, handlebars, bootstrap, etc) and deep within the app is a modal popup with a form in it.
When the modal pops open, the first time you click on any form field the form field de-selects itself. After that first click you can use the form as normal. When the app is ultimately loaded into an iFrame in production (don't ask) the first time you click on any form field or hover over any button, the whole page scrolls down until the top of the div element the form is within inside of the modal is at the top of the page, but after it does this once, it doesn't do it again (confused yet? Yeah, it's complex and layered).
I'm at a loss for how to even begin debugging this problem (thousands of lines of code, two handfuls of libraries).
I tried these:
console.log('bound events: ', $._data(this.$el.find('#RandomFieldID')[0], 'events'));
console.dir($('#elmId').data('events'));
console.log('bound events: ', $._data($('body')[0], 'events'));
But that yielded nothing.
Since this is library upon library upon framework upon framework I'm not even sure where to begin trying to find the thing that has obviously bound itself to these fields, or even if it is the fields that are being bound to or something else entirely...
So, any suggestions on good strategies for how to debug a mysteriously bound Javascript event (with multiple JS libraries and frameworks, which can't be just commented out until the problem resolves because they are relied upon to even get the HTML to appear on the page in the first place)?
And, unfortunately I can't do a jsfiddle or something because, as I said, this is deep deep within the app and I'd basically have to re-create the app inside of JSFiddle (impossible) to link to an example (and, it's not in an external facing site, so, I can't just link to it live in production).
I'm stumped.
Here's how I do it with Chrome.
Ctrl-Shift-J to open Javascript console.
Click the little magnifying glass in bottom left, it lets you select an element with your mouse.
Click an element on your page (it will highlight as you go to it.) It will highlight in the DOM at the bottom and show a bunch of properties on the bottom right.
At the bottom right go all the way past the CSS attributes and stuff down to event listeners:
Pick the event listener you're interested in. It will show you the bound function as well as the exact line of code in what script would be executed. That should tell you what library is doing your crazy stuff.
I find the Chrome debugger to be much more powerful and fast (doesn't lag) compared to FireBug and the IE developer tools. It's highly recommended :)