Accessibility- FOCUS
Source: Accesibility - Udacity Front End Web Development Nanodegree
- a. FOCUS (MANAGEMENT): What is focus (management)?
- b. DOM ORDER
- c. TABINDEX ATTRIBUTE
- d. SKIP LINKS
- e. MANAGE FOCUS
- f. KEYBOARD DESIGN PATTERNS
- g. OFF-SCREEN CONTENT
- h. MODALS & KEYBOARD TRAPS
FOCUS= the location on a page that receives input from the keyboard
. It shows where keyboard events go in a page.
- An HTML user interface(UI) consists of multiple interactive widgets (input, buttons & select elements),
- Such as:
form controls
,scrollable regions
,links
,dialog boxes
,browser tabs
, that form ahierarchy
ofautomatic tab order + built-in keyboard event handling
. They are implicitely focussable. - Yet, NOT all elements are focussable. Examples are
header
,paragraphs
andimages
.
FOCUS MANAGEMENT= Move focus around the page using your keyboard to interact with the interface.
- TAB ORDER: key input is channeled from the system, through a hierarchy of interactive widgets, to the "active(= focused) widget:
TAB
: move focus forwardSHIFT TAB
: move focus backwardsArrow keys
: to navigate inside a component
Don't add focus to any content a user can Not interact with.
The DOM order matters for FOCUS MANAGEMENT: element.tabIndex;
Tab order = same as DOM order, even if `visual` order is changed (with CSS).
Changing the `visual` order can cause confusion among `screen readers` and `keyboard users`, who depend on keyboard navigation.
- Work with native elements is good practice for FOCUS behaviour, because they are automatically be inserted into the TAB ORDER, based on their DOM POSITION.
<button> I should </button>
<button> Be Focussed </button>
<button> Last! </button>
- If Visual order is changed (with CSS), it can cause confusion:
<button style= "float: right;">
I should </button>
<button> Be Focussed </button>
<button> Last! </button>
- To prevent confusion, the Reading and navigation order (code order) SHOULD BE logical and intuïtive.
- Control FOCUS BEHAVIOUR with TABINDEX ATTRIBUTE:
element.tabIndex;
(tabindex value of an element) - NOTE:
ONLY NEEDED
forInteractional elements
(input, buttons & select elements) - These elements Must have FOCUS:
Header links
Call to Action button
Search Input
Form Elements
Footer Links
- NOT needed for
non-interactional elements
(HTML-tags, such as: headers, paragraphs or images) - These elements should NOT have FOCUS, instead use SEMANTICS:
Header
Block of text (paragraph)
Image
_ [.] NOT in tab order.
- [.] focus programmatically by
focus()
- [.] Used for Off screen content, that appear in response to a user event() (ex. modals).
HTML: <dialog id="modal" tabindex="-1"></dialog>
JS: document.querySelector(´#modal´).focus()
- [.] to insert Unfocusable element in tab order.
- [.] focus programmatically by
focus()
- [.] So,
keyboard events
get directed to it.
HTML: <div id="dropdown" tabindex="0">Settings</div>
JS: document.querySelector(´#modal´).focus()
-
[.] Jump to the (IN) front of the tab order.
-
[.] If multiple elements,
lowest value
= first (in tab order). -
[.] This practice is discouraged and seen as an anti-pattern!
-
NOTE: never remove the
focus indicator
from an interactive element unless you're going to replace it. -
Otherwise a keyboard user might not know which element is focused!
- Allow
screen reader, keyboard, and switch devices users
to navigate towards main pagecontent. - It bypasses all the following elements before the main content:
navigation bar
,sublists
,sitebars
,hamburger menus
. - Skip links makes all links hidden until focus().
<!--html code-->
<a href="#main-content" class="skip-link">Skip to main content</a>
<nav>
.
.
.
</nav>
<main id="main-content" tabindex="-1">
.
.
.
</main>
/*CSS code*/
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: red;
color: white;
padding: 8px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
- To manage
Focus in Complex Components
, use the WAI-Aria guidelines. - The ARIA Authoring Practices ("ARIA Design Patterns doc") provides guidance in selecting which
keyboard interaction
is appropriate for complex component ("actions", such asdrop down menu
,tree view component
.
- Use
keyboard design patterns
to implement focus inside of a component. - EXAMPLE: Radio pattern
Implement Keyboard Event Listeners
- Use ROVING FOCUS(
"roving tabindex"
) to make changes in:radiogroup.js
file. - first radio button:
- tabindex="0"
- checked
- focus()
<li tabindex="0" checked> //set tab index to 0 on the currently active item
<li tabindex="-1"> //set tab index to -1 for all children
<li tabindex="-1">
<li tabindex="-1">
<li tabindex="-1">
- The component uses a
keyboard event listener
to know which keyboard the user pressed. - to know on which component to set next tabindex to 0
- when moving to the next element => first element:
- tabindex="-1"
- remove checked
- unfocus ::
<li tabindex="-1"> //change tabindex to -1, remove checked
<li tabindex="0"> checked //add focus(), later remove focus() and add checked
<li tabindex="-1">
<li tabindex="-1">
<li tabindex="-1">
Next element: 1. tabindex="0" / 2. checked / 3. focus()
- If last element
tab
move to first element. - If first element
shift
+tab
move to last element.
- ARIA Authoring Best Practices 1.0 (Radio Button)
- ARIA Authoring Best Practices 1.1 (Radio Group)
- Example
- ARIA- Design Patterns and Widgets
- Video
- A drawer panel can lead focus to
visually disappear
. - Overcome it by setting
display: none;
orvisibility: hidden;
####How to check if OFF-SCREEN CONTENT
is ACCESSIBLE
?
- Track focus: to find
missing focus
, type into console:document.activeElement;
- this gives a reference to the current focussed item
- Use the Chrome Accessibility Developer Tools Extension to quickly find accessibility issues in your page:
- to add an
Accessibility Properties panel
to Elements inspector - to add an
Accessibility option
to audits panel.
- to add an
- KEYBOARD TRAP: keyboard focus is
locked or trapped at one particular page element
. - The user CAN NOT
navigate
to and from all navigable page elements, using only a keyboard. - Prevent a
permanent keyboard trap
.
EXCEPTION: use a temporary keyboard trap
for a MODAL
- Use WebAim in
Modals
to trap keyboard focus inside the modal until modal is closed. - It => return focus to
last focused element
before modal open. HTML:
<button class="modal toggle"> Open Modal </button>
<div class="modal">
<h1> Join our newsletter </h1>
<p> short description about the newsletter & why to join?
</p>
<div class="field">
<label for="fullname"> Full Name </label>
<input id="fullname" type= "text">
</div>
<div class="field">
<label for="email"> E-mail address </label>
<input id="email" type= "text">
</div>
<button id="signup"> Sign me up!</button>
</div>
<div class="modal overlay"></div>
JAVASCRIPT:
//hold previosly focussed element
let focusedElementBeforeModal;
//find the modal and its overlay
const modal document.querySelector(´modal´);
const modalOverlay document.querySelector(´modalOverlay´);
//add EventListener "click" on ModalToggle to openModal
let modalToggle document.querySelector(´modalToggle´);
modalToggle.addEventListener(´click´, openModal);
//create function openModal
function openModal(){
//save current focus
focusedElementBeforeModal= document.activeElement;
//Listen for and trap the keyword
modal.addEventListener(´keydown´, trapTabKey);
//Listen to indicators to close the modal
modalOverlay.addEventListener(´click´, closeModal);
//Sign-up button
let signupBtn.addEventListener(´click´, closeModal);
//Find all focusable children
const focusableElementString = ´a[href], area[href],
input:not([disabled]), select:not([disabled]),
textarea:not([disabled]), button:not([disabled]), iframe, object,
embed, [tabindex=´0´]), [contenteditable]´;
let focusableElements =
modal.querySelectorAll(´focusableElementString´);
//Convert Nodelist to Array
focusableElements= Array.prototype.slice.call(focusableElements);
//Figure out first and last element in group of focusableElements
let firstTabStop = focusableElements[0];
let secondTabStop = focusableElements[focusableElements.length -1];
//Show the modal and overlay
modal.style.display = ´block´;
modalOverlay.style.display = ´block´;
//Focus firstTabStop
firstTabStop.focus();
function TapKeyPress()
//Check for TapKeyPress
if (e.keyCode === 9) {
//shift + TAB
if (e.shiftKey) {
if document.activeElement=== firstTabStop) {
e.preventDefault();
lastTabStop.focus();
}
//TAB
} else {
if document.activeElement=== lastTabStop) {
e.preventDefault();
firstTabStop.focus();
}
}
}
//Escape
if (e.keyCode ===27) {
closeModal();
}
}
}
function closeModal(){
//Hide the Modal and Overlay
modal.style.display = ´none´;
modalOverlay.style.display = ´none´;
//Set focus back to element before modalOpen
focusedElementBeforeModal.focus();
}