Accessible DOM scripting with ARIA Léonie Watson LJWatson.co.uk @LeonieWatson 1
May 08, 2015
LJWatson.co.uk @LeonieWatson 1
Accessible DOM scripting with ARIA
Léonie Watson
LJWatson.co.uk @LeonieWatson 2
Overview
• Anatomy of a rich internet application• First principles• Web accessibility stack• Best practices• Walkthrough examples
LJWatson.co.uk @LeonieWatson 3
Anatomy of a rich internet application
LJWatson.co.uk @LeonieWatson 4
Controls and widgets
• Controls are single elements, like checkboxes, buttons or input fields
• Widgets are composites of multiple elements, like sliders, accordions or modal dialogs
LJWatson.co.uk @LeonieWatson 5
Applications
• Rich internet applications are web applications with desktop characteristics
LJWatson.co.uk @LeonieWatson 6
First principles
LJWatson.co.uk @LeonieWatson 7
Perceivable
• Controls and widgets must provide information to assistive technologies
• Dynamic changes in content must be communicated to assistive technologies
LJWatson.co.uk @LeonieWatson 8
Operable
• Controls and widgets must be keyboard accessible
LJWatson.co.uk @LeonieWatson 9
Understandable
• Controls and widgets must be properly labelled and described
LJWatson.co.uk @LeonieWatson 10
Robust
• Controls and widgets must have reasonable backwards compatibility.
• Controls and widgets should have good forwards compatibility
LJWatson.co.uk @LeonieWatson 11
Web accessibility stack
LJWatson.co.uk @LeonieWatson 12
Layers in the web accessibility stack
• Assistive technology• Accessibility API• Accessible Rich Internet Applications (ARIA)• Document Object Model (DOM)
LJWatson.co.uk @LeonieWatson
Understanding the web accessibility stack: DOM layer
<img src="button.gif" alt="Accept" onclick="doSomething()" onkeypress="doSomething()">
13
LJWatson.co.uk @LeonieWatson 14
Understanding the web accessibility stack: ARIA layer
<img src="button.gif" alt="Accept" onclick="doSomething()" onkeypress="doSomething()" tabindex="0" role="button">
LJWatson.co.uk @LeonieWatson 15
Best practices
LJWatson.co.uk @LeonieWatson 16
Use native HTML elements
• Do this:<p><button>Button text</button></p>
LJWatson.co.uk @LeonieWatson 17
Don’t override native semantics
• Don’t do this:<p role="button">Button text</p>
• Do this:<span role="button">Button text</span>
LJWatson.co.uk @LeonieWatson 18
Managing keyboard focus
LJWatson.co.uk @LeonieWatson 19
Make controls and widgets focusable
• Elements with tabindex="0" are part of the natural tab sequence
• Elements with tabindex="-1" are not, but are focusable with scripting
• Elements with tabindex=">0" are never a good idea
LJWatson.co.uk @LeonieWatson 20
Make focus visible
• Do this:a:focus, a:hover, a:active { text-decoration: underline;}
• Don't do this:a { text-outline:none;}
LJWatson.co.uk @LeonieWatson 21
Providing keyboard accessibility
LJWatson.co.uk @LeonieWatson 22
Use appropriate event handlers• Do this:<span tabindex="0" role="button" onclick="doSomething()" onkeypress="doSomething()"> <img src="example.png" alt="Submit"></span>
• Don't do this:<span tabindex="0" role="button" onclick="doSomething()"> <img src="example.png" alt="Submit"></span>
LJWatson.co.uk @LeonieWatson 23
Don’t cause keyboard traps
• Don't do this:<input type="text" id="user" required onblur="if(this.value=='') {this.focus();}">
• Don’t do this either:onfocus = "this.blur()"
LJWatson.co.uk @LeonieWatson 24
Don’t change context on focus
• Don't do this:<select onchange="document.location=‘http://google.com';"> …</select>
LJWatson.co.uk @LeonieWatson 25
Handling widget focus
LJWatson.co.uk @LeonieWatson 26
Give widgets a single tab stop
• A widget represents a single tab stop, and other keys are used to interact with the widget itself
LJWatson.co.uk @LeonieWatson 27
Using JavaScript to handle child focus
• Set tabindex of current child to 0, and all other children to -1
• As the user moves to another child, update the tabindex for the previous and current children accordingly
• Use element.focus() to move focus to the child with tabindex set to 0
LJWatson.co.uk @LeonieWatson 28
Using ARIA to handle child focus
• Set the tabindex of the parent element to 0, and set its aria-activedescendant property to the id of the currently active child
• As the user moves to another child, update the aria-activedescendant property accordingly
LJWatson.co.uk @LeonieWatson 29
Put the key handler on the parent element
• Captures the keystrokes, determines which element takes focus next, and updates the aria-activedesendant property
LJWatson.co.uk @LeonieWatson 30
Use the scrollIntoView() method
• Or a purpose built function, to ensure the active descendant moves into view
LJWatson.co.uk @LeonieWatson 31
Providing keyboard shortcuts
LJWatson.co.uk @LeonieWatson 32
Don’t enable keyboard shortcuts by default
• Many assistive technologies are controlled with native shortcuts
• Custom shortcuts should be clearly documented
LJWatson.co.uk @LeonieWatson 33
Cancel (or swallow) keyboard events
• Prevent the key from executing an action outside of the widget or web application, unless focus is on a form field
LJWatson.co.uk @LeonieWatson 34
Dynamically updating content
LJWatson.co.uk @LeonieWatson 35
Using CSS to show/hide content
• Use CSS when the content makes sense as part of the page, not when it’s dependent on a certain action
LJWatson.co.uk @LeonieWatson 36
Injecting content into the DOM• Do this:<div id="errors"></div><script>var errors = document.getElementById("errors");var error = document.createElement("p");
error.appendChild(document.createTextNode("You must provide a username."));errors.appendChild(error);</script>
LJWatson.co.uk @LeonieWatson 37
Using innerHTML
• Do this:<div id="errors"></div><script>var html = "";
html = html + "<li>You must enter a username</li>";html = html + "<li>Please use at least one number in your password</li>";document.getElementById("errors").innerHTML = "<ul>" + html + "</ul>";</script>
LJWatson.co.uk @LeonieWatson 38
Walkthrough examples
LJWatson.co.uk @LeonieWatson 39
Expandable content (HTML/ARIA)<div id="collapsible"> <button id="tea" aria-controls="tea-content">Types of tea</button>
<div style="display:none;" id="tea-content" aria-expanded="false"> <h2>Types of tea</h2> <ul> <li>Black tea</li> <li>Green tea</li> </ul> </div></div>
LJWatson.co.uk @LeonieWatson 40
Expandable content (JavaScript)<script>document.getElementById("tea").onclick = function() { var tea = document.getElementById("tea-content"); var expanded;
tea.style.display = (tea.style.display == "block") ? "none" : "block“; expanded = tea.getAttribute("aria-expanded") == "false" ? "true" : "false“; tea.setAttribute("aria-expanded", expanded);};</script>
LJWatson.co.uk @LeonieWatson 41
Live region updates (HTML/ARIA)<h1>Tequila</h1><p>Tequila makes me happy...</p>
<div> <button onclick="updateItems()">Add to basket</button></div>
<h2>Basket summary</h2>
<div aria-live="assertive" aria-atomic="true"> <p>Your basket contains <span id="quantity">0</span> items.</p></div>
LJWatson.co.uk @LeonieWatson 42
Live region updates (JavaScript)
<script>var items = 0;
function updateItems () { items = items + 1;document.getElementById("quantity").innerHTML=items;}</script>
LJWatson.co.uk @LeonieWatson 43
Modal dialogs (HTML/ARIA)<div> <button id="launchBtn">Open modal</button></div>
<div id="modal" style="display:none;" role="alertdialog" aria-labelledby="mdlTitle" aria-describedby="mdlMsg“><h1 id="mdlTitle">Modal title</h1><p id="mdlMsg">Modal message.</p><div><button id="ok">Ok</button><button id="cancel">Cancel</button></div></div>
LJWatson.co.uk @LeonieWatson 44
Modal dialogs (JavaScript 1)(function(){
function openModal() { modal.style.display = 'block‘; setTimeout(function () {objFirst.focus();}, 100);}
function closeModal() { modal.style.display = 'none‘; trigger.focus();}
…})();
LJWatson.co.uk @LeonieWatson 45
Modal dialogs (JavaScript 2)(function(){…
function handleKeyboard(event) { switch (event.keyCode) { case 9: if (event.target === objLast && !event.shiftKey) { objFirst.focus(); event.preventDefault(); } else if (event.target === objFirst && event.shiftKey) { objLast.focus(); event.preventDefault(); } return false; break; case 27: closeModal(); return false; break; default: return true; }}
…}
LJWatson.co.uk @LeonieWatson 46
Resources• WAI-ARIA 1.0 Authoring Practices:
http://www.w3.org/TR/wai-aria-practices/
• Using WAI-ARIA in HTML:http://www.w3.org/TR/2013/WD-aria-in-html-20130214/
• HTML to Platform Accessibility APIs Implementation Guide:http://www.w3.org/TR/html-aapi/
• The Paciello Group Blog:http://blog.paciellogroup.com/
• Open AJAX Alliance Examples:http://www.oaa-accessibility.org/examples/
LJWatson.co.uk @LeonieWatson 47
Thank you!