Implementing keyboard accessibility (HTML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Looking for the C#/VB/C++/XAML version of this topic? See Implementing keyboard accessibility (XAML).

Many users who are blind or have mobility issues rely on the keyboard as the sole means of navigating your app UI and accessing its functionality. If your app does not provide good keyboard access, these users will have difficulty using your app, or may not be able to use it at all.

This topic describes the markup and code that you need to implement to ensure that your Windows Store app using JavaScript is accessible to keyboard users.

See this feature in action as part of our App features, start to finish series: User interaction: Touch input... and beyond.

Initial keyboard focus

When your app starts, set initial keyboard focus on the element that users will intuitively (or most likely) interact with first. Typically, the most appropriate location is the main content view of the app so that a user can immediately scroll the content using the arrow keys. For more info on setting focus to specific controls, see focus.

Keyboard navigation among UI elements

You should set the tabIndex attribute to a value that is greater than or equal to 0 for all interactive UI elements that are not in the tab order by default. Setting the tabIndex attribute is important because screen reader users navigate among interactive UI elements by using the Tab key.

The following example includes the top tree element in the tab order. Information about the current tree item is exposed with the aria-activedescendant attribute.

<!-- The top tree item must be added to the tab order. -->
<div id="folders" role="tree" aria-label="Folders" tabindex="0"
    aria-activedescendant="n-0" >
...
    <div id="n-0" class="selected" role="treeitem" aria-expanded="true" 
            onclick="...">
...
</div>

Note that focusable HTML tags like a href="…", input, and select are in the tab order by default.

The following rules apply to the tabIndex attribute.

  • UI elements with tabIndex equal to 0 are added to the tab order based on document order.
  • UI elements with tabIndex greater than 0 are added to the tab order based on the tabIndex value.
  • UI elements with tabIndex less than 0 are not added to the tab order, but can receive keyboard focus.

Note  Do not give text elements (such as p tags) a tabIndex greater than or equal to 0. Text isn't typically interactive and should not have the connotation that setting focus to it will enable some action that's possible. Text elements will still be detected by assistive technologies, and read aloud in screen readers, but that relies on techniques other than finding those elements in the practical tab order.

 

Keyboard navigation within a UI element

For composite elements, it is important to ensure proper inner navigation among the contained elements. You can implement inner navigation for a composite element in several ways.

A composite element can manage its current active child to reduce the overhead of having all children be focusable. Such a composite element is included in the tab order, and it handles keyboard navigation events. The composite element exposes information about the currently active child element by using the aria-activedescendant attribute.

<!-- Custom tree view element. -->
<div id="folders" class="tree" role="tree" aria-label="Folders" tabindex="0"
        aria-activedescendant="n-0" >
    <div id="n-0" class="selected" role="treeitem" aria-expanded="true" 
            onclick="...">
        Libraries</div>
    <div role="group" >
        <div id="n-0-1" role="treeitem" aria-expanded="false" onclick="…">Docs</div>
        <div id="n-0-2" role="treeitem" aria-expanded="false" onclick="…">Music</div>
        <div id="n-0-3" role="treeitem" aria-expanded="false" onclick="…">Pics</div>
    </div>
</div>

<script>
    var folders = document.getElementById('folders');
    // Ensure keyboard navigation/operation with arrow keys.
    folders.addEventListener('keydown', function(e) {
        var itm = e.srcElement;
        if (e.keyCode === Win.Utilities.Key.upArrow) {
            // Update aria-activedescendant with the previous node id.
            // Update the class attribute to mark the selected node.
        } else if (e.keyCode === Win.Utilities.Key.downArrow) {
            // Update aria-activedescendant with the next node id.
            // Update the class attribute to mark the selected node.
        }
    });
</script>

An alternative approach is to dynamically manage the tabIndex attribute for the node. This method is known as providing a "roving index".

<!-- Custom tree view element. -->
<div id="folders" role="tree" aria-label="Folders" >
    <div id="n-0" role="treeitem" aria-expanded="true" tabindex="0">Libraries</div>
    <div role="group" >
        <!-- Child tree items: Documents, Music, Pictures ... -->
        <div id="n-0-1" role="treeitem" aria-expanded="false" tabindex="-1">Docs</div>
        <div id="n-0-2" role="treeitem" aria-expanded="false" tabindex="-1">Music</div>
        <div id="n-0-3" role="treeitem" aria-expanded="false" tabindex="-1">Pics</div>
    </div>
</div>

<script>
    var folders = document.getElementById('folders');
    // Ensure keyboard navigation/operation with arrow keys.
    folders.addEventListener('keydown', function(e) {
        var itm = e.srcElement;
        if (e.keyCode === Win.Utilities.Key.upArrow) {
            // Update tabindex attributes.
        } else if (e.keyCode === Win.Utilities.Key.downArrow) {
            // Update tabindex attributes.
        }
    });
</script>

As shown in the previous two examples, the recommendation is to use arrow keys as keyboard shortcuts for navigating among child elements. If tree view nodes have separate subelements for handling expand–collapse and node activation, you should use the left and right arrow keys to provide keyboard expand–collapse functionality.

Keyboard activation

Ensure that UI elements that can be clicked can also be invoked with the keyboard.

For UI elements that can be invoked, implement keyboard event handlers for the Space and Enter keys. This makes the basic keyboard accessibility support complete and enables users to accomplish basic app scenarios by using only the keyboard; that is, users can reach all interactive UI elements and activate the default functionality.

<!-- Custom tree view element. -->
<div id="folders" role="tree" aria-label="Folders" aria-activedescendant="n-0" 
        tabindex="0" >
    <div id="n-0" role="treeitem" aria-expanded="true" onclick="..." >Libraries</div>

    <div role="group" >
        <!—Child tree items: Documents, Music, Pictures ... -->
        <div id="n-0-1" role="treeitem" aria-expanded="false" onclick="...">Docs</div>   
        <div id="n-0-2" role="treeitem" aria-expanded="false" onclick="...">Music</div>
        <div id="n-0-3" role="treeitem" aria-expanded="false" onclick="...">Pics</div>
    </div>
</div>

<script>
    // Ensure keyboard activation
    folders.addEventListener('keydown', function(e) {
        if (e.srcElement && (e.keyCode === Win.Utilities.Key.enter ||
                e.keycode === Win.Utilities.Key.space)) {
            e.srcElement.click(e);
        }
    });
</script>

Keyboard shortcuts (optional)

In addition to implementing keyboard navigation and activation for your app, it is a good practice to implement shortcuts for your app's functionality. A shortcut is a keyboard combination that enhances productivity by providing an efficient way for the user to access app functionality.

An access key is a shortcut to a piece of UI in your app. Access keys consist of the Alt key and a letter key.

An accelerator key is a shortcut to an app command. Your app may or may not have UI that corresponds exactly to the command. Accelerator keys consist of the Ctrl key and a letter key.

It is imperative that you provide an easy way for users who rely on screen readers and other assistive technology to discover your app's shortcut keys. You should declare your shortcut keys in your app's HTML markup by using the accesskey and x-ms-acceleratorkey attributes, and communicate shortcut keys by using tooltips, accessible names, accessible descriptions, or some other form of on-screen communication. At a minimum, shortcut keys should be well documented in your app's help content.

<!-- Alt+F sets focus to firstName and is exposed by the -->
<!-- AccessKey UIA property.                             -->
<label for="firstName">First Name</label>
<input id="firstName" type="text" accesskey="F" /> 

<!-- Alt+S invokes the Save button and is exposed by the -->
<!-- AccessKey UIA property.                             -->
<button accesskey="S">Save</button>

<!-- Ctrl+S invokes the Save button and is exposed by a tooltip. -->
<button id="sendButton" value="Send" title="Send (Ctrl+S)">Send</button>

The framework supports shortcut key functionality for setting focus or invoking elements, but you must implement shortcut keys in JavaScript by using keyboard event handlers.

<script>
    var sendButton = document.getElementById('sendButton');
    sendButton.addEventListener('keyup', function(e) {
        var itm = e.srcElement;
        if (e.ctrlKey && e.keyCode === 83 ) {
            // Invoke send functionality.
        }
    });
</script>

You must also consider shortcut keys during localization. Localizing shortcut keys is especially important because selecting a shortcut key typically depends on the label for the element.

<script>
    var sendButton = document.getElementById('saveButton');
    sendButton.innerHTML = res.getString('saveButton');
    sendButton.setAttribute('accesskey',res.getString('saveButtonAccessKey');
</script>
<!-- resources.resw -->
    <data name="saveButton" xml:space="preserve"><value>Save</value></data> 
    <data name="saveButtonAccessKey" xml:space="preserve"><value>S</value></data>

For more guidance about implementing shortcut keys, see Shortcut keys in the Windows User Experience Interaction Guidelines.

Keyboard accessibility and Windows Phone

A Windows Phone device typically doesn't have a dedicated, hardware keyboard. However, a Soft Input Panel (SIP) can support several keyboard accessibility scenarios. Screen readers can read text input from the Text SIP, including announcing deletions. Users can discover where their fingers are because the screen reader can detect that the user is scanning keys, and it reads the scanned key name aloud. Also, some of the keyboard-oriented accessibility concepts can be mapped to related assistive technology behaviors that don't use a keyboard at all. For example, even though a SIP won't include a Tab key, Narrator supports a touch gesture that's the equivalent of pressing the Tab key, so having a useful tab order through the controls in a UI is still an important accessibility principle. Arrow keys as used for navigating the parts within complex controls are also supported through Narrator touch gestures. Once focus has reached a control that's not for text input, Narrator supports a gesture that invokes that control's action.

Keyboard shortcuts aren't typically relevant for apps on Windows Phone, because a SIP won't include Control or Alt keys.

Meeting basic accessibility requirements