Les listes ARIA (listbox)

Introduction

ARIA met à disposition un grand nombre de composants qui peuvent servir dans les interfaces riches (onglet, accordéon, potentiomètre (slider)…). Dans cet exemple, nous allons nous intéresser à l’utilisation des listbox.

Une listbox est un composant qui permet à l’utilisateur de sélectionner un ou plusieurs éléments à partir d’une liste de choix.

Les éléments d’une listbox sont statiques, une listbox n’est pas destinée à recevoir des éléments interactifs (tels que des liens, des boutons…), en revanche celle-ci peut contenir des images à l’inverse d’un select.

Plus d’infos : WAI-ARIA Authoring Practices.

Mise en garde

Bien que des progrès soient réalisés à chaque nouvelle version, le support de l’ARIA est encore partiel pour l’ensemble des lecteurs d’écran. Son utilisation doit être conditionnée par des tests de compatibilité sur l’environnement cible (couple navigateur/lecteur d’écran).

Mise en place

Code HTML

Pour créer une listbox, il suffit d’appliquer le rôle listbox à un conteneur parent. Puis d’appliquer le rôle option aux différentes options de la liste. On n’oubliera pas de rendre cette liste focusable pour les utilisateurs qui naviguent à l’aide du clavier (ajout d’un attribut tabindex="0") ni d’ajouter un label (aria-label ou aria-labelledby) pour les personnes qui utilisent un lecteur d’écran. Enfin, on ajoutera également un attribut tabindex="-1" aux options pour qu’elles puissent recevoir le focus via Javascript.


    <ul role="listbox" tabindex="0" aria-label="liste des dossiers">
        <li tabindex="-1" role="option">Boite de réception (4) <span class="sr-only">messages non lus</span></li>
        <li tabindex="-1" role="option">Eléments envoyés</li>
        <li tabindex="-1" role="option">Brouillons</li>
        <li tabindex="-1" role="option">Corbeille</li>          
    </ul>
      

À noter que les balises ul et li pourraient être remplacées par de simples balises div.

Interactions

Pour coller avec le pattern ARIA de la listbox (WAI-ARIA Authoring Practices), on va gérer quelques interactions à l’aide de code Javascript.

Lors de la prise du focus, si aucun élément n’est sélectionné, on doit automatiquement sélectionner la première option. Si un élément est déjà sélectionné lorsque la listbox reçoit le focus, on doit déplacer le focus sur celui-ci.


       $("[role=listbox]").on("focus", function () {
           // If no selected element, select the first by default
           if (!$(this).find("[aria-selected=true]").length) {               
                $(this).find("[role=option]:first").attr("aria-selected", "true").focus();
           } else {
               $(this).find("[aria-selected=true]").focus();
           }
        });
      

On va ensuite gérer l’interaction clavier :


    $("[role=listbox]").on("keydown", function (e) {            
        var currentItem = $(this).find("[aria-selected=true]");          
        switch (e.keyCode) {
            case 38:  // Up arrow
                if (currentItem.prev().length) {
                    currentItem.attr("aria-selected", "false");                    
                    currentItem.prev().attr("aria-selected", "true").focus();
                }                    
                e.preventDefault();
                break;
            case 40: // Down arrow
                if (currentItem.next().length) {
                    currentItem.attr("aria-selected", "false");
                    currentItem.next().attr("aria-selected", "true").focus();
                }
                e.preventDefault();
                break;
        }
    });
      

Enfin, il nous reste à gérer la souris. Lorsqu’une option est sélectionnée, on modifie son attribut aria-selected.


    $("[role=option]").on("mousedown", function (e) {
        $(this).parent().find("[aria-selected=true]").attr("aria-selected", "false");
        $(this).attr("aria-selected", "true");
        e.preventDefault();
    });
      

On ajoute un peu de cosmétique en modifiant le pointeur de la souris au survol des options.


    [role=option] {
        cursor: pointer;
    }
      

Exemple

Voici un exemple de liste implémentée à partir du code ci-dessus.

Liste des dossiers courrier

Place aux tests

Utilisation de la souris : on peut sélectionner un élément de la liste à l’aide de la souris.

Utilisation au lecteur d’écran : avec NVDA, il est possible de parcourir les différents éléments de la liste, il est également possible de connaître l’élément en cours de sélection.

Utilisation au clavier : à l’aide de la touche Tab, il est possible d’accéder à la liste, ensuite les flèches haut et bas permettent de déplacer la sélection. Par contre, les touches Maj+Tab devraient permettre de faire remonter le focus dans la page, or celui-ci est bloqué à l’intérieur de la liste.

Correction de la navigation au clavier

Lorsque le focus est positionné sur un élément de la liste, la combinaison de touches Maj+Tab fait remonter le focus sur le premier élément focusable qui dans ce cas est l’élément listbox. Or, dès que la listbox reçoit le focus, on déplace automatiquement le focus sur l’élément sélectionné.

Pour corriger le problème, il suffit de rendre la listbox non focusable une fois que le focus est situé sur une des options.


       $("[role=option]").on("focus", function (e) {
           $(this).parent().attr("tabindex", "-1");
       });
       
       $("[role=option]").on("blur", function (e) {
           $(this).parent().attr("tabindex", "0");
       });
      

Exemple fonctionnel

Liste des dossiers courrier

Place aux tests

Utilisation de la souris : OK

Utilisation au lecteur d’écran : OK

Utilisation au clavier : OK

Exemple webmail

Voici une maquette statique qui met en œuvre le composant listbox de cet exemple et celui de l’exemple sur les listbox avec cases à cocher.

Ouvrir le webmail

Liens

Plus d’infos sur ARIA et les listbox :