Have accessibility expertise on the team

Process oriented Project manager

One or more people on the team should have experience with:

  • Existing guidelines and legislation for accessibility (e.g. WCAG)
  • Good coding practices to ensure things like keyboard access and understandable navigation for screen reader
  • Different kinds of assistive technology and how they work
  • Use of testing tools for accessibility
  • Simulation exercises
  • User testing with users with disabilities
  • Technology that facilitates accessibility

Introduce accessibility from the beginning

Process oriented Project manager Frontend developer Interaction designer Graphic designer Procurer Tester

  • Follow the accessibility standard WCAG
  • Include accessibility on an equal basis as usability and user experience
  • Apply user-centred design principles and include users with disabilities

Accessibility in the specification

  • There has to be specific, measurable goals for accessibility in the specification. Look at the interface and the planned functionalities and determine what WCAG criteria need to be fulfilled.

Planning

  • Include people with disabilities in your target audience. 
  • Think accessibility when choosing what technology to use. For example a framework should  be compliant with WAI-ARIA. Some frameworks have better support for accessibility than others.

Communicate accessibility within the team

Process oriented Project manager Frontend developer Backend developer Information architect Graphic designer Interaction designer

Make sure everyone on the team understands:

  • That accessibility is interdisciplinary. This means everyone is responsible for accessibility within their professional sphere.
  • Why accessibility is important
  • How accessibility affects different sides of web development.

When the design is handed over to the programmer it is a good idea to explain that something in the design has been decided for the sake of accessibility. If not, it might be overlooked in the implementation phase.

Show the team someone with a disability using the web. This can deepen the understanding of accessibility and increase motivation. Here are some videos that might be useful:

Figure 1: YouTube video of blind person using computer

Figure 2: YouTube video of blind person using iPhone 4S

Figure 3: YouTube video of scanning software

Figure 4: YouTube video of person using computer with eye scanning

Follow existing design principles

Process oriented Graphic designer Interaction designer Information architect

Good design and user experience go a long way in making a site more accessible. An application that is usable for everyone is the most efficient way of ensuring it can be used by someone with a disability. Pay extra attention to ease of navigation, scalability and responsive design, font-type, font-size and contrast.

Some examples of good design principles are:

  • Know the main things people want to do on your site and make them obvious and easy
  • Save the user steps where possible
  • Make it easy to recover from errors
  • Know what questions the user is likely to have and answer them on the FAQ page
  • Tell the users what they want to know, even things like shipping cost and parking fee
  • The language should be clear and concise and needless words omitted
  • Have a clear visual hierarchy where the most important things come first
  • Use conventions, for example the placing of the search field in the top right corner
  • Break the site up in clearly defined areas such as navigation, news feeds, content and external links
  • Make it obvious what is clickable
  • Avoid too much noise by not having everything on the same page, split it up
  • Help the user find her way on the website by using things like clear labels in the navigation and breadcrumbs and make sure the search functionality is optimised

Test accessibility at key stages

Process oriented Tester Frontend developer

Test using the following methods:

  • Automatic accessibility testing
  • Expert based testing
  • User testing

Do short task-based sessions and focus attention on one aspect at the time, e.g. how accessible the forms are.

Key stages for testing are every time something new and essential to the application is developed during the:

  • Designing phase (Start testing with sketches and work your way from there)
  • Development phase (Use automatic testing tool from your first HTML line)

All the different ways of testing for accessibility described below should be applied throughout the process.

Automatic tools

Table 3 gives an overview of some automatic tools for testing accessibility and their functionalities.

Tool Examines data tables Checks structure Checks alt-text Checks labels Checks contrast Examines WAI-ARIA Simulates screen reader Usable on local/password protected sites
Web Accessibility Evaluation Tool (WAVE) No Yes Yes Yes No No No No
WAVE Toolbar for Firefox No Yes Yes Yes No No No Yes
Accessibility Evaluator for Firefox No Yes Yes Yes No No No No
Web Accessibility Checker No Yes Yes Yes No No No No
WCAG Contrast Checker for Mozilla No Yes Yes Yes Yes No No No
WebAim Colour Contrast Checker No No No No Yes No No No
Juicy Studio Accessibility Toolbar Yes No No No Yes Yes No No
Fangs Screen Reader Emulator for Firefox No No No No No No Yes No
Firefox Accessibility Extension Yes Yes Yes Yes Yes Yes No No
Web Accessibility Toolbar (WAT) Yes Yes Yes No Yes Yes No No

Table 3: Automatic testing tools and their functionalities

Expert based testing

There are many different ways of doing expert based testing. The most common ones are:

Simulation

Simulation is used to simulate what it would be like to use an application with some kind of disability. There are several ways of simulating:

Turning of the following things in the browser:

  • Style sheets
  • Images
  • Sound
  • JavaScript
  • Java
  • Support for Flash/Silverlight
  • Pop-up windows

Be aware that it might be difficult to turn of JavaScript in new browsers.

Navigate using only the keyboard. Try out a few familiar websites first to get used to keyboard navigation. Be aware that and it might be difficult to turn on keyboard access in new browsers.

Turn off the display and navigate the application using a screen reader. Possible screen readers to test with are:

  • Apple screen reader (free software)
  • VoiceOver
  • Demo version of JAWS or the
  • NVDA for Windows (free software)
  • Firefox Screen-reader simulator

User testing

User testing with people with disabilities is a bit different from user testing with users without disabilities. To ensure the highest possible quality of the testing keep this in mind:

  • User may be dependent on large equipment of AT (e.g. Braille keyboard, foot mouse)
  • User should use their own equipment which they are comfortable with
  • User might need to perform the testing in their home or workplace (but they might also be perfectly able to come to a testing facility

Be aware that the tasks need to be the same, but there might be some differences in communication, e. g. saying choose link instead of click on link, talk about content rather than colour, or structural placement rather than visual placement.

Log all accessible script modules

Make sure you log all well functioning and accessible script modules. It facilitates accessibility in the future through reuse.

Use WAI-ARIA mark-up

Technology oriented Frontend developer

ARIA stands for Accessible Rich Internet Applications. WAI-ARIA is designed to aid in making dynamic and interactive web applications more accessible. It provides semantics and conveys updates and changes to assistive technology.

It can be used for:

  • Adding functionality that does not exist elsewhere, e.g. conveying updates to assistive technology
  • Conveying functionality that exist elsewhere, but can not be passed on to assistive technology otherwise, e.g. extend/collapse or sliders

WAI-ARIA should be not used as a bandage for poor code. 

The following will give an introduction to:

  • WAI-ARIA roles
  • WAI-ARIA states and properties
  • WAI-ARIA Live regions
  • WAI-ARIA use of tabindex

WAI-ARIA roles

Use WAI-ARIA role attributes to communicate a widgets role to assistive technology (AT). The role given by the WAI-ARIA role attribute trumps the role of the native element. The WAI-ARIA specification maintains a list of roles.

JavaScript is often used to insert custom modal dialogs (usually divs) instead of opening a new browser window. The WAI-ARIA role dialog can be used on elements used as containers for the dialog to inform screen reader users that a custom dialog is being inserted (or made visible via CSS). 

In the following example a div element has a role attribute of dialog. A dialog is an application window that is designed to interrupt the current processing of an application in order to prompt the user to enter information or require a response.

By using the role dialog and the WAI-ARIA attributes aria-labelledby and aria-descibedby on the same element, we provide additional information to AT.

1
2
3
4
5
6
7
8
<div id="myDialog" role="dialog"
     aria-labelledby="myTitle"
     aria-describedby="myDesc">
    <div id="myTitle">Save changes?</div>
    <button id="saveMe" type="button"> Yes </button>
    <button id="discardMe" type="button">No </button>
    <button id="neverMind" type="button">Cancel </button>
</div>
2-3

The aria-labelledby attribute is similar to aria-describedby in that both reference other elements to calculate a text alternative. However, a label should be concise, whereas a description is intended to provide more detailed information. Depending on the type of screen-reader that is used, the attributes aria-labelledby and aria-describedby will either be announced upon opening the dialog, or they will be available to read via the virtual cursor.

<div id="myDialog" role="dialog"
     aria-labelledby="myTitle"
     aria-describedby="myDesc">
    <div id="myTitle">Save changes?</div>
    <button id="saveMe" type="button"> Yes </button>
    <button id="discardMe" type="button">No </button>
    <button id="neverMind" type="button">Cancel </button>
</div>

This example follows the following important rules:

  1. If you use the role “dialog”, you must use aria-labelledby to point to the element containing the visible dialog title. The aria-labelledby attribute must be on the same HTML element as the role “dialog” attribute.
  2. If your dialog has one main descriptive text, you must use aria-describedby to point to its element, also on the same element that has role “dialog”.

Document landmark roles

Document landmark roles are a subset of regular roles, they are semantic extenders that provide training wheels for semantic elements that don’t exist or haven’t been adopted fully by all browsers. 

Use them to help screen reader users understand the role of a section on a page and help orientate themselves within the document. 

WAI-ARIA defines the following document landmark roles:

  • Article (content that makes sense in its own right such as a complete blog post)
  • Banner (site-orientated content, such as the title of the page and the logo)
  • Complementary (supporting content for the main content)
  • Contentinfo (information about the content)
  • Main (main content)
  • Navigation (content that contains the links to navigate this or related documents)
  • Search (contains a search form to search the site)

The following example specifies the landmark roles of banner, navigation, main and contentinfo. Landmark roles make navigation between areas of your page more efficient for different kinds of users. They help convey basic semantic intent and can serve as hooks and helpers for other technologies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<header role="banner">
   <p>Put company logo, etc. here.</p>
</header>
<nav role="navigation">
   <ul>
      <li>Put navigation here</li>
   </ul>
</nav>
<main role="main">
   <p>Put main content here.</p>
</main>
<footer role="contentinfo">
   <p>Put copyright, etc. here.</p>
</footer>
1, 9, 12

Within any document or application, the author SHOULD mark no more than one element each with the main, banner and contentinfo role.

<header role="banner">
   <p>Put company logo, etc. here.</p>
</header>
<nav role="navigation">
   <ul>
      <li>Put navigation here</li>
   </ul>
</nav>
<main role="main">
   <p>Put main content here.</p>
</main>
<footer role="contentinfo">
   <p>Put copyright, etc. here.</p>
</footer>

WARNING: The WAI-ARIA role navigation does not work well with the HTML5

States and properties

Use WAI-ARIA states and properties in combination with roles. Changes in states or properties will notify the user of an AT that a change has occurred and help understand how to interact with the widget. The state identifies a unique configuration of information for an object. For example, the aria-checked property has three state values; true, false and mixed.

In the accessible dialog example we provide extra information to the AT user by using WAI-ARIA properties aria-labelledby and aria-description. By further adding the state aria-hidden we prevent screenreaders from interacting with the rest of the page once the modal dialog is open.

1
2
3
4
5
6
7
8
<main id="mainPage" role="main"
    aria-hidden="true">
    <div id="modal" role="dialog"
         aria-hidden="false"
         aria-labelledby="modalTitle" 
         aria-describedby="modalDescription">
    </div>
</main>
2

When the modal dialog is displayed, the mainPage is marked with aria-hidden='true' to prevent screen readers from interacting with it once the modal dialog is open.

4-6

By using state aria-hidden and properties aria-labelledby and aria-describedby, we increase accessibility for the modal dialog for the AT user.

<main id="mainPage" role="main"
    aria-hidden="true">
    <div id="modal" role="dialog"
         aria-hidden="false"
         aria-labelledby="modalTitle" 
         aria-describedby="modalDescription">
    </div>
</main>

A good rule of thumb is that elements that change how they look often have changing states. The dialog changes by going from being invisible to visible . So we are going to use aria-hidden="true" on the dialog initially and change it's value when it is shown.

function showModal(obj) {
    document.getElementById('mainPage').setAttribute('aria-hidden','true'); 
    document.getElementById('modal').setAttribute('aria-hidden','false');
function showModal(obj) {
    document.getElementById('mainPage').setAttribute('aria-hidden','true'); 
    document.getElementById('modal').setAttribute('aria-hidden','false');

It is possible to use CSS selectors to show and hide the dialog based on the aria-hidden value. Then there is no need to change the CSS class in the code, only the value of aria-hidden.

[aria-hidden=true] {visibility: hidden;}
[aria-hidden=true] {visibility: hidden;}

There is a full list of states and properties to help define accessible widgets in the WAI-ARIA specification.

Live regions

WAI-ARIA is the only language capable of conveying updates to assistive technology. Live regions allow elements in a document to be announced if there are changes, without the user losing focus on their current activity.

The aria-live property has a value indicating one of three verbosity levels in a region:

  • Off: This indicates that the region is not live
  • Polite: This indicates that it is not necessary to respond until user completes their current activity
  • Assertive: This value is a higher priority than normal, but does not necessarily interrupt the user immediately

They are written as such:

<ul aria-live="off">
<ul aria-live="polite">
<ul aria-live="assertive">
<ul aria-live="off">
<ul aria-live="polite">
<ul aria-live="assertive">

Other important properties that can be used when defining live regions are:

Aria-atomic: Indicates if AT should present all or only part of the changed region to the user. It has the values true or false. If this property is set to true, AT should present the entire region as a whole.

In the following example, all elements within an unordered list will be announced in their entirety when the region is spoken, unless another element further down the chain overrides the aria-atomic property.

<ul aria-atomic="true"
    aria-live="polite">
<ul aria-atomic="true"
    aria-live="polite">

Aria-busy: Prevents AT announcing changes before the updates are complete. It has the values true or false. If multiple parts of a live region need to be loaded before changes are announced to the user, the aria-busy property can be set to true until the final part is loaded, and then set to false when the updates are complete.

<ul aria-atomic="true"
    aria-live="polite"
    aria-busy="true">
<ul aria-atomic="true"
    aria-live="polite"
    aria-busy="true">

Aria-relevant: Indicates what changes are considered relevant within a region. Accepts a space separated list of the following property values:

  • Additions: Nodes are added to the DOM within the region.
  • Removals: Nodes are removed from the DOM within the region.
  • Text: Text is added or removed from the DOM.
  • All: All of the above apply to this region.

In the absence of an explicit aria-relevant property, the default is to assume there are text changes and additions:

<ul aria-relevant="text additions">
<ul aria-relevant="text additions">

The following example would only announce changes if nodes are added to the DOM within the region. If there are text changes, or nodes are removed within the region, the user will not be notified.

<ul aria-relevant="additions"
    aria-atomic="true"
    aria-live="polite">
<ul aria-relevant="additions"
    aria-atomic="true"
    aria-live="polite">

Tabindex

The HTML tabindex attribute can be used to set tab structure on a page. Do not use tabindex as a replacement for a logical reading order. If mark-up has a logical structure you don’t need tabindex for interface elements that are already in the keyboard tab order, such as buttons, links and form elements.

WAI-ARIA extends tabindex to be: 

  • used on all visible elements
  • given focus through scripting

WAI-ARIA allows a negative value (typically -1) to be specified for elements that should not appear in the keyboard tab order, but can be programmatically focused.

Tabindex is relevant every time you want to set focus to something, not just when using script. Use it for all widgets that have a series of components that need keyboard access, such as a tree. For example a menu widget where the menu itself is in the tab order but the menu items is not. Instead the menu items could be programmed so they can be navigated using cursor keys. This way, users do not have to tab through all items in the menu, and can better navigate the document.

The following example uses a tabindex attribute value of 0 to put a

element into the tab order so that a keyboard user can navigate to the element:

<div tabindex="0">
...
</div>
<div tabindex="0">
...
</div>

The following example uses a negative tabindex attribute value, so that the element is not placed in the tab order, but can receive programmatic focus:

<a href="http://accessibilityagent.no" id="progaccess" tabindex="-1">
    42 reasons to implement accessibility
</a>
<a href="http://accessibilityagent.no" id="progaccess" tabindex="-1">
    42 reasons to implement accessibility
</a>

The following snippet of JavaScript selects the element defined above, and uses the focus method to place focus on the element:

1
2
var objDiv = document.getElementById('progaccess');
objDiv.focus();
2

Focus on the element

var objDiv = document.getElementById('progaccess');
objDiv.focus();

Related guidelines:

WCAG Guideline 4.1.2 Name, Role, Value

Follow and validate the HTML5 standard

Technology oriented Frontend developer

Standards should be used because assistive technology is developed according to standards. The HTML5 standard has stronger semantics than any older standard, which is beneficial for accessibility because it helps assistive technology determining what the different elements are.

Declare doctype as such:

<!DOCTYPE html>
<!DOCTYPE html>

Validate using Validator.nu (X) HTML5 Validator or W3C mark-up validation service.

Using input types like numbers, url and email will provide the right keyboard popping up on touch screens and is also beneficial for live form validation.

 

Touch keyboard and input field with input type='number'

Figure 5: Touch keyboard and input field with input type=”number”

WARNING: Be aware that there may be issues using

 and

Combine WAI-ARIA and HTML5 mark-up

Technology oriented Frontend developer

Neither WAI-ARIA nor HTML5 is fully supported in all browsers and on all devices. It is therefore smart to combine them to get a fuller support. This is no problem to do. The code example on Document landmark roles did this. It is llustrated in figure 6.

 

Image illustrating combination of WAI-ARIA and HTML5 elements

Figure 6: Combination of WAI-ARIA and HTML5 elements

 

Table 1 gives a brief overview of HTML5 elements and their WAI-ARIA equivalent.

HTML5 WAI-ARIA Description
 
role=”banner”

Introduction to a page or section. Can contain a heading (H1-H6), site logo, navigation.

  role=”navigation”

Can be used for various types of navigation such as site navigation, sub-navigation, breadcrumbs, previous/next links.

 
role=”contentinfo”

Describes the page or a section of the page. A page’s footer may contain author name, copyright info, privacy policy, etc.

role=”complementary”

Information that is tangentially related to the main page content, but can be read separately. Visually you might see this as a sidebar.

 
no equivalent

Independent item such as a blog post, article, etc. Think of it as something that could be independently picked up and moved around, such as blog posts in a RSS feed.

Table 1: HTML5 elements and WAI-ARIA equivalents

Use progressive enhancement

Technology oriented Frontend developer Graphic designer Interaction designer

Progressive enhancement allows everyone to access the basic content and functionality of a web page, using any browser or Internet connection, while also providing an enhanced version of the page to those with more advanced browser software or greater bandwidth.

The core of Progressive enhancement is separating:

  • Content and basic functionality (HTML)
  • Presentation (CSS)
  • Behaviour (JavaScript)

The principles of Progressive enhancement are:

  • Basic content should be accessible to all web browsers and devices
  • Basic functionality should be accessible to all web browsers and devices
  • Sparse, semantic mark-up contains all content
  • Enhanced layout is provided by externally linked CSS
  • Enhanced behaviour is provided by unobtrusive, externally linked JavaScript
  • End-user web browser preferences are respected

The process of Progressive enhancement

  1. Start with marking up the content. Make sure the mark-up conveys the greatest level of detail about the content it wraps around. HTML5 and WAI-ARIA goes a long way to serve this purpose. This is essential for offering a basic experience to:
    • Search engines
    • People on mobile devices
    • People on old browsers
  2. Create a separate CSS file and link to it as such:
<link rel="stylesheet" type="text/css" href="main.css">
<link rel="stylesheet" type="text/css" href="main.css">

This is for users who have:

    • Basic CSS support
    • Lack support for JavaScript

3. Create a separate JavaScript file and link to it as such:

<script src="main.js"></script>
<script src="main.js"></script>

Add JavaScript in an unobtrusive way (See next guideline).

Related guidelines:

Difi UU-skolen: Utforming og presentasjon (norwegian)

WCAG technique SCR24: Use progressive enhancement to open new windows on user request

 

Use Unobtrusive JavaScript

Technology oriented Frontend developer

Unobtrusive JavaScript is about catering for people whose browser lack JavaScript support, or if JavaScript fails. This means not making JavaScript a requirement for a functional application. It is also about avoiding unnecessary movements on a web page, unintuitive widget functionality and unfamiliar controls. This is an advantage to everyone, but especially benefits keyboard and screen-reader users. This section also describes some general good coding practises with other benefits besides accessibility.

WARNING: Note that Unobtrusive JavaScript is not necessarily accessible JavaScript and is no guarantee for keyboard and screen reader access.

The seven rules of unobtrusive JavaScript:

  • Do not make any assumptions
  • Find your hooks and relationships
  • Use CSS to traverse the DOM
  • Understand browsers and users
  • Understand events
  • Play well with others
  • Work for the next developer

Do not make any assumptions

Do not expect JavaScript to be available, and do not expect the intended mark-up to be there. Four things to keep in mind are:

  • Do not expect browsers to support certain methods and have the correct properties, but test for them before accessing them
  • Do not expect the correct HTML to be at your disposal, but check for it and do nothing when it is not available
  • Keep functionality independent of input device
  • Expect other scripts to try to interfere with the functionality and keep the scope of the scripts as secure as possible

Find your hooks and relationships

Before starting to plan a script:

  • Look at the HTML the script will be enhancing
  • Consider what is the best way of letting the script interact with it
  • Consider the hooks and relationships in the HTML

HTML hooks are:

  • Unique IDs (in valid HTML). Access them with the DOM method getElementById.
  • HTML elements which can be retrieved with getElementsByTagName and CSS classes.

Regarding HTML relationships ask the following questions:

  • How can I reach this element the easiest way and with the least steps traversing the DOM?
  • What elements do I have to alter to update all the child elements which should be changed?
  • What attributes does one element have that I can use to link to another element?

Use CSS to traverse the DOM

Using CSS to traverse the DOM is more effective, takes fewer resources and will not create any unnecessary dependency on  JavaScript.

In the example below, we perform a similar styling of images first with CSS and then with JavaScript.

1
2
<img class="css-border random-color-border" src="http://lorempixel.com/200/200/nature/1"/>
<img class="css-border" src="http://lorempixel.com/200/200/nature/2"/>
<img class="css-border random-color-border" src="http://lorempixel.com/200/200/nature/1"/>
<img class="css-border" src="http://lorempixel.com/200/200/nature/2"/>

1
2
3
4
5
6
7
.css-border{
    width: 200px;
    height: 200px;
    border:5px solid;
    border-radius:100px;
    border-bottom-left-radius:0;
}
.css-border{
    width: 200px;
    height: 200px;
    border:5px solid;
    border-radius:100px;
    border-bottom-left-radius:0;
}

1
2
3
4
5
6
7
8
(function(){
    var images = document.querySelectorAll('img');
    for (var i = 0; i < images.length; i++){
      images[i].style.border = '5px solid';
      images[i].style.borderBottomLeftRadius = 0;
      images[i].className='css-border';
    }
})();
4-5

Don't do this. Leave precise style details to the stylesheet

6

It is best practice to dynamically manipulate classes via the className property

(function(){
    var images = document.querySelectorAll('img');
    for (var i = 0; i < images.length; i++){
      images[i].style.border = '5px solid';
      images[i].style.borderBottomLeftRadius = 0;
      images[i].className='css-border';
    }
})();

In many cases, and where possible, it really is best practice to dynamically manipulate classes via the className property since the ultimate appearance of all of the styling hooks can be controlled in a single stylesheet. JavaScript code then also becomes cleaner since instead of being dedicated to styling details, it can focus on the overall semantics of each section it is creating or manipulating, leaving the precise style details to the stylesheet.

Read more about Using dynamic styling information.

Use JavaScript to enhance CSS

JavaScript can interact with stylesheets, allowing you to write programs that change a document's style dynamically.

There are three ways to do this:

  • By working with the document's list of stylesheets—for example: adding, removing or modifying a stylesheet.
  • By working with the rules in a stylesheet—for example: adding, removing or modifying a rule.
  • By working with an individual element in the DOM—modifying its style independently of the document's stylesheets

1
2
3
4
5
6
7
(function(){
    var images = document.querySelectorAll('.random-color-border');
    for (var i = 0; i < images.length; i++){
      var randomColor = ' #'+(~~(Math.random()*(1<<24))).toString(16);
      images[i].style.borderColor = randomColor;
    }
})();
4

Dynamism can be implemented with the support of scripting. This is a progressive enhancement of the styling, not affecting functionality.

(function(){
    var images = document.querySelectorAll('.random-color-border');
    for (var i = 0; i < images.length; i++){
      var randomColor = ' #'+(~~(Math.random()*(1<<24))).toString(16);
      images[i].style.borderColor = randomColor;
    }
})();

Play with this example of using JavaScript to enhance CSS in Codepen.

Understand users and browsers

You need to understand:

  • how browsers work
  • how browsers fail
  • what users expect to happen

Do not wander too far from the way browsers work and how users expect them to work. Consider the following:

  • Will the interface work independent of input device, and if not, what should be the fallback?
  • Is the interface following rules of the browser or the rules of the rich interface? Is it for example possible to navigate a multi level menu with cursors or is tabbing required? Are some keyboard shortcuts overlooked? Try to keep the conventional keyboard shortcuts.
  • What necessary functionality is dependent on JavaScript?

Understand Events

Event handling helps with separating the JavaScript from the HTML and CSS, and also goes a bit further.

  • The elements in the document are placed there to wait for handlers to listen to a change happening to them. When it happens the handlers retrieve an object (normally a parameter called e) that tells them what happened to what and what can be done with it.

Event handling does not only happen to the element you want to reach, but also to all the elements above it in the DOM hierarchy. (This does not apply to all events. Focus and blur do not do that.) This allows you to assign one single event handler to for example a navigation list and use the event handling's methods to reach the element in question. This technique is called event delegation and it has several benefits:

  • You only need to test if a single element exists, not each of them
  • It is possible to dynamically add or remove new child elements without having to remove or add new event handlers
  • It is possible to react to the same event on different elements

The event handling follows an event order when there are elements inside elements as such:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
    <body>
        <h1>This example uses the addEventListener() to demonstrate event order</h1>
        <div id="myDiv">
            <button id="myBtn">Click me</button>
        </div>
    </body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <h1>This example uses the addEventListener() to demonstrate event order</h1>
        <div id="myDiv">
            <button id="myBtn">Click me</button>
        </div>
    </body>
</html>

1
2
3
4
5
6
document.getElementById("myDiv").addEventListener("click", myFunction);
document.getElementById("myBtn").addEventListener("click", myFunction);

function myFunction() {
    document.getElementById("myDiv").insertAdjacentHTML('beforeend', '<p>You clicked ' + this + "</p>");
}
document.getElementById("myDiv").addEventListener("click", myFunction);
document.getElementById("myBtn").addEventListener("click", myFunction);

function myFunction() {
    document.getElementById("myDiv").insertAdjacentHTML('beforeend', '<p>You clicked ' + this + "</p>");
}

This will output:

You clicked [object HTMLButtonElement]
You clicked [object HTMLDivElement]

Play with this  example of event listeners in Codepen.

There are two different event order models:

Event capturing: the outer element event takes place first. This means it starts capturing events from the outer element.

Image illustrating Event capturing Figure 7: W3C event model - Event capturing

Event bubbling: the inner element event takes place first. It starts capturing events from the inner element.

Image illustrating Event bubbling

Figure 8: W3C event model - Event bubbling

Event capturing and event bubbling can be combined so that events are first captured until it reaches the target element and then bubbles up again.

Image illustrating  combination of event capturing and event bubbling

Figure 9: W3C event model - Combination of event capturing and event bubbling

Play well with others

There will hardly ever be only one script used in a document. Make sure your script does not interfere with others, and make your script difficult to interfere with.

  • The code should not have global function or variable names that others scripts can override
  • Instantiate every variable using the var keyword
    • Declared variables are constrained in the execution context in which they are declared. Undeclared variables are always global.
    • Declared variables are created before any code is executed. Undeclared variables do not exist until the code assigning to them is executed.
    • Declared variables are a non-configurable property of their execution context (function or global). Undeclared variables are configurable (e.g. can be deleted).

      Because of these three differences, failure to declare variables will very likely lead to unexpected results. Thus it is recommended to always declare variables, regardless of whether they are in a function or global scope.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var nav = document.querySelector('nav');

function init() {
  if (nav.classList.contains('nav-hide')) {
    showNavigation();
  }
}

function showNavigation() {
  if (nav.classList.contains('nav-hide')) {
    nav.classList.remove('nav-hide');
    nav.classList.add('nav-show');
  }
}

function hideNavigation() {
  if (nav.classList.contains("nav-show")) {
    nav.classList.remove("nav-show");
    nav.classList.add("nav-hide");
  }
}

init();
1

The script has a global variable called nav which can be accessed from all the functions init(), show() and reset(). The functions can access the global variable and each other by name.

4

HTML5’s classList functionality (IE10+) makes adding and removing classes easy. You can feature detect if the browser supports it by using  if ("classList" in document.documentElement)

var nav = document.querySelector('nav');

function init() {
  if (nav.classList.contains('nav-hide')) {
    showNavigation();
  }
}

function showNavigation() {
  if (nav.classList.contains('nav-hide')) {
    nav.classList.remove('nav-hide');
    nav.classList.add('nav-show');
  }
}

function hideNavigation() {
  if (nav.classList.contains("nav-show")) {
    nav.classList.remove("nav-show");
    nav.classList.add("nav-hide");
  }
}

init();

Play with this example of declaring variables in Codepen.

The object literal

  • Avoid all global code by wrapping the code in an object using the object literal. That way you turn the functions into methods and the variables into properties.
  • Define the methods and variables with a name followed by a colon and separate each of them from the others with a comma.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var myScript = {
    nav:document.querySelector('nav'),
    init:function(){
        myScript.showNavigation();
    },
    showNavigation:function(){
        if(myScript.nav.classList.contains('nav-hide')){
            myScript.nav.classList.remove('nav-hide');
            myScript.nav.classList.add('nav-show');
        }
    },
    hideNavigation:function(){
        if(myScript.nav.classList.contains('nav-show')){
            myScript.nav.classList.remove('nav-show');
            myScript.nav.classList.add('nav-hide');
        }
    }
}

myScript.init();
var myScript = {
    nav:document.querySelector('nav'),
    init:function(){
        myScript.showNavigation();
    },
    showNavigation:function(){
        if(myScript.nav.classList.contains('nav-hide')){
            myScript.nav.classList.remove('nav-hide');
            myScript.nav.classList.add('nav-show');
        }
    },
    hideNavigation:function(){
        if(myScript.nav.classList.contains('nav-show')){
            myScript.nav.classList.remove('nav-show');
            myScript.nav.classList.add('nav-hide');
        }
    }
}

myScript.init();

Play with this example of Object literal in Codepen.

These methods can be accessed from outside and inside the object by pre-pending the object name followed by a full stop. The drawback with this pattern is that the name of the object needs to be repeated every time it is accessed from another method. In addition, everything put inside the object is publicly accessible. 

Module pattern

If you want to only make parts of the script accessible to other script in the document it is possible use the module pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var myScript = function() {
  // these are private
  var nav = document.querySelector('nav');
  var init = function() {
    myScript.showNavigation();
  }

  // these are public
  return {
    showNavigation: function() {
      if (nav.classList.contains('nav-hide')) {
        nav.classList.remove('nav-hide');
        nav.classList.add('nav-show');
      }
    },
    hideNavigation: function() {
      if (nav.classList.contains('nav-show')) {
        nav.classList.remove('nav-show');
        nav.classList.add('nav-hide');
      }
    },
    init: init
  }
}();

myScript.init();
5

Problem: to access one public method from another or from a private method you need to go through the verbose long name.

9

Public methods and properties wrapped in a return statement and using the object literal

var myScript = function() {
  // these are private
  var nav = document.querySelector('nav');
  var init = function() {
    myScript.showNavigation();
  }

  // these are public
  return {
    showNavigation: function() {
      if (nav.classList.contains('nav-hide')) {
        nav.classList.remove('nav-hide');
        nav.classList.add('nav-show');
      }
    },
    hideNavigation: function() {
      if (nav.classList.contains('nav-show')) {
        nav.classList.remove('nav-show');
        nav.classList.add('nav-hide');
      }
    },
    init: init
  }
}();

myScript.init();

Play with this example of Module patterns in Codepen.

You can access the public properties and methods that are returned the same way as in the object literal. The problem is that to access one public method from another or from a private method you need to go through the verbose long name again (the main object name can get rather long). 

Module pattern - return an object with synonyms

To avoid repeating long verbose names, define the methods as private and only return an object with synonyms.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var myScript = function() {
  // these are all private methods and properties
  var nav = document.querySelector('nav');

  var init = function() {
    showNavigation();
  }

  var showNavigation = function() {
    if (nav.classList.contains('nav-hide')) {
      nav.classList.remove('nav-hide');
      nav.classList.add('nav-show');
    }
  }

  var hideNavigation = function() {
    if (nav.classList.contains('nav-show')) {
      nav.classList.remove('nav-show');
      nav.classList.add('nav-hide');
    }
  }

  //return objects with synonyms
  return {
    showNavigation: showNavigation,
    hideNavigation: hideNavigation,
    init: init
  }
}();

myScript.init();
6

We can now access showNavigation with its short name.

24

return public pointers to the private methods and properties you want to reveal

var myScript = function() {
  // these are all private methods and properties
  var nav = document.querySelector('nav');

  var init = function() {
    showNavigation();
  }

  var showNavigation = function() {
    if (nav.classList.contains('nav-hide')) {
      nav.classList.remove('nav-hide');
      nav.classList.add('nav-show');
    }
  }

  var hideNavigation = function() {
    if (nav.classList.contains('nav-show')) {
      nav.classList.remove('nav-show');
      nav.classList.add('nav-hide');
    }
  }

  //return objects with synonyms
  return {
    showNavigation: showNavigation,
    hideNavigation: hideNavigation,
    init: init
  }
}();

myScript.init();

Play with this example of returning an object with synonyms in Codepen.

This allows for a consistency in coding style and gives the possibility to write shorter synonyms when they are revealed.

Anonymous function

To avoid revealing any methods or properties, it is possible to wrap the whole code block in an anonymous function and call it immediately after it was defined.

“The risk we are running by adding names to the global namespace, is that someone (or you) has already used that name elsewhere. This can result in a conflict. In the worst case you may break other code.” -lillylabs.no

In the example below we use an anonynous function to progressively enhance the page by creating a print button only if the browser supports it. Notice how defensive the script is, we don’t assume anything.

1
2
3
<p id="printThis">
  Thank you for your order. Please print this page for your records.
</p>
<p id="printThis">
  Thank you for your order. Please print this page for your records.
</p>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function() {
  if (document.getElementById) {
    var printThis = document.getElementById('printThis');
    if (printThis && typeof window.print === 'function') {
      var printButton = document.createElement('input');
      printButton.setAttribute('type', 'button');
      printButton.setAttribute('value', 'Print this now');
      printButton.onclick = function() {
        window.print();
      };
      printThis.appendChild(printButton);
    }
  }
})();
1,14

To avoid leaving any global variables behind, we wrap the whole functionality in an anonymous function and immediately execute it - this is done with (function(){})()

2-3

We test for DOM support and try to get the element we want to add the button to

4

We then test if the element exists and if the browser has a window object and a print method

5-10

we create a new click button and apply window.print() as the click event handler

11

The last step is to add the button to the paragraph.

(function() {
  if (document.getElementById) {
    var printThis = document.getElementById('printThis');
    if (printThis && typeof window.print === 'function') {
      var printButton = document.createElement('input');
      printButton.setAttribute('type', 'button');
      printButton.setAttribute('value', 'Print this now');
      printButton.onclick = function() {
        window.print();
      };
      printThis.appendChild(printButton);
    }
  }
})();

Play with this  example of Anonymous function in Codepen or read more about progressive enhancement vs graceful degradation on w3.org

Anonymous functions is great pattern for functionality that just needs to be executed once and has no dependency on other functions. This will make the code work well for the user and the machine it is running on as well as other developers.

Work for the next developer

Think about the developer who has to take over once this code is in production. Consider the following:

  • Are all the variable and function names logical and easy to understand?
  • Is the code logically structured? Is it possible to "read" it from top to bottom?
  • Are the dependencies obvious?
  • Are areas that might be confusing commented?

The HTML and CSS of a document is much more likely to change than the JavaScript as these make up visual output. It is therefore a good idea not to have any class and ID names or strings that will be shown to the end user buried somewhere in the code, but separate it out into a configuration object instead.

This way maintainers know exactly where to change these without having to alter the rest of your code.

1
2
.show{display:block}
.hide{display:none}
.show{display:block}
.hide{display:none}

1
2
3
4
5
<button onclick="myScript.showNavigation()">Show navigation</button>
<button onclick="myScript.hideNavigation()">Hide navigation</button>
<nav id="nav" class="show" >
    Page navigation
</nav>
<button onclick="myScript.showNavigation()">Show navigation</button>
<button onclick="myScript.hideNavigation()">Hide navigation</button>
<nav id="nav" class="show" >
    Page navigation
</nav>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var myScript = function() {
  var config = {
    navigationID: 'nav',
    visibleClass: 'show',
    invisibleClass: 'hide'
  };

  var nav = document.getElementById(config.navigationID);

  var showNavigation = function() {
    nav.classList.add(config.visibleClass);
    nav.classList.remove(config.invisibleClass);
  }
  var hideNavigation = function() {
    nav.classList.remove(config.visibleClass);
    nav.classList.add(config.invisibleClass);
  }
  return {
    showNavigation: showNavigation,
    hideNavigation: hideNavigation
  }
}();
2

We define a configuration object that contain all hardcoded references to the classes used in our html, instead of having references to css classes spread all around out javascript code. 

var myScript = function() {
  var config = {
    navigationID: 'nav',
    visibleClass: 'show',
    invisibleClass: 'hide'
  };

  var nav = document.getElementById(config.navigationID);

  var showNavigation = function() {
    nav.classList.add(config.visibleClass);
    nav.classList.remove(config.invisibleClass);
  }
  var hideNavigation = function() {
    nav.classList.remove(config.visibleClass);
    nav.classList.add(config.invisibleClass);
  }
  return {
    showNavigation: showNavigation,
    hideNavigation: hideNavigation
  }
}();

Play with this example of Configuration object in Codepen.

Make sure methods are device independent

Technology oriented Frontend developer

Device independent JavaScript methods are not dependent on a specific device input. Using a combination of the following methods will ensure accessibility both from mouse, keyboard, touch and assistive technology. Ensuring keyboard access goes a long way for ensuring access to different assistive technologies.

Input methods for mouse are:

  • onMouseOver
  • onMouseOut
  • onClick (Mostly works with the Enter key)

Input methods for keyboard are:

  • onFocus 
  • onBlur (Especially practical for live validation)
  • onChange (use with caution, goes against WCAG)
  • onSelect (use with caution, goes against WCAG)
  • onkeypress 
  • onkeydown 
  • onkeyup

Input methods for touch are:

  • touchstart
  • touchmove
  • touchend
  • touchenter
  • touchleave
  • touchcancel

Without device independent methods users may have problems with:

  • Navigation
  • Content being hidden from them
  • Lack of control over automated content changes

Read more in Accessible JavaScript – JavaScript event handlers or in Luke Wroblewskis book Mobile and Multi-device design.


WARNING: Use onChange and onSelect with caution as they go against WCAG 3.2.1. and 3.2.2

Related guidelines:
Difi UU-skolen: Tastaturnavigering (norwegian)
WCAG Guideline 2.1 Keyboard accessible: Make all functionality accessible from a keyboard

Use accessible modal windows instead of pop-ups

Technology oriented Frontend developer

Modal windows should be used instead of pop-up windows, and should be made accessible with WAI-ARIA. In a modal dialog screen-readers will speak the title, the text, and the currently focused button automatically. In the following example the title is called “modalTitle” and the text is called “modalDescription”.

The first line declares a container that encompasses the whole dialog. HTML5 still does not have a proper dialog element that is supported everywhere. Therefore use the WAI-ARIA role "dialog".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<main id="mainPage" role="main"
      aria-hidden="true">
    <div id="modal"
         role="dialog"
         aria-labelledby="modalTitle"
         aria-describedby="modalDescription"
         aria-hidden="false">
        <div id="modalDescription" class="screen-reader-offscreen">
            Beginning of modal dialog window example. Escape will cancel and close the window.
        </div>
        <h1 id="modalTitle">Modal dialog window example</h1>
        <p>
            These are the onscreen instructions that are not attached explicitly to a focusable element.
            Can screen reader users read this text with the virtual cursor?
        </p>
        <button id="modalCloseButton" title="Close modal dialog window">
            <img id="cancel" src="x.png" alt="Close modal dialog window">
        </button>
    </div>
</main>
16

The modalCloseButton is the only button and the first focusable element in the dialog. It is therefore the best place to set initial keyboard focus (as in: I can just press “return” and carry on with my task). Make sure each active dialog has a focused descendant element that has keyboard focus. When dialog is closed, you must also set focus back to the element that opened the dialog, or any other useful element from which the user of your app will most likely continue working.

<main id="mainPage" role="main"
      aria-hidden="true">
    <div id="modal"
         role="dialog"
         aria-labelledby="modalTitle"
         aria-describedby="modalDescription"
         aria-hidden="false">
        <div id="modalDescription" class="screen-reader-offscreen">
            Beginning of modal dialog window example. Escape will cancel and close the window.
        </div>
        <h1 id="modalTitle">Modal dialog window example</h1>
        <p>
            These are the onscreen instructions that are not attached explicitly to a focusable element.
            Can screen reader users read this text with the virtual cursor?
        </p>
        <button id="modalCloseButton" title="Close modal dialog window">
            <img id="cancel" src="x.png" alt="Close modal dialog window">
        </button>
    </div>
</main>

Remember that WAI-ARIA merely provides semantic information. It does not automatically introduce certain types of behaviour. You have to use JavaScript to do that. To ensure accessibility remember the following:

  • Set focus on the first keyboard focusable element within the dialog
  • Trap keyboard focus and create a natural tab order inside the dialog
  • Provide an escape route
  • Restore focus after closing the dialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function showModal(dialog) {

    // save current focus
    focusedElementBeforeModal = document.activeElement;

    // Focuses the close button
    dialog.find('button:last-of-type').focus()

    document.getElementById('mainPage').setAttribute('aria-hidden','true');
    document.getElementById('modal').setAttribute('aria-hidden','false');
}

function hideModal() {

    // Moves focus back on closing the dialog
    focusedElementBeforeModal.focus();

    document.getElementById('mainPage').setAttribute('aria-hidden','false');
    document.getElementById('modal').setAttribute('aria-hidden','true');
}
function showModal(dialog) {

    // save current focus
    focusedElementBeforeModal = document.activeElement;

    // Focuses the close button
    dialog.find('button:last-of-type').focus()

    document.getElementById('mainPage').setAttribute('aria-hidden','true');
    document.getElementById('modal').setAttribute('aria-hidden','false');
}

function hideModal() {

    // Moves focus back on closing the dialog
    focusedElementBeforeModal.focus();

    document.getElementById('mainPage').setAttribute('aria-hidden','false');
    document.getElementById('modal').setAttribute('aria-hidden','true');
}

Play with this example of extremely accessible modal dialog in Codepen.

For more information on how to implement accessible modal dialogs and more complex examples read Marco Zehes article Advanced ARIA Tip #2: Accessible modal dialogs and/or look at an example by Greg Kraus called The Incredible Accessible Modal Window, also available on GitHub.

Use technology that facilitates accessibility

Technology oriented Frontend developer

It has been said that accessibility is not a technical issue, it is a social one. The technical aids are there and it is very possible to create accessible rich internet applications. However, it may take more time and it demands skills and knowledge about accessibility. This is expensive. Therefore using and promoting technology that facilitates accessibility and makes it "fast and easy" is important.

Libraries and frameworks

Use a framework that facilitates accessibility with:

  • WAI-ARIA compliance
  • Server rendering 
  • Warnings of inaccessible coding 
  • Components with keyboard support

Some frameworks might not be very good with accessibility, but offer external frameworks which adds accessibility on top.

A few possibilities are:

  • React (server rendering, warnings when writing inaccessible code)
  • jQuery (WAI-ARIA compliance and keyboard support)
  • Boostrap (Has a plug-in that adds accessibility to all their default plug-ins)

WARNING: Remember that you always have to check that plug-ins is accessible. Do not rely on the framework alone.

Web Components

Creating accessible Web Components will facilitate accessibility in the future through reuse of these components. How accessible a Web Component is, is up to the developer. Consider many of the same things as when creating RIAs in general:

  • Add WAI-ARIA
  • Resizable text
  • Give it an alternative text
  • Make sure it works without audio
  • Ensure keyboard functionality
  • Ensure it is manageable to operate with a screen reader

To learn about Web Components start by reading the W3C introduction. W3C also provides some good examples of Web Components.

When you want to start using Web Components the official website gives a general overview of:

  • specifications used in Web Components
  • libraries you can use to create your own Web Components
  • browser support
  • general discussion and community

The Web Components wiki gives information and provides links to tutorials for:

There are many other resources for information and examples.

You can also read more about creating accessible Web Components in the article Accessible Web Components.

© 2015 Accessibilityagent (BETA) - Implementing accessibility so you can drink beer.