Developer's guide to accessible DOM scripting

Developer's guide to
accessible DOM scripting

Enter.JS, Darmstadt 2016

JavaScript is not evil

Progressive enhancement is good

Platform Accessibility APIs

Platform controls

Bold checkbox in Windows 10

Web control


<input type="checkbox" id="bold">
<label for="bold">Bold</label>

DOM tree

DOM tree

Accessibility tree

Accessibility tree

Manipulating the DOM

With HTML...

You get:

Interactive elements

HTML has natively interactive elements, including:

Expected interactions

Interactive elements have expected interactions:

Implicit semantics

Most HTML elements have default semantics (role and state):

HTML screen reader demo

So why don't we do that?

Primitive building blocks

The <div> and <span> elements have:

Rule 1 - Focus

Interactive things must be focusable

DOM order

The tab order follows the DOM order of interactive elements

DOM order example


<button>1</button>
<button>2</button>
<button>3</button>

DOM order demo

The tabindex attribute

tabindex="0" example


<button>1</button>
<span id="button" tabindex="0">2</span>
<button>3</button>

tabindex="0" demo

tabindex="-1" example


<button><1</button><
<button tabindex="-1"><2</button><
<button><3</button><

tabindex="-1" demo

tabindex="1" example


<button tabindex="3">1</button>
<button tabindex="2">2</button>
<button tabindex="1">3</button>

tabindex="1" demo

Roving tabindex

Roving tabindex example


  <ul>
    <li>
        <a href="#panel1" id="tab1">Blanco</a>
    </li>
    <li>
       <tabindex="-1" a href="#panel2" id="tab2">Reposado</a>
    </li>
        <li>
       <tabindex="-1" a href="#panel3" id="tab3">Joven</a>
    </li>
</ul>

Roving tabindex demo

Disclosure demo

ljwatson.github.io/design-patterns/di sclosure3/

Basic HTML


<span id="button">Tequila <span id="icon"></span></span>
<div id="content">Makes me happy</div>

Disclosure demo

Disclosure button

Add focus


<span id="button" tabindex="0">Tequila <span 
id="icon"></span></span>

<script>
  var button = document.getElementById('button');
  button.setAttribute('tabindex', 0);
</script>

Rule 2 - Interaction

Interactive things must have expected behaviours

Mouse events


button.addEventListener('click', disclose, false);

Keyboard events


button.addEventListener('keydown', function(event) {
    if (event.keyCode == 13 || event.keyCode ==32) {
        disclose();
    }
});

Touch events

300ms event dispatch delay

Mousemove not tracked

Touch Events & Pointer Events

Getting touchy

Patrick Lauke's Getting touchy deck and Getting touchy talk.

Rule 3 - Semantics

When using primitives provide semantic information

Role

Most HTML elements have roles that define their purpose

<img> element

Has a default implicit role of "graphic" or "image"


<img src="tequila.png" alt="Chamuco's tequila">

State

Many HTML elements have associated states

required attribute

Indicates when a form input is required


<input type="checkbox" id="terms" required>
<label>Accept terms</label>

ARIA

Add button role


button.setAttribute('role', 'button');

Add aria-expanded attribute


button.setAttribute('aria-expanded', 'false');

Add aria-hidden attribute


icon.setAttribute('aria-hidden', 'true');

Add hidden attribute


content.setAttribute('hidden', 'true');

Add functionality


if (content.hasAttribute('hidden')) {
    button.setAttribute('aria-expanded', 'true');
    button.setAttribute('aria-controls', 'content');
    content.removeAttribute('hidden');
}
else {
    button.setAttribute('aria-expanded', 'false');
    content.setAttribute('hidden', 'true');
    button.removeAttribute('aria-controls');
}

Disclosure screen reader demo

Ember component


App.TequilaButtonComponent = Ember.Component.extend({
	tagName: 'tequila-button',
	nameBinding: 'tequila.name',
	attributeBindings: ['tabindex'],
	answer: false,
	label: function() { return "Is " + this.get('name') + " tequila good?"; }
					.property('name'),
	tabindex: 1,
	click: function(event) { alert('Yes'); }
});
	

Rendered DOM


<tequila-button tabindex="1" class="ember-view" id="ember260">
	<script type="text/x-placeholder" id="metamorph-2-start"></script>
	Is Reposado tequila good?
	<script type="text/x-placeholder" id="metamorph-2-end"></script>
</tequila-button>

Ember component Screen reader demo

Accessible Ember component


App.TequilaButtonComponent = Ember.Component.extend({
	tagName: 'tequila-button',
	nameBinding: 'tequila.name',
	attributeBindings: ['label:aria-label', 'tabindex'],
	answer: false,
	label: function() { return "Is " + this.get('name') + " tequila good?"; }
					.property('name'),
	tabindex: 0,
	ariaRole: 'button',
	click: function(event) { alert('Yes'); },
	keyDown: function(event) {
		if (event.keyCode == 13 || event.keyCode == 32) { this.click(event); }
	}
});
	

Rendered DOM


<tequila-button role="button" tabindex="0" aria-label="Is Reposado tequila good?"
 				class="ember-view" id="ember260">
	<script type="text/x-placeholder" id="metamorph-2-start"></script>
	Is Reposado tequila good?
	<script type="text/x-placeholder" id="metamorph-2-end"></script>
</tequila-button>

Accessible Ember component Screen reader demo

Tenon API

Use the Tenon API with your build tools

Installation

Install a Tenon module for Grunt, Gulp or other environments

Required parameters

Send a post request to the API with required parameters:

Optional parameters

Choose optional parameters such as:

More tools

Test it yourself

Abandon your mouse, turn on a screen reader, zoom in/out

Thank you!