480 likes | 662 Views
Accessible DOM scripting with ARIA. Léonie Watson. Overview. Anatomy of a rich internet application. Web accessibility stack. First principles. Best practices. Walkthrough examples. Anatomy of a rich internet application. Controls and widgets.
E N D
Accessible DOM scripting with ARIA Léonie Watson LJWatson.co.uk @LeonieWatson
Overview • Anatomy of a rich internet application. • Web accessibility stack. • First principles. • Best practices. • Walkthrough examples. LJWatson.co.uk @LeonieWatson
Anatomy of a rich internet application LJWatson.co.uk @LeonieWatson
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
Applications • Rich internet applications are web applications with desktop characteristics. LJWatson.co.uk @LeonieWatson
First principles LJWatson.co.uk @LeonieWatson
Perceivable • Controls and widgets must provide information to assistive technologies. • Dynamic changes in content must be communicated to assistive technologies. LJWatson.co.uk @LeonieWatson
Operable • Controls and widgets must be keyboard accessible. LJWatson.co.uk @LeonieWatson
Understandable • Controls and widgets must be properly labelled and described. LJWatson.co.uk @LeonieWatson
Robust • Controls and widgets must have reasonable backwards compatibility. • Controls and widgets should have good forwards compatibility. LJWatson.co.uk @LeonieWatson
Web accessibility stack LJWatson.co.uk @LeonieWatson
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()"> LJWatson.co.uk @LeonieWatson
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
Best practices LJWatson.co.uk @LeonieWatson
Use native HTML elements • Do this:<p><button>Button text</button></p> LJWatson.co.uk @LeonieWatson
Don’t override native semantics • Don’t do this:<prole="button">Button text</p> • Do this:<span role="button">Button text</span> LJWatson.co.uk @LeonieWatson
Managing keyboard focus LJWatson.co.uk @LeonieWatson
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
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
Providing keyboard accessibility LJWatson.co.uk @LeonieWatson
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
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
Don’t change context on focus • Don't do this:<select onchange="document.location=‘http://google.com';"> …</select> LJWatson.co.uk @LeonieWatson
Handling widget focus LJWatson.co.uk @LeonieWatson
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
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
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
Put the key handler on the parent element • Captures the keystrokes, determines which element takes focus next, and updates the aria-activedesendantproperty. LJWatson.co.uk @LeonieWatson
Use the scrollIntoView() method • Or a purpose built function, to ensure the active descendant moves into view. LJWatson.co.uk @LeonieWatson
Providing keyboard shortcuts LJWatson.co.uk @LeonieWatson
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
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
Dynamically updating content LJWatson.co.uk @LeonieWatson
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
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
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
Walkthrough examples LJWatson.co.uk @LeonieWatson
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
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
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
Live region updates (JavaScript) <script> var items = 0; function updateItems () { items = items + 1; document.getElementById("quantity").innerHTML=items; } </script> LJWatson.co.uk @LeonieWatson
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
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
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
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
Thank you! LJWatson.co.uk @LeonieWatson