VoiceOver focuses on aria-hidden element - html

On our (Vue.js v.2) webpage we have a section where you can swipe between cards, or by pressing "left" and "right" button. This swiping section is written with vanilla JS, and no fancy library. In order to have the adjacent visible when swiping, I need to have all of them visible in the DOM tree, but hiding them from the viewer. I have made it so all the unfocused cards are aria-hidden, and it works great when using ChromeVox. The problem is that when using VoiceOver, and I click the "right" button, and immediately tab down to the card, it will read out the card to the left, and also get trapped there since the card is aria-hidden. Code-wise the change from aria-visible to aria-hidden happens as soon as the button is pressed, but it seems like VoiceOver has already decided that when I am focusing on the "right" button, that the next element should be card 5 (for instance). If I wait around 1 or 2 seconds after clicking the "right" button, it will change the "next" element, and focus on the correct one (card 6) when I tab down. Is there any way to get around this, so it will focus only on the aria-visible element? Maybe a way to "force refresh" VoiceOver's stack of elements to read next? Maybe if I remove the message being read out when clicking the button, it will refresh immediately? I still haven't found of doing either of those things. I created a low quality flowchart to illustrate the problem better. What I want is for it to behave like ChromeVox.
I have tried several methods of getting this to work now, and it feels like it's a bug with VoiceOver. Some of the (desperate) attempts I've tried: setting tabindex=-1, role=presentation, changing the ID of "right" button dynamically as I navigate between cards, creating an empty div with a dynamic ID below the button, using aria-flowto, dynamically setting aria-describedby on the "next" element, and different variations between these and some other stuff I can't remember.

Eventually I found a solution that kinda works. I'm not very happy about it, but it's better than nothing. What I did was to make the title inside the card aria-hidden, and creating a currentHeader variable in store. I created an sr-only & aria-visible title above the swiping section, where the v-html points to the currentHeader variable. This way, the "next" element for the "right" button will always be the same element, but content will change after I click the button. It's not a perfect solution, and for some reason it makes VoiceOver "halt" when trying to go to the next element immediately after clicking the button, but at least the user won't read the wrong text and get trapped. Here's a pseudocode illustration of how I did it if my explaination was confusing:
// old solution // old swiping-section
<button id="left" /> <div v-for="element in elements" />
<button id="right" /> <h3 v-html="element.title" />
<swiping-section /> <p v-html="element.desc" />
</div>
// new solution // new swiping section
<button id="left" /> <div v-for="element in elements" />
<button id="right" /> <h3 aria-hidden="true" "v-html="element.title" />
<h3 class="sr-only" v-html="currentHeader" /> <p v-html="element.desc" />
<swiping-section /> </div>
If anyone finds a better way to do it, please post your solution.

Related

What should be the aria-role for the box that can be opened by clicking on notification icon on navbar?

I am trying to implement the notification component that will show the list of the items and will be opened by clicking on the notification icon on the fixed navigation bar on the top. I don't think it's a menu bar. Because the menu provides the actions that can be performed and it can also have a sub-menu.
https://www.w3.org/TR/wai-aria-practices/#menu
Can anyone let me know what should be the aria-role of such kind of components?
Below is the code sample. I will open the template dynamically by clicking on the notification icon button:-
<button aria-label="notifications">
<mat-icon class="mr-md">notifications</mat-icon>
</button>
<!-- Notification template -->
<div class="notifications__item">
Notifications
<li *ngFor="let notification of notifications" class="notifications__item">
<mat-icon class="notifications__icon material-icons-round">
{{ notification.icon }}
</mat-icon>
<div class="notifications__content">
<div [ngClass]="{ 'notifications__warn': notification?.type }">
<span>{{ notification.title }}</span>
</div>
<div>{{ notification.description }}</div>
</div>
<small class="notifications__caption">
{{ notification.duration }}
</small>
</li>
</div>
There are still a lot of things to consider that your example doesn't cover, so this isn't a complete answer, it is just pointing you to the relevant WAI-ARIA depending on what route you take.
The button
The first thing to consider is the button. You need to tell screen reader users what state it is currently in. For this we use aria-expanded to indicate whether the item it controls is currently opened or closed. (aria-expanded="true" for open, aria-expanded="false" for closed.)
At the same time we want to indicate what item this button controls (as the notification list isn't 'owned' by the element - for example if it was an <li> with a nested <ul> in a menu then the list would be 'owned' by it).
For this we would use aria-controls or aria-owns and point it to the ID of the element it controls. For the difference between them see this stack overflow post as a good explanation, in this example I would say it is aria-controls but yet again depends on your implementation and positioning in the DOM.
With regards to the button itself and where it sits in your menu, this is still considered navigation so it should sit within your <nav> element. However if this sits outside of your navigation along with say a 'help' and 'account' section you may consider those items part of a toolbar. (yet again I would say it doesn't apply here but something to look at)
Also it doesn't appear to be applicable here but if you include any links etc. within the 'popup' / modal that shows the notification list (i.e. a 'view all notifications' link), you should consider aria-haspopup="true"
The notification list
Right so we have a button pointed to the container (don't forget to give the container the relevant ID for aria-owns or aria-controls). Next what about the container itself?
Well in this example it appears that the container should be treated like a modal.
So for this reason you need to consider:-
trapping focus in the modal,
close with Escape,
returning focus to the button that activated it on close,
providing a close button that is accessible by keyboard,
a title for the modal (even if it is visually hidden)
What I would recommend is add some of the accessibility features above, try it with a screen reader and keyboard and see if it is easy to use. Once you have decided on your pattern ask some more questions on specific use case issues as the above is general guidance.
A few things to consider based on your markup
Additional things to consider from your example:-
use aria-hidden="true" on your icons, they don't add anything for screen readers (assuming your notification.title is descriptive).
For the notification title consider making it a relevant heading (<h2> - <h6> depending on position in document.
Don't forget to add some visually-hidden text that describes the warning level (I can see you have some form of colouring / distinction in [ngClass]="{ 'notifications__warn': notification?.type }" - expose the same info to screen readers.)
You currently have a <li> within a <div> - maybe change the outer <div> into an <ul> so it is semantically correct (<div class="notifications__item"> into <ul class="notifications__item">)
I hope the above is useful to set you on the right track, a lot to read but after reading the linked articles you should be able to make a better decision on what pattern you are using (as I didn't even mention making this a sub item within your menu) and can then ask some more questions on specific details you don't yet understand.
final thoughts / tips
test with a screen reader - this is the biggest tip I can give on working out how WAI-ARIA works and interacts with things.
Also if you are ever in doubt as to whether a WAI-ARIA attributre is applicable it is better to not include it.
Incorrect use or WAI-ARIA is actually worse than not including it at all so make sure you understand when to use an attribute reasonably well before implementing it. If I am ever unsure (as it still happens to me!) I tend to look at 2 or 3 examples of it in use and see if my pattern fits the examples I looked at.

Only screen-reading a child element when it is highlighted

I have an element roughly like this:
<a class="parent" tabindex="0">
<span class="info">One line of data.</span>
<span class="info">Another line of data.</span>
<button class="action">Click here to do stuff.</button>
</a>
Because of reasons, I have a link that takes users to a certain page, and inside that link is a button that does something related to the link, but functionally different. When I tab over the parent link with a screen reader, it reads the text of everything inside - both info spans and the button, which is not the behavior I'm looking for. What I want to happen is:
If a user tabs to the parent link, it reads off the contents of the two info spans, but not the button, as this would mislead users about what the link does.
If a user tabs specifically to the button, the contents of the button is read off.
I tried assembling a custom aria-label for the parent link to do what I want, but it proved to be very difficult because some of the content I want to be read is interpolated HTML, which doesn't go very easily into an English string. Is there an easier way of doing what I want to do?

How do I keep a list of images from resetting when I choose one?

I am using ASP.NET web forms to design a site. One of the pages uses a <DIV> to hold a scrolling list of asp:ImageButton controls, each of which displays an image. The goal is to be able to display a long list of images inside a scrollable DIV tag, making it easier for the user to choose one without using a third-party control and avoiding controls that require paging. Here's a sample of the DIV I've built:
<div id="divOne" runat="server" style="height: 230px; width: 330px; overflow: auto;text-align: center;">
<asp:ImageButton runat="server" ImageUrl="../Images/image1_small.jpg" AlternateText="Image #1" CommandArgument="Image #1" OnClick="ImagePicked" /><br />
<asp:ImageButton runat="server" ImageUrl="../Images/image2_small.jpg" AlternateText="Image #2" CommandArgument="Image #2" OnClick="ImagePicked" /><br />
...
</div>
When the user clicks an image, it triggers an action to update another part of the page. But the problem is that when the image is clicked and the action fires, the list of images resets back to the first one at the top of the list.
I tried using an UpdatePanel to control this, but it still happens. Is there any way to prevent the list of images from resetting?
If you just want to save scroll position after postback, there are several ways to do it. The simplest one is to save scroll position with javascript whenever onscroll event triggers, then restore saved value on page load, as shown here:
Maintain scroll position of a div within a page on postback
If that doesn't quite work for you, you can try storing scroll position in a hidden field (see this answer: https://stackoverflow.com/a/1184659/1202275). Some also suggest to store it in a cookie, but that can backfire if a person using the page has cookies disabled for any reason.
Hope that helps!

How to label a loading animation for WAI-ARIA?

I'm working on fixing some accessibility issues on a web page. I have this div that acts as a dialog, and inside at one point a div containing a loading animation and a text "Working..." is displayed.
I am unsure as to how to label these two items in order to correctly notify the blind user that there is a progress animation and that it's working and he should wait.
<div id="loading" style="display: none;">
<div class="mgBot15"><span class="txt16" role="alert">Working...</span></div>
<img src="loading.png" role="progressbar" aria-busy="true"/>
</div>
I tried adding the role and aria-busy properties to the img (also to the parent div, at first).
When this div appears (by changing the display style property), it correctly reads "Working..." but I hear no indication that it's busy and that the user should wait, am I missing something?
I've looked all over for examples for loading animations, to no avail so far.
Note: I'm using NVDA as a screenreader to test.
Thanks
The best solution I could come up with was using role alert, and aria-busy="true".
<div id="loading" style="display: none;">
<div class="mgBot15"><span class="txt16" role="alert" aria-busy="true">Working...</span></div>
<img src="loading.png" alt="loading" />
</div>
I believe the most sensible approach would to use the combo
aria-busy="true" aria-live="polite"
The reason for that is because some pages might have a lot of separate loaders (let's say one for each component, not a single loader for the whole page) and it you use aria-live="assertive" or role="alert" it will be very intrusive and each of the loaders will get called out.
The correct role to use here is progressbar as the original question used. Other roles like alert may work, but they are less specific, meaning assistive technology may present the information in a less ideal manner.
There are a few issue with the original question's example, though:
If you wish to have the text be announced in the same as an alert is, aria-live="assertive" should be used rather than the alert role. That aria-live value is what causes the screenreader to announce the text when it does for an alert.
The text to be announced should be set on the element with the progressbar role using the aria-valuetext attribute. It should not be set solely on a separate adjacent element. If it needs to also be included in another element for presentational reasons, that element should have aria-hidden="true".
Per the spec, aria-valuemin and aria-valuemax are to be specified even when the progress is indeterminate (like a spinning loading indicator). These could be set to 0 and 100 respectively as simple placeholders implying a percentage.
When the loading is complete, the aria-valuenow could be set to whatever was used for aria-valuemax, and aria-busy can be set to false.
This leads to one potential alternative to the original question:
<div id="loading" role="progressbar" aria-valuetext="Working…" aria-busy="true"
aria-live="assertive" aria-valuemin="0" aria-valuemax="100">
<div class="mgBot15" aria-hidden="true"><span class="txt16">Working...</span></div>
<img src="loading.png" alt="" />
</div>
After a day of fiddling with a similar issue, I was able to finally get this working with a lot of reading and experimenting. I'm using NVDA for a screen reader.
<div class="loader" show.bind="isLoading" role="alert" aria-label="Loading"></div>
The following attributes were key: role and aria-label.
Above example makes NVDA announce "Loading alert" once isLoading becomes true. Important to note is that NVDA pronounces the aria-label value, followed by "alert". Using roles "log" or "status" did not end up in any announcement.
Bootstrap used role="status" like this :
<div class="spinner-grow text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
and in MDN it said :
The status role is a type of live region and a container whose content
is advisory information for the user that is not important enough to
justify an alert, and is often presented as a status bar. When the
role is added to an element, the browser will send out an accessible
status event to assistive technology products which can then notify
the user about it.
There's a good article I came across that explains what needs to be done for this scenario Loading spinner accessibility
The spinner should be included inside the container. Its visibility can be toggled in relation to the aria-busy attribute. They should always be opposites, i.e, if currently loading, section[aria-busy="true"], .tn-spinner[aria-hidden="false"], once the content is loaded, toggle to false and true respectively.

Clicking a checkbox in a link causes the link to the followed — how can I avoid this?

I have checkbox insde a link. In all browsers except Chrome, when clicking on the checkbox you follow the link (instead of just having the checkbox become selected).
How do I avoid this behaviour?
Demo (hover over one of the product images to see the checkbox):
http://livedemo07571.prestatrend.com/category.php?id_category=9
And here’s the code in question:
<a href="http://livedemo07571.prestatrend.com/product.php?id_product=25" class="product_img_link">
<img src="http://livedemo07571.prestatrend.com/img/p/25-65-large.jpg" height="469" width="469" alt="Crew Neck Jumper" />
<span class="new">New</span>
<div class="right_block large">
<h3 class="large">Crew Neck Jumper</h3>
<span class="product_arrow"></span>
<p class="availability_container"><span class="availability">Available</span></p>
<span class="slash">/</span>
<p class="price_container"><span class="price" style="display: inline;">$2,390.00</span></p>
<p class="compare large"><input type="checkbox" class="comparator" id="comparator_item_25" value="comparator_item_25" /> <label for="comparator_item_25">Select to compare</label></p>
</div>
</a>
This isn't valid HTML (see report). The way to avoid this is, quite simply, to include only text or images inside an anchor tag, and move the checkbox outside. You could use some jQuery to add a click event to the box which would navigate to the next page.
If you want for-sure don't want to move it outside the <a> then you'd have to have an onclick="return false;" and add a listener with jQuery that toggles it when its clicked. I'm not sure if this would work in all browsers, and your best option is just to do it a standards friendly way.
I think this happen because you have the Div (block element) is inside the A (inline element) tag and by default the event will bubble up soon as you click the checkbox.
Even if HTML 5 has made the exception for the A tag and now allow a block element to be nested within that inline element. To get that working the same accross all browser you'll have to wait they all support the html 5 features
1) try with a different doctype
2) build the div outside the A has the link is not required to be executed