Contrôles DHTML personnalisés navigables au clavier
===================================================

### Le problème : les pages DHTML actuelles ne sont pas accessibles au clavier

 Un nombre croissant d'applications Web utilise JavaScript pour imiter des contrôles (
 *widgets*
 ) applicatifs comme des menus, des vues arborescentes, des champs de texte enrichis et des panneaux à onglets. Les développeurs Web innovent constamment et les applications futures contiendront des éléments complexes et interactifs comme des feuilles de calcul, des calendriers, des graphes organisationnels et plus encore. Jusqu'à présent, les développeurs désirant rendre leurs contrôles basés sur des `<div>` et autres `<span>` stylés ne disposaient pas des techniques nécessaires. Pourtant, l'accessibilité au clavier fait partie des nécessités dont tout développeur Web devrait tenir compte.

Prenons un exemple concret : la plupart des menus DHTML ne se comportent pas comme des menus normaux en ce qui concerne l'accès au clavier. Même s'il y a moyen d'accéder au menu avec le clavier, une erreur courante est de placer chaque élément du menu dans l'ordre de tabulation (souvent réalisé implicitement en faisant de chaque choix du menu un élément `<a>`). En réalité, le comportement correct d'un menu est que le menu entier doit figurer une seule fois dans l'ordre de tabulation, et les flèches doivent être utilisées pour se déplacer de choix en choix au sein du menu. Ceci vaut également pour les autres contrôles de « navigation groupée » comme les vues arborescentes, tableaux et panneaux à onglets.

Il est à présent possible pour les auteurs HTML de faire les choses correctement. La manière de rendre ces contrôles compatibles avec les technologies d'assistance est détaillée dans : ARIA : Applications riches Internet accessibles.

### La solution : modifier le comportement standard de `tabindex`

Firefox 1.5 suit l'exemple de Microsoft Internet Explorer en étendant l'attribut `tabindex` pour permettre à n'importe quel élément d'obtenir ou non le focus. En suivant le système d'IE pour `tabindex`, il devient possible de permettre aux contrôles DHTML, déjà accessibles au clavier dans IE, de l'être également dans Firefox 1.5. Les règles doivent subir quelques petites entorses afin de permettre aux auteurs de rendre leurs contrôles personnalisés accessibles.

Le tableau qui suit décrit le nouveau comportement de `tabindex` :

| Attribut `tabindex` | Focus disponible à la souris ou par JavaScript via `element.focus()` | Navigable avec tabulation |
| --- | --- | --- |
| non présent | Suit le comportement par défaut de l'élément (oui pour les contrôles de formulaires, les liens, etc). | Suit le comportement par défaut de l'élément. |
| Négatif (par exemple `tabindex="-1"`) | Oui | Non, l'auteur doit donner le focus avec `element.focus()` suite à l'utilisation des flèches ou d'autres touches. |
| Zéro (par exemple `tabindex="0"`) | Oui | Dans l'ordre de tabulation relativement à la position de l'élément dans le document. |
| Positif (par exemple `tabindex="33"`) | Oui | La valeur `tabindex` change manuellement lorsque cet élément est positionné dans l'ordre de tabulation. Ces éléments seront positionnés dans l'ordre de tabulation avant les éléments ayant `tabindex="0"` ou qui sont naturellement *tabulables* . |

### Utilisation du nouveau système

Pour rendre un contrôle simple navigable avec tabulation, la solution est d'utiliser `tabindex="0"` sur l'élément `<div>>` ou `<span>` le représentant. Vous pouvez consulter un exemple d'une case à cocher basée sur un `<span>` accessible au clavier tant dans Firefox 1.5 que dans IE (bien que la règle `:before` pour l'image de la case à cocher ne fonctionne pas dans IE).

 Pour les contrôles de groupe (comme les menus, les panneaux à onglets, grilles ou vues arborescentes) l'élément parent doit avoir `tabindex="0"`, et chaque choix descendant (onglet/cellule/ligne) doit avoir `tabindex="-1"`. Un évènement `keydown` surveillant les flèches directionnelles peut ensuite utiliser `element.focus()` pour donner le focus au contrôle descendant approprié et lui donner un style lui donnant un aspect particulier montrant qu'il a le focus. Vous pouvez consulter un exemple d'une vue arborescente DHTML accessible au clavier et aux lecteurs d'écran dans Firefox (
 *nightlies*
 ). Le travail pour le faire fonctionner dans IE est encore en cours.

N'oubliez pas que ceci ne fait pas encore partie d'un standard W3C ou autre organisme officiel. Pour l'instant, il est nécessaire de faire quelques entorses aux règles afin d'obtenir une pleine accessibilité au clavier.

### Astuces d'écriture

#### Utilisation d'`onfocus` pour suivre le focus

Les attributs de gestion d'évènements `onfocus` et `onblur` peuvent à présent être utilisés sur tous les éléments. Il n'y a pas d'interface DOM standard pour obtenir l'élément ayant actuellement le focus dans le document, par conséquent il est nécessaire d'utiliser une variable JavaScript pour le suivre.

Ne supposez pas que tous les changements de focus viendront des évènements clavier ou souris, car les technologies d'assistance, comme les lecteurs d'écran, peuvent donner le focus à n'importe quel élément pouvant en disposer et cela doit être traité élégamment par le contrôle JavaScript.

#### Changement dynamique de la possibilité d'obtenir le focus à l'aide de la propriété `tabIndex`

Ceci peut être utile à réaliser si un contrôle personnalisé devient actif ou inactif. Les contrôles inactifs ne doivent pas être dans l'ordre de tabulation. Cependant, il est typiquement possible de les atteindre avec les flèches s'ils font partie d'un contrôle de navigation groupé.

#### Utilisation de `setTimeout` avec `element.focus()` pour donner le focus

N'utilisez pas `createEvent()`, `initEvent()` et `dispatchEvent()` pour donner le focus à un élément, parce que les évènements DOM `focus` sont seulement considérés comme informels — générés par le système après que quelque chose ait reçu le focus, mais pas réellement pour donner le focus. Le retardateur est nécessaire, tant dans IE que dans Firefox 1.5, pour empêcher les scripts de faire des choses étranges et inattendues si l'utilisateur clique sur des boutons ou d'autres contrôles. Concrètement, le code pour donner le focus à un élément ressemblera à quelque chose comme ceci :


```js
setTimeout("gFocusItem.focus();", 0); // gFocusItem doit être une variable globale
```


#### Ne pas utiliser `:focus` ou des sélecteurs d'attribut pour styler le focus

Il ne sera pas possible d'utiliser `:focus` ou des sélecteurs d'attribut pour styler l'élément ayant le focus, si vous voulez que cela apparaisse également dans IE. Changez plutôt le style dans un gestionnaire d'évènement `onfocus`. Par exemple, pour le traitement du focus d'un élément de menu, ajoutez `this.style.backgroundColor = "gray";`.

#### Toujours dessiner le focus pour les éléments avec `tabindex="-1"` et qui reçoivent le focus par programmation

IE ne dessinera pas automatiquement l'encadrement du focus pour les éléments qui reçoivent le focus de manière programmée. Choisissez entre changer la couleur de fond via quelque chose comme `this.style.backgroundColor = "gray";` ou ajoutez une bordure pointillée via `this.style.border = "1px dotted invert"`. Dans le cas d'une bordure pointillée, il sera nécessaire de s'assurer que ces éléments aient une bordure invisible de `1px` au départ, afin que l'élément ne change pas de taille lorsque le style de bordure est appliqué (les bordures prennent de la place et IE n'implémente pas les encadrements CSS).

#### Utilisation de `onkeydown` pour les évènements clavier, plutôt que `onkeypress`

IE ne déclenchera pas les évènements `keypress` pour les touches non alphanumériques.

#### Empêcher les évènements clavier d'effectuer des fonctions du navigateur

Si une touche comme une flèche directionnelle est utilisée, empêchez le navigateur d'utiliser cette touche pour faire quelque chose d'autre (comme faire défiler la page) en utilisant un code similaire à ce qui suit :


```html
<span tabindex="-1" onkeydown="return handleKeyDown();"></span>
```


Si `handleKeyDown()` renvoie `false`, l'évènement sera consommé, empêchant le navigateur d'effectuer quelque action que ce soit, basée sur la touche pressée.

#### Utilisation d'évènements clavier pour permettre l'activation de l'élément

Pour chaque gestionnaire d'évènement lié à la souris, un évènement clavier correspondant est nécessaire. Par exemple, si vous avez `onclick="faireQuelqueChose()"` vous aurez aussi besoin de `onkeydown="return event.keyCode != 13 || faireQuelqueChose();"` afin de permettre à la touche Entrée d'activer cet élément.

#### Utilisation de try/catch pour éviter les erreurs JavaScript

Ce système n'est actuellement pas supporté par Opera, Safari et les versions anciennes de Mozilla (1.7 et précédentes). Comme certains navigateurs ne supportent pas les nouvelles possibilités comme la propriété `tabIndex` sur tous les éléments, utilisez try/catch aux endroits appropriés. Les contrôles doivent rester utilisables avec la souris sur les navigateurs ne supportant pas le système DHTML de navigation au clavier. Son support est déjà planifié pour Opera et Safari (via les spécifications du WHATWG).

#### Ne pas se baser sur un comportement cohérent de la répétition d'une touche, pour l'instant

Malheureusement, `onkeydown` peut ou non être répété suivant le système d'exploitation utilisé. Consultez le bug Firefox 91592 dans la base de données Bugzilla.