Would using aria-controls on a button to reference a jQuery UI dialog element that it opens be appropriate usage of ARIA?
I already notice that jQuery UI automatically wraps the div in another separate div that has role=dialog and aria-labelledby with the title of the dialog. I've seen aria-controls on relationships between tab links and tab panels as well as buttons and form elements (inputs, dropdowns, etc) but not between buttons and dialog boxes.
Here is a code example to demonstrate what I'm asking about:
$('button[aria-controls$="-dialog"]').on('click', function() {
var $dialog = $('#' + $(this).attr('aria-controls'));
$dialog.dialog('open');
});
$('.jqui-dialog').dialog({
modal: true,
autoOpen: false
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src=https://code.jquery.com/ui/1.11.4/jquery-ui.min.js></script>
<link rel=stylesheet type=text/css href=https://code.jquery.com/ui/1.11.4/themes/dot-luv/jquery-ui.css>
<button type=button aria-controls=add-new-item-dialog>
Add new item
</button>
<div id=add-new-item-dialog class=jqui-dialog title='Add new item'>
<p>A form here.</p>
</div>
In the jQuery current implementation, I don't think it's appropriate.
According to the ARIA documentation for "aria-controls"
http://www.w3.org/TR/wai-aria/states_and_properties#aria-controls
Identifies the element (or elements) whose contents or presence are controlled by the current element. See related aria-owns.
What does this mean?
This can be used for buttons which control a video, a slider, a tab panel, or whatever needs to be controlled.
When you focus such button, JAWS announces "Use Jaws key + alt + m to move to the controlled element". So if the element has display:none, it won't work. If you want to control the "presence" of an element you'll have to target a parent element or play with visibility/z-index CSS attributes.
The expected result is that the screen reader can lead you to the affected element, without requiring that you pressed the button.
In your case, you have, when the page loads, an ui-dialog with the css property display: none. So when the screenreader will announce that the button can affect another element, you won't be able to jump to it unless you effectively press it before as the element is not already in the visual flow.
TL;DR: the aria-controls attribute indicates which element can be affected by a control. The screen reader will give you a quick access to that element (without requiring you to effectively press the control itself). This requires that the visual focus could be moved to the targeted element.
Yes, using aria-controls for an element role button is allowed per the ARIA spec and your use case makes sense. It would be up to the user agent/assistive technology to provide whatever behavior it deems appropriate for this relationship (with JAWS you can use INSERT-ALT-M to navigate to the controlled element. If the dialog is not modal and the button toggles it, this provides useful navigational support. If the dialog is modal, it does not add much to the user experience).
What will improve the user experience in your case is if you aria-haspopup="true" to indicate that the button will spawn a popup. This will then be announced to screen reader users.
Here Leonie Watson (ARIA WG member) specifically uses a button as an example for ARIA controls and talks about how screen readers currently (as of the writing) support it http://tink.uk/using-the-aria-controls-attribute/
Related
It's common for password inputs to have a show/hide button but I'm finding little content on the web about any accessibility concerns relevant to them - should I be attaching any kinds of ARIA attributes to the button or password input? Does it make sense for that to be a checkbox or is a button that triggers JS to achieve the effect fine too?
Not sure what I should be looking out for as someone not very steeped in, but wanting to understand accessibility best practices.
Interesting question.
This is perhaps the most relevant bits of litterature I could find on the subject:
A disclosure is a button that controls visibility of a section of content. When the controlled content is hidden, it is often styled as a typical push button with a right-pointing arrow or triangle to hint that activating the button will display additional content. When the content is visible, the arrow or triangle typically points down.
(and)
The element that shows and hides the content has role button.
When the content is visible, the element with role button has aria-expanded set to true. When the content area is hidden, it is set to false.
Optionally, the element with role button has a value specified for aria-controls that refers to the element that contains all the content that is shown or hidden.
See https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure
I'm no usability expert at all but it doesn't seem crazy to see a connection with your use case. So to answer your question here are the ARIA attributes I'd apply, along with some JavaScript.
function toggle_visibility(el) {
const control = el.getAttribute('aria-controls');
const expanded = el.getAttribute('aria-expanded') === 'false';
document.querySelector(`#${control}`).type = expanded ? 'text' : 'password';
el.setAttribute('aria-expanded', String(expanded));
el.textContent = expanded ? 'hide' : 'show';
}
document.querySelector('button').addEventListener('click', ({
target
}) => toggle_visibility(target));
<div>
<label for="password">password</label>
<input type="password" id="password">
<button aria-controls="password" aria-expanded="false">show</button>
</div>
And here's a screencast of the Chrome Dev Tools. Note how in the Accessibility panel we're able to refer to the password control.
As none of the so-far given answers cover the topic completely, I'll try to do it here.
You have at least three possibilities:
A checkbox;
A toggle button;
A button with changing text.
Checkbox
This is the easiest solution.
<label><input id="showPwd" type="checkbox"> Show Password</label>
And then you add some JavaScript to change your password input type from password when the check box is cleared to text when it is checked.
From the user perspective this solution is also the most obvious: both screen reader users and users of other assistive technologies usually easily perceive the state of the check box. Another benefit of this approach is that the status is seen immediately with no need to re-check it with additional commands.
Toggle Button
This involves some ARIA, not much, though.
<button id="showPwd" aria-pressed="false">Show password</button>
This way you will need to change both the button CSS styling and the value of the aria-pressed attribute in your JavaScript (and, of course, change the type of your password input accordingly).
From the user perspective, this approach has both advantages and drawbacks. the main advantage for screen reader users is that when the button is pressed, the user will hear "Show Password button pressed" vs. "Show password button" which would help to spot the status of the button for users with hearing and/or cognitive issues better and faster.
Button with Changing Text
An easy, but kind of oldish solution. You basically have a <button> that says "Show Password", and when the password is revealed, the button would say "Hide Password".
This is the worst solution from the assistive technology user perspective, since a screen reader would not notify its user automagically when the text on the button changes (unless you add some additional black magic like ARIA alerts, but it's not worth it, I'm sure). Typically when the text changes, the user should re-check the status with a command that announces the current line or object.
There is another drawback that spans both button solutions: screen readers usually have a possibility to navigate by element and to spawn lists of elements of the same type. In this case a confusion may occur as the form contains more buttons (at least the submit one). With check boxes the chance of such confusion is much lower, if the form is not crowded with many check boxes.
Say you have website, and when you refresh it, if you check which element has focus, using this code:
setInterval(function () {
console.log('ID: ', $(document.activeElement).attr('id') + ' Tag: ' + $(document.activeElement).prop('tagName')
+ ' C: ' + $(document.activeElement).attr('class'));
}, 2000);
You will see that tag of element is 'BODY' element. How do you restore focus to the same element using javascript because, things like $('body').focus(); doesn't work.
EDIT: It should also reset the 'focus' flow of document. So after you click TAB it will focus the same element as if you would refresh page then click TAB.
EDIT 2: I need to reset focus on some action like keyDown, to default state - the state after you load page. From my research I know that element focused after you load page is 'body', and then after you click Tab, the first element in focus flow of your website is focused. I can't do that using $('body').focus(); - it doesn't focus body and doesn't reset current flow of focus of document.
EDIT 3: So far We have managed to somehow reset focus to body element of website using this code: document.activeElement.blur(); so my above script would say that body is focused, but it doesn't reset the actual flow of current focus, when you use keyboard to navigate website (TAB button). It's possible to have workaround and select specified element you want, but that's not answer to question. What is general purpose mechanism to reset flow of keyboard navigation of website to default state, without refreshing a page?
EDIT 4: https://jsfiddle.net/4qjb5asw/5/
Rather than specifically focusing the <body>, you can clear the focus of the active element by doing document.activeElement.blur(); This should restore focus to the <body> element and reset the focus flow.
Click around in the snippet below to see what the current document.activeElement is.
$("#reset").on("click", function() {
document.activeElement.blur();
logActiveElement();
});
$("form").children().on("focus", logActiveElement);
function logActiveElement() {
console.log("The active element is now: " + $(document.activeElement).prop('tagName'));
}
.blur {
padding: 5px;
border: 2px solid blue;
margin-top: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input type="text" />
<textarea></textarea>
<button type="button">Test Button</button>
</form>
<div class="blur">
<button id="reset">RESET ACTIVE ELEMENT</button>
</div>
If you want granular control of focus, consider using the tabIndex [1] property. Basically, it allows you to specify tab order by assigning numbers to each of the elements which can have focus. An added benefit is that tabIndex enables you to specify elements to take which may not be by default--exactly what you need.
Tab order and focus is extremely important for accessibility, since User does not have a pointing device. So, this tag is designed to give you precise control of focus. It enables you to specify all focus-able elements on your page. In your case, it provides a handle to all focus-able elements, enabling you to specifically set focus where you need it, when you need it.
Not sure which framework you're using, but jQuery is a candidate for cross-browser compatibility.
From the jQuery .focus() page:
In recent browser versions, the event can be extended to include all
element types by explicitly setting the element's tabindex property. [2]
Perhaps .focus() based on tabindex; Something like this:
$('input[tabindex='+ntabindex+']').focus(); // [3]
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
https://api.jquery.com/focus/
https://forum.jquery.com/topic/how-to-focus-on-next-object-according-to-tab-index-not-object-order-on-page
Just be sure to check your tab order if using this feature. Tab through your page and make sure you've got all the focus-able elements properly ordered.
Sounds like you are just looking to reset the default focus for the document on certain events like keydown on a particular key so that the user can begin tabbing through the document from the top after triggering the event.
You can reset the default focus for the document with:
document.activeElement.blur()
You can add any event listener you want to trigger the function. For the keydown event on the escape key it would be:
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
document.activeElement.blur()
}
});
IMPORTANT: Note that if you are testing this in an embedded tool like an SO snippet or jsfiddle, you will need to add a little more code to set the focus to the element wrapper for that tool to emulate what would happen if the display area represented the entire document (otherwise, you will reset the focus to the main document and in the case of SO you will start tabbing through the menus, question, answers, comments, and other elements on the page before you get to the snippet).
Example snippet for testing that will reset the focus to the wrapper element in the snippet so that tabbing will restart from the beginning of the snippet (and not from the beginning of the document for this SO page):
/*
* All code below referencing the "snippet" variable is only
* here to help us pretend that the snippet result
* represents the entire document (not needed in a
* regular implementation).
*/
const snippet = document.querySelector('#snippet');
snippet.focus();
const btn = document.querySelector('#button1');
const resetFocus = () => {
document.activeElement.blur();
snippet.focus();
}
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
resetFocus();
}
});
btn.addEventListener('click', (event) => {
resetFocus();
});
<!--
The "snippet" wrapper div with tabindex attribute
is only here to help us pretend that the snippet result
represents the entire document (not needed in a regular
implementation). The tabindex helps ensure that the
wrapper div is focusable.
-->
<div id="snippet" tabindex="0">
<div>Tab to or select input to focus. To reset focus:
</div>
<ul>
<li>Press escape key.</li>
<li>Or, click "Reset" button.</li>
</ul>
<div>
<input id="input1" type="text" name="input1" />
</div>
<div>
<input id="input2" type="text" name="input2" />
</div>
<button id="button1">Reset</button>
</div>
Your question can be splitted into three parts
find the default focus element of a website.
focus on a element
control of next focus element
If you have control to the element on screen, or if it is predictable static. a simple document.querySelector("#id").focus() will focus the default element.
If it is not the case, you need some detection which could be complicated.
Browser will first find element with smallest positive non-zero [tabIndex] number.
Than if #anchor is present, it use first focusable element after it.
Otherwise first focusable element a[href],button,input,object,area[href] from body.
As you might not want to focus on the element, but want it to be focus next.
You can inject a invisible element just before it, with anoptional tabIndex hack, and focus on your invisible element.
and dismiss the injected element on blur.
I have a header div element at the top of the page. Each time new content is loaded onto the page, I want to move focus back to the header, given it is at the top of the document,
<div tabindex="-1" role="header">
I always want this to be tabbed first
</div>
The important point is setting tabindex to -1 which removes it from the default focus flow of the browser and allows me to then programmatically set focus using javascript.
document.querySelector('div[role="header"]').focus();
This snippet is ran every time new content is shown and means people using keyboards get a consistent start point for each page in a single page application.
Now when I hit the tab key, the <a> tag is always focussed first.
this will keep your specified element focused
$(document).ready(function(){
setInterval(function(){
var focusbox = document.getElementById("element_to_focus");
focusbox.focus();
});
})
The "snippet" wrapper div with a tabindex attribute
is only here to help us pretend that the snippet result represents the entire document (not needed in a regular implementation). The tab index helps ensure that the wrapper div is focusable.
Create a link with href="#" and style="display: none; position: absolute;" at the beginning of your body.
When you want to reset the focus: show the element, focus it and hide it back.
If we don't manually set any of the HTML elements to be focused using javascript, how does HTML decide which element to be focused?
It is done via tabindex attribute. By default it goes through focusable elements by position in page, but you can modify this behaviour.
From linked article:
Focusing non focusable elements:
tabindex=0
When tabindex is set to 0, the element is inserted into the tab order based on its location in the source code. If the element is focusable by default there’s no need to use tabindex at all, but if you’re repurposing an element like a span or div, then tabindex=0 is the natural way to include it in the tab order.
Ignore some focusable elements:
tabindex=-1
When tabindex is set to a negative integer like -1, it becomes programmatically focusable but it isn’t included in the tab order.
And finally: choose by yourself the order, no matter position of the element:
tabindex=1+
It imposes a tab order on the content that bears no resemblance to the expected tab order.
If you mean "What tells the browser which elements can be focused?" then you are looking for the tabindex attribute. Adding this to an element will allow an input device (i.e. mouse, keyboard) to trigger a focus state on the element.
If your question is basically, "how are things focused on?", this is done using an input device, i.e. mouse, and keyboard.
if you mean when the page loads you can use the autofocus attribute
<input type="text" autofocus>
There is actually no element that gets the focus by default.
We can check this pretty easily by creating a simple site and log
document.querySelectorAll(":focus")
to the console.
You will see that it will return an empty array, meaning that no element is focused.
The document.activeElement attribute:
Returns the currently focused element, that is, the element that will get keystroke events if the user types any
This is clear for a single browsing context (single document + window). If a focusable element (input, textarea contenteditable element) has a visible selection, it will be the document.activeElement element.
When you have one or more iframes on the page and they have focusable elements, the behavior changes.
If an input in an iframe is focused, the parent document’s activeElement property will point to that iframe’s iframe element (the actual <iframe> node). However, any previously selected text in focusable elements will stay visibly selected, though it visually appears inactive (greyed-out):
If you then focus back to the input in the parent document, the iframe’s input will no longer be the iframe document’s activeElement, and it will also appear visibly selected though inactive:
How can I check for any focusable elements in a document with a visible selection, even when it isn’t the document’s activeElement?
Edit
I’ve made a demo playground to illustrate the behavior: http://codepen.io/acusti/pen/PGakzY?editors=0010
You can store the .activeElement or selection in an array; for example at an event, then select the element or selection at index array.length - 2 of the array.
See also How to clear the contents of an iFrame from another iFrame , How to pass css :active pseudo class to javascript?
#guest271314 pointed me in the right direction for solving this issue. As illustrated in this demo, using document.getSelection() allows you to find any focusable elements in a browsing context with a visible selection, even when they are “inactive” because a different browsing context (or another browser window or part of the browser) is active. The getSelection() API does require a bit of extra logic to deal with the different kind of things it can return. But for the purposes of identifying a selection with a focusable element (input, textarea), after using document.getSelection() and then checking anchorNode in the returned selection object for childNodes && childNodes.length, you can reliably get the element with the selection by accessing selection.anchorNode.childNodes[selection.anchorOffset].
I have following structure of web application, for which I'm automating testing:
<em unselectable="on" class="x-btn-split">
<button type="button" class="x-btn-text " id="ext-gen523" title="Add Options">Add</button>
</em>
Both objects has event listeners,added viw JavaScript in some linked js-file (I don't know what file exactly).
After clicking on button "Add" with mouse, calls AJAX function calls and row adding to some table, after clicking on em element, opens application menu.
I try to reproduce clicking on button ADD via VBScript (as I say, I'm automating testing of this application),instead of adding row into table, application menu opening. But I know exactly, that I'm clicking button Add, but not em-element.
PseudoCode, that do so:
set obj = domHelper.GetElementByClassName(container,"x-btn-text","button")
obj.click
Please, let me know If you know reasons for this behavior, because I'm really don't know what to think. (I've tried many different approaches to fix this, but nothing helped)
Thank you!
click, like most events, ‘bubbles’ up through all ancestor elements. When you click the <button>, yes, you're also clicking the <em> that contains it.
If the event handler for the <em> needs to know that the click was directly in the content area of the <em> but not any of its children, it should look at the event.target (srcElement in IE<9) property to check that the click is on itself, or not on the button, before opening a menu. Alternatively, if the button's click handler calls event.stopPropagation() (cancelBubble= true in IE<9), then the event will stop and not pass up any further.