ARIA for Elements Without Focus - html

I'm struggling to enable proper ARIA support for this case. I have a input field which works like a filter, and a set of elements which will be filtered by this input field. The focus is always on the input field, and with arrow up and down you can navigate through the result set. The input needs constant focus because whenever I start typing again, the input should be updated and filter the result set.
Now I want that my screen reader reads the name of the elements when I navigate through the result set. But if I press arrow down (or up) the reader repeats the full part of the input field.
Hint: The result set contains images and text and will open the element in a new view when it is clicked.
<input ng-change="$ctrl.doFilter()" ng-keydown="$ctrl.handleKeydown($event)">
<div class="filter-results" role="list">
<div ng-repeat="item in $ctrl.results track by $index"
ng-class="($index == $ctrl.selectedItem ? 'item-selected' : '')"
ng-click="$ctrl.navigateToSelected()"
ng-mouseover="$ctrl.selectItem($index)"
role="listitem"
<div ng-bind-html="$ctrl.displayName(item)"></div>
</div>
</div>
(shortened example)
HandleKeypress just sets the id of selected item, which will be highlighted by using the proper class.
Is there any solution that screen readers read the name (displayName) of the selected item?

One way to do it, and I'm not advocating this is the best way, but it seems to work, is to have an onkeydown handler for your input field (which you may already have) and when the up/down arrow key is pressed (which it sounds like you're already listening for), you can update a visually hidden <span> (or <div>) that has aria-live set to "polite" and update the text within that <span> with the text of your result item. I think the screen reader will still read the contents of your input field but it should also read the aria-live text too. Maybe not the ideal solution, but you'll get your result item announced.
Some (very) rough code:
<span id="result" aria-live="polite" class="sr-only"></span>
<script>
function mykeydown(e) {
if (e.keyCode == 40)
document.getElementById("result").innerHTML = "whatever is the next result";
}
</script>
Note: You can see the "sr-only" class here - What is sr-only in Bootstrap 3?

Related

Adding role="listitem" out of role="listbox"

While working on updating a webpage, I have a control that is basically a multiselect control, looks like the below image.
Once the user sets focus on the textbox (the one with purple border, the box below gets visible, wherein on check of the items, the same gets appended to the above text box.
Question:
My question is more from the Accessibility perspective. To allow assistive technologies to read (narrator) this control properly. I am using role="listbox" to the text box and role='listitem' to each of the checkboxes, which I understand it wrong way since listitem should be added as direct child to listbox. But in my case it is not possible.
Is there any way I can link up the textbox and checkbox list and make narrator to treat them as a single control?
<div class='multiselect_wrapper'>
<input type="text" role='listbox' aria-multiselecttable='true' />
<div class="chkList">
<fieldset>
<div>
<label role="listitem" for='chk1'>
<input type='checkbox' id='chk1'>Pizza
</label>
<label role="listitem" for='chk2'>
<input type='checkbox' id='chk2'>Lemonade
</label>
<label role="listitem" for='chk3'>
<input type='checkbox' id='chk3'>Fruit Salad
</label>
</div>
</fieldset>
</div>
</div>
Here's a quick example of a very simple checkbox list:
codepen .io /anon/pen/eboEVB
This is just a quite incomplete example. Do inspire from it, but don't copy-paste it as is.
A few points of attention:
As you have effectively mentionned, the items having role=listitem must be child of the element with role=listbox. This is an obligation, meaning that you have no choice but leave the textbox apar if you want to have correct and accessible code.
If you set role=listitem to the checkboxes, it will override the default implicit role=checkbox. The consequence are that the screen reader won't see them as checkboxes anymore, and thus will keep reading their labels but no longer their state (checked or unchecked). You must set the role=listitem to another element for which there is no default implicit role, such as the divs used in the example
Note how the focus is given to checkboxes rather than to listitems. In this way, we obtain for free normal keyboard processing (i.e. spacebar toggles), as well as the default behavior screen reader have with checkboxes (i.e. announce label and state + help like "press spacebar to change state")
You are recommanded to set the attributes aria-posinset and aria-size to each item, otherwise the screen reader might not see your listbox as such
Set tabindex=-1 in all the items except the currently selected one, which must have tabindex=0. Usually in listboxes, the keyboard user don't tab on each item, and use arrow keys to go from one item to another once they are in the list box. The example is incomplete, it would be nice to support home, end, page up and page down as well.
Don't forget to keep the aria-descendant attribute of the listbox in sync with selection changes. This is important because screen readers use it to tell which item is currently selected
Edit: I'm unable to post my HTML+js code, please help!
I'm trying to post a link to codepen but it is refused. The code is quite long but anyway, if I post it indented with 4 spaces (using Ctrl+K), it is still interpreted instead of just be shown.

Tracking Link Click on Google Tag Manager

I want to track clicks on the following button/link with Google Tag Manager. I created a trigger in Google Tag Manager that triggers when the element_id = 100. This works fine, except that when I click exactly on the text, it doesn't do anything, the link looks like a button, with the text in the middle of it. I can't change anything to the html or css, otherwise I can think of multiple things, so I need to find a solution without changing the html. Also, the 'myclass' class and the 'label' class get used in other elements.
<a class="myclass" id="100" href="http://www.url.com">
<span class="label">Text</span>
</a>
Anyone an idea?
Thanks a lot,
The following workaround worked:
Create trigger when element text contains "Text". This will trigger events on the button and the label on the button, of all buttons with "Text" as label.
Create tag for that trigger that checks with simple javascript if either the id of the current element = 100, which will happen when you click the button but not the label, or that the id of the parent = 100, which happens when you click the label. You can get the element that triggered the tag using the built-in variable "Click Element". Which you need to access the parent element.
Technically, you shouldn't have a CSS ID that starts with (or is) a number, so not sure if your code example is accurate or not. Whatever the case, you're probably better off using "matches CSS selector" so that you don't need to use any custom JS.
If indeed your HTML uses id="100", then the above will work. If it's anything else that doesn't start with a number, then you can use
#whatever > span

How to set the focus on text (no form)

I know about HTML5's autofocus attribute, but AFAIK it only applies to input tags.
Is there a way to set the focus automatically (and preferably without JS) on, say, a scrollable div with text, so that the viewer can immediately scroll using keyboard ? Or would it be browser-specific ?
I can't find useful ressources online so I'm wondering if "focus" if the right word.
<div tabindex='0'>
this will receive the focus immediately
</div>
tabindex="0"
The tabindex value can allow for some interesting behaviour.
If given a value of "-1", the element can't be tabbed to but focus
can be given to the element programmatically (using element.focus()).
If given a value of 0, the element can be focused via the keyboard
and falls into the tabbing flow of the document.
Values greater than 0 create a priority level with 1 being the most
important.
For more info you can look at the following link http://snook.ca/archives/accessibility_and_usability/elements_focusable_with_tabindex
UPDATE
The other option is to try something like this.
Add the following code to the body tag, substituting the form and field names for your own:
<body OnLoad="document.myform.mytextfield.focus();">
<form name="myform">
<input type="text" name="mytextfield">
<button type="button" onclick="javascript:alert('testing')" name="myButton">Click Me!</button>
</form>

Form enter key action with lists and AngularJS

In my AngularJS project I have an account details page where you can change your personal account information. This page allows for multiple phone numbers and e-mailaddresses to be supplied. Using mouse input (or tabbing to buttons and pressing them with space bar) works perfectly, however I'd like to add the convenience of the enter key pressing the 'logical' buttons.
My form looks like (accidentally forgot to translate a few items):
A simplified version of the HTML for the form can be found on PasteBin, I've mainly removed the directives for managing the lists.
All buttons are <button> elements except for the cancel button which an <a> to the previous page, and the submit button is <button type="submit">.
When selecting any text box and pressing enter, the first (non-disabled) <button> element is 'clicked'. Meaning if I would change the last name, hit enter, the first phone number would be removed.
When you're in a new entry of phone numbers or e-mailaddresses (the row with the green + button) it should click that button, and if it's disabled do nothing.
When you're in any other text box on the form it should hit the save button, and also if the save button's disabled, do nothing.
Both buttons will be disabled based on form validation.
There'd be no trouble in changing the type of a button from button to submit if that'd help.
I would preferably have an all HTML solution, using just semantics, but I doubt that's really possible. So the logical alternative would be to use an AngularJS directive.
Please do not provide a jQuery or plain JavaScript solution relying on IDs or something like that. I don't want to hack my way around AngularJS, rather embrace it.
In the meantime I've worked on a directive that allows me to declare what I've called 'submit scopes'.
In essence you have actions (inputs) and targets (buttons), they're bound through a service by a key you can assign in the template. To avoid keys from clashing and from simple annoying work you can create a submit-scope which will cause it's children to prepend a unique key to the value they're accessing.
Within a submit-scope you can still override an action to use a global key instead by setting the attribute global-submit="true".
Example code:
<div submit-scope>
<input type="text" submit-action />
<button type="button" submit-target>Pressing enter in the above field will click this button.</button>
</div>
You can view the entire source code and a slightly larger example on Plnkr.
I just tried to replace
<button>Cancel</button>
with
<input type="button" value="Cancel">
and it seems to work correctly...

Make an HTML element non-focusable

Is it possible to make an HTML element non-focusable?
I understand that a list of elements that can receive focus can be defined and that a user can navigate through these elements by pressing a Tab key. I also see that it is up to the browser to control this.
But maybe there is a way to make certain elements non-focusable, say I want a user to skip a certain <a> tag when pressing a Tab.
unfocusable
A negative value means that the element should be focusable, but should not be reachable via sequential keyboard navigation.
See also: developer.mozilla.org
To completely prevent focus, not just when using the tab button, set disabled as an attribute in your HTML element.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<input class="form-control" type="text"> Click this, you can see it's focusable.
<input class="form-control" type="text" readonly> Click this, you can see it's focusable.
<input class="form-control" type="text" readonly tabindex="-1"> Click this, you can see it's focusable. Not tab'able.
<input class="form-control" type="text" disabled> Click this, you can see it's <strong>not</strong> focusable.
In order to make an prevent an element from taking focus ("non-focusable"), you need to use Javascript to watch for the focus and prevent the default interaction.
In order to prevent an element from being tabbed to, use tabindex=-1 attribute.
Adding tabindex=-1 will make any element focusable, even div elements. This means when a user clicks on it, it would likely get a focus outline, depending on the browser..
You would ideally, want this:
/**
* #this {HTMLElement}
* #param {FocusEvent} event
* #return {void}
*/
function preventFocus(event) {
if (event.relatedTarget) {
// Revert focus back to previous blurring element
event.relatedTarget.focus();
} else {
// No previous focus target, blur instead
this.blur();
// Alternatively: event.currentTarget.blur();
}
}
/* ... */
element.setAttribute('tabindex', '-1');
element.addEventListener('focus', preventFocus);
For safe typechecking, you can perform if (event.relatedTarget instanceof HTMLElement) instead if (event.relatedTarget).
TabIndex is what your looking for: http://www.w3schools.com/jsref/prop_html_tabindex.asp.
When you set a tabIndex value to -1 you will skip it when tabbing through your form.
In case you are looking for a global solution:
Link
document.body.addEventListener('focusin', (e) => {
if (e.target.classList.contains('__nofocus')) {
e.relatedTarget ? e.relatedTarget.focus() : e.target.blur();
}
});
It should work for anchors, buttons and anything else that can receive focus by default. Don't forget to set tabindex="-1" as well as the element would be unpassable by Tab-key navigation.
For the element you do not want to be focused on tab, you have to put the tabindex as a negative value.
I used focusable="false", because tabindex="-1" was not working in IE.
Making a focusable-by-default HTML element a non-focusable one isn't possible without JavaScript.
After diving into focus-related DOM events, I've came up with the following implementation (based on the #ShortFuse's answer, but fixed some issues and edge cases):
// A focus event handler to prevent focusing an element it attached to
onFocus(event: FocusEvent): void {
event.preventDefault();
// Try to remove the focus from this element.
// This is important to always perform, since just focusing the previously focused element won't work in Edge/FF, if that element is unable to actually get the focus back (became invisible, etc.): the focus would stay on the current element in such a case
const currentTarget: any | null = event.currentTarget;
if (currentTarget !== null && isFunction(currentTarget.blur))
currentTarget.blur();
// Try to set focus back to the previous element
const relatedTarget: any | null = event.relatedTarget;
if (relatedTarget !== null && isFunction(relatedTarget.focus))
relatedTarget.focus();
}
// Not the best implementation, but works for the majority of the real-world cases
export function isFunction(value: any): value is Function {
return value instanceof Function;
}
This is implemented in TypeScript, but could be easily adjusted for plain JavaScript.