Concevoir un composant d’auto-complétion accessible

20 avril, par Équipe Access42

Aujourd’hui, nous partageons avec joie la traduction en français de l’article « Building an accessible autocomplete control » écrit d’Adam Silver et publié le 2 février 2020.

Nous remercions l’auteur pour son aimable autorisation.

L’extrait qui suit est tiré de mon livre, Form Design Patterns.

Vous pourrez le retrouver (en anglais) au milieu du chapitre 3, A Flight Booking Form, où je présente différentes manières de saisir un pays de destination dans un formulaire de réservation de vols.

Malheureusement, les contrôles HTML natifs des formulaires ne sont pas assez robustes pour ce type d’interaction.

Il est donc nécessaire d’envisager de concevoir un système d’auto-complétion depuis le début.

En guise d’avant-propos, je vous annonce dès maintenant que, contrairement à ce qu’on pourrait imaginer, l’auto-complétion est un des composants d’interface les plus complexes que j’aie jamais eu à réaliser.

Un élément d’auto-complétion.
Un élément d’auto-complétion affiche 3 suggestions. La deuxième apparaît en surbrillance.

L’auto-complétion affiche des suggestions qui correspondent à une chaîne de caractères saisis en temps réel.

Elle permet de sélectionner une suggestion pour terminer sa saisie rapidement avec une valeur exacte, ou alors de continuer à saisir des caractères pour affiner les options proposées.

Le code de base

Pour que l’auto-complétion fonctionne sans JavaScript, il faut commencer avec un élément de formulaire natif, fourni gratuitement par les navigateurs.

Comme je l’explique plus tôt dans mon livre, opter pour des boutons radio infligerait trop de possibilités de réponse aux utilisateurs et utilisatrices ; utiliser un moteur de recherche prend du temps et ne garantit pas d’obtenir un résultat ; et la solution de la datalist est trop peu fiable. Il ne reste alors plus que la liste déroulante.

<div class="field">
 <label for="destination">
   <span class="field-label">Destination</span>
 </label>
 <select name="destination" id="destination">
   <option value="">Sélectionner</option>
   <option value="">France</option>
   <option value="">Allemagne</option>
   <!-- … -->
 </select>
</div>

Le code enrichi

Lorsque JavaScript est disponible, le constructeur Autocomplete() que nous allons définir enrichira le HTML de base. On obtiendra alors :

<div class="field">
 <label for="destination">
   <span class="field-label">Destination</span>
 </label>
 <select name="destination" aria-hidden="true" tabindex="-1" class="visually-hidden">
   <!-- options ici -->
 </select>
 <div class="autocomplete">
   <input aria-owns="autocomplete-options--destination" autocapitalize="none" type="text" autocomplete="off"  aria-autocomplete="list" role="combobox" id="destination" aria-expanded="false">
   <svg focusable="false" version="1.1" xmlns="http://www.w3.org/2000/svg">
     <!-- reste du SVG ici -->
   </svg>
   <ul id="autocomplete-options--destination" role="listbox" class="hidden">
     <li role="option" tabindex="-1" aria-selected="false" data-option-value="1" id="autocomplete_1">
             France
     </li>
     <li role="option" tabindex="-1" aria-selected="true" data-option-value="2" id="autocomplete_2">
             Allemagne
     </li>
     <!-- plus d’options ici -->
   </ul>
   <div aria-live="polite" role="status" class="visually-hidden">
         13 résultats disponibles.
   </div>
 </div>
</div>

Masquer la liste déroulante sans empêcher l’envoi de sa valeur

Pour masquer la liste déroulante sans empêcher l’envoi de sa valeur au serveur, il faut ajouter :

  • visually-hidden pour rendre la liste déroulante invisible aux personnes voyantes ;
  • aria-hidden="true" pour la rendre invisible pour les personnes qui utilisent un lecteur d’écran ;
  • tabindex="-1" pour empêcher les personnes naviguant au clavier de positionner le focus dessus.

Si vous utilisez les 3 attributs ensemble, il est généralement recommandé d’utiliser la propriété display: none, qui génère le même résultat, mais avec un code plus propre.

Par contre, cette méthode empêcherait l’envoi au serveur de la valeur de la liste déroulante. Et c’est un point important parce que même si les utilisateurs et les utilisatrices n’auront pas à interagir directement avec la liste déroulante, la valeur correspondante doit quand même être envoyée pour être traitée.

Réattribuer l’étiquette

On transfert l’attribut id de la liste déroulante à la zone de saisie, parce que l’étiquette doit lui être associée, d’une part pour qu’elle soit restituée par les lecteurs d’écran, et d’autre part pour étendre sa zone de clic. Dans mon livre, je traite ce point dans le premier chapitre, A Registration Form, sur le formulaire d’inscription.

L’attribut name de la zone de texte n’est pas nécessaire puisque sa valeur n’est pas envoyée au serveur. Il remplit purement une fonction d’interaction et sert de proxy pour définir la valeur de la liste déroulante en arrière-plan.

À propos des attributs de la zone de saisie

La propriété role="combobox" garantit que l’élément sera bien restitué comme étant une combo box. Une combo box est un champ de saisie accompagné d’une liste de suggestions prédéfinies.

La propriété ,aria-autocomplete="list" permet d’indiquer qu’une liste d’options va s’afficher. La propriété aria-expanded permet d’indiquer si le menu est réduit ou développé selon que la valeur est définie à true ou ,false.

L’attribut ,autocomplete="off" empêche les navigateurs d’afficher leurs propres suggestions et donc d’interférer avec les suggestions proposées par notre composant.

Enfin, l’attribut autocapitalize="none" empêche les navigateurs d’ajouter automatiquement une majuscule à la première lettre de la saisie. Le chapitre 4 A Login Form, sur le formulaire de connexion, explore cette question plus en profondeur.

On utilise CSS pour superposer l’icône SVG sur la zone de saisie. L’attribut ,focusable="false" empêche que les éléments SVG ne prennent le focus par défaut dans Internet Explorer.

À propos des attributs du menu

La propriété role="list" est utilisée pour afficher le menu sous forme d’une liste puisqu’il contiendra une liste d’options. À chaque option est associée une propriété role="option".

La propriété aria-selected="true" permet d’indiquer quelle option au sein de la liste est sélectionnée ou non, selon que la valeur est définie sur true ou false.

L’attribut tabindex="-1" signifie que le focus peut être programmé pour se positionner sur l’option lorsque certaines touches de clavier sont activées. On se penchera sur ces interactions au clavier un peu plus tard.

L’attribut data-option-value enregistre la valeur de la liste déroulante. Lorsqu’on clique sur une option suggérée par l’auto-complétion, la valeur de la zone de saisie est actualisée. Les deux valeurs sont ainsi synchronisées. Cette action fait le pont entre l’interface (ce que la personne voit) et la valeur de la liste déroulante (ce qu’elle ne voit pas).

Utiliser une zone live pour informer les personnes utilisant un lecteur d’écran que des suggestions sont disponibles

Les personnes voyant l’écran verront les suggestions s’afficher dans le menu à mesure qu’elles saisiront des caractères, mais les personnes se servant d’un lecteur d’écran ne détermineront pas forcément les suggestions présentes dans le menu si elles ne quittent pas la zone de saisie pour explorer ce menu.

Afin de proposer à ces personnes une expérience identique (premier principe de conception inclusive), on va utiliser une zone live, que j’étudie plus en détail dans le chapitre A Checkout Form.

À mesure que le menu est généré, la zone live se remplira d’autant de résultats disponibles. Par exemple : « 13 résultats disponibles ». Avec ces informations à disposition, les utilisateurs et utilisatrices de lecteurs d’écran peuvent décider de poursuivre leur saisie pour affiner les résultats ou alors sélectionner l’une des suggestions à partir du menu.

Étant donné que ces informations ne sont utiles que pour les personnes qui utilisent un lecteur d’écran, on les masque en utilisant visually-hidden, une fois de plus.

Gérer la saisie de texte

Lorsque quelqu’un saisit des caractères dans la zone de texte, il faudra écouter, en JavaScript, l’activation de certaines touches.

Autocomplete.prototype.createTextBox = function() {
 this.textBox.on('keyup', $.proxy(this, 'onTextBoxKeyUp'));
};
Autocomplete.prototype.onTextBoxKeyUp = function(e) {
 switch (e.keyCode) {
   case this.keys.esc:
   case this.keys.up:
   case this.keys.left:
   case this.keys.right:
   case this.keys.space:
   case this.keys.enter:
   case this.keys.tab:
   case this.keys.shift:
     // ignorer, sinon le menu s’affichera
     break;
   case this.keys.down:
     this.onTextBoxDownPressed(e);
     break;
   default:
     this.onTextBoxType(e);
 }
};

L’objet this.keys est un ensemble de nombres qui correspondent à des touches spécifiques en fonction de leur nom. Ça permet d’éviter les nombres magiques, rendant le code plus facile à comprendre.
L’instruction switch exclut les touches Echap, Flèche Haut, Flèche Gauche, Flèche Droite, Espace, Entrée, Tabulation, et Shift. Sans ça, le cas par défaut s’exécuterait, affichant à tort le menu.

Au lieu de préciser ces touches qui ne sont pas concernées, on aurait pu préciser les touches qui, à l’inverse, sont concernées. Mais ça nécessiterait de préciser une longue liste de touches, au risque d’en oublier.

Les principales instructions qui nous intéressent sont les deux dernières, à savoir le cas où la personne appuie sur la touche Flèche Bas, et le cas par défaut mentionné juste avant, c’est-à-dire tout le reste (une lettre, un nombre, un symbole, etc.). Dans ce cas-là, la fonction onTextBoxType() sera appelée.

Autocomplete.prototype.onTextBoxType = function(e) {
 // afficher les options uniquement lorsque des caractères sont saisis
 if(this.textBox.val().trim().length > 0) {
   // retrouver les options en fonction de la valeur saisie
   var options = this.getOptions(this.textBox.val().trim().toLowerCase());

   // constituer le menu en fonction des options
   this.buildMenu(options);

   // afficher le menu
   this.showMenu();

   // actualiser la zone live
   this.updateStatus(options.length);
 }
 // actualiser la valeur de la liste déroulante que
 // le serveur utilise pour traiter les données
 this.updateSelectBox();
};

La méthode getOptions() (étudiée plus loin) filtre les options en fonction de la saisie.

Prévoir une seule tabulation pour les contrôles composites

L’auto-complétion est un contrôle composite ce qui signifie qu’il est constitué d’éléments interactifs et d’éléments pouvant recevoir le focus. Par exemple : on saisit des caractères dans la zone de texte puis on se déplace vers le menu pour sélectionner une option.

Les contrôles composites ne doivent susciter qu’une seule tabulation, conformément à la spécification WAI-ARIA Authoring Practices 1.1 qui indique :

Il existe une pratique de navigation au clavier, admise par toutes les plateformes, selon laquelle les touches Tabulation et Maj+Tabulation déplacent le focus d’un composant d’interface à un autre tandis que d’autres touches, principalement les flèches de direction, déplacent le focus à l’intérieur des composants d’interface qui possèdent plusieurs éléments pouvant recevoir le focus. On appelle ce parcours du focus à la pression de la touche Tabulation « séquence de tabulation » ou « boucle de tabulation » (tab ring, en anglais). (Traduction libre)

Un ensemble de boutons radio est aussi un contrôle composite.

Une fois que le premier bouton radio a reçu le focus, il est possible d’utiliser les flèches de direction pour se déplacer entre chaque option. Appuyer sur la touche Tabulation déplacera le focus sur l’élément tabulable le plus proche dans la séquence de tabulation.

Retournons maintenant à l’auto-complétion.

La zone de saisie reçoit naturellement le focus quand on appuie sur la touche Tabulation. Une fois le focus positionné dessus, on pourra alors appuyer sur les touches de direction pour parcourir le menu. On regardera ça en détail un peu plus loin.

Si on appuie sur la touche Tabulation dès lors que le focus est positionné sur la zone de saisie ou sur une option du menu, le menu devrait disparaître de façon à ne pas masquer le contenu en dessous lorsque le menu ne sert pas. On verra un peu plus tard comment y arriver.

Utiliser ARIA activedescendant est incompatible avec l’auto-complétion

Souvent, les systèmes d’auto-complétion utilisent la propriété aria-activedescendant comme solution alternative pour s’assurer qu’il n’y ait qu’une seule tabulation.

Cet attribut permet de conserver le focus sur le conteneur du composant en permanence, et de restituer l’élément qui est activé à ce moment-là.
Mais ça ne fonctionne pas avec l’auto-complétion parce que la zone de saisie est sœur du menu et non le parent.

Masquer le menu à la perte du focus (blur) ne fonctionne pas

L’événement onblur est déclenché lorsqu’on quitte un élément qui jusqu’alors avait le focus. Dans le cas de l’auto-complétion, on pourrait écouter cet événement sur la zone de saisie.

L’avantage de l’événement onblur est qu’il sera déclenché dès qu’on quitte la zone de saisie au moyen de la touche Tabulation et en cliquant ou en touchant une zone en dehors de l’élément.

this.textBox.on('blur', function(e) { // masquer le menu });

Mais malheureusement, déplacer le focus sur le menu programmatiquement déclenche la perte du focus, masquant le menu. Résultat, le menu devient inaccessible aux personnes naviguant exclusivement au clavier.

Une solution consiste à utiliser setTimeout(), qui nous permet d’associer un délai à l’événement. Ce délai nous laisse le temps d’annuler l’événement en utilisant clearTimeout() dans le cas où l’utilisateur ou l’utilisatrice déciderait de déplacer le focus sur le menu avant que ce délai ne se soit écoulé.

Cette solution empêche le menu de disparaître, le rendant donc accessible de nouveau.

this.textBox.on('blur', $.proxy(function(e) {
 // définir un délai avant de masquer le menu
 this.timeout = window.setTimeout(function() {
   // masquer le menu
 }, 100);
}, this));

this.menu.on('focus', $.proxy(function(e) {
 // annuler masquer le menu
 window.clearTimeout(this.timeout);
}, this));

Mais cette méthode n’est pas satisfaisante, car l’événement blur présente des problèmes sous iOS 10. L’événement blur est déclenché de façon erronée sur la zone de saisie dès lors que l’on masque le clavier virtuel. Le menu devient donc complètement inaccessible.

La véritable solution est la suivante.

Masquer le menu en écoutant l’utilisation de la touche tabulation

Au lieu de masquer le menu en utilisant l’événement blur, on peut utiliser l’événement keydown (touche enfoncée) pour détecter l’utilisation de la touche Tabulation.

this.textBox.on('keydown', $.proxy(function(e) {
 switch (e.keyCode) {
   case this.keys.tab:
     // masquer le menu
     break;
 }
}, this));

Mais contrairement à l’événement blur, la méthode keydown (touche enfoncée) ne prend pas en compte les cas où un élément perd le focus parce qu’on a cliqué en dehors de celui-ci.

On corrigera ça en écoutant l’événement click au sein du document et en veillant à masquer le menu uniquement lorsqu’on clique en dehors de celui-ci.

$(document).on(’click’, $.proxy(function(e) if(!this.container[0].contains(e.target)) // masquer le menu , this)) ;

Appuyer sur la touche Flèche Bas pour déplacer le focus sur le menu

Lorsque la zone de saisie reçoit le focus, appuyer sur la touche Flèche Bas déclenche la fonction onTextBoxDownPressed().

Autocomplete.prototype.onTextBoxDownPressed = function(e) {
 var option;
 var options;
 var value = this.textBox.val().trim();
 /*
   Lorsque la valeur est vide ou si elle correspond exactement
   à une option, afficher le menu en entier
 */
 if(value.length === 0 || this.isExactMatch(value)) {

   // récupérer les options en fonction de la valeur
   options = this.getAllOptions();

   // constituer le menu en fonction des options
   this.buildMenu(options);

   // afficher le menu
   this.showMenu();

   // récupérer la première option du menu
   option = this.getFirstOption();

   // mettre la première option en surbrillance
   this.highlightOption(option);

 /*
   Lorsqu’une valeur ne possède pas de correspondance exacte,
   afficher les options qui s’en rapprochent
 */
 } else {

   // récupérer les options en fonction de la valeur
   options = this.getOptions(value);

   // s’il y a des options
   if(options.length > 0) {

     // constituer le menu en fonction des options
     this.buildMenu(options);

     // afficher le menu
     this.showMenu();

     // récupérer la première option du menu
     option = this.getFirstOption();

     // mettre la première option en surbrillance
     this.highlightOption(option);
   }
 }
};

Si on appuie sur la touche Flèche Bas sans avoir saisi de contenu dans la zone de texte, le menu affichera toutes les options et le focus se positionnera sur la première option.

Il en va de même si on saisit une correspondance exacte. Mais ce cas est rare dans la pratique. Généralement, on sélectionne une option parmi celles proposées, pour aller plus vite.

La condition else remplira le menu avec les options qui correspondent (s’il y en a), et le focus se positionnera sur la première d’entre elles. Ces deux scénarios se terminent avec un appel de la fonction highlightOption(), que nous allons examiner plus loin.

Faire défiler les suggestions du menu

Le menu peut contenir des centaines d’options. Pour s’assurer que les éléments du menu soient visibles, on utilisera les styles suivants :

.autocomplete [role=listbox] {
 max-height: 12em;
 overflow-y: scroll;
 -webkit-overflow-scrolling: touch;
}

La propriété max-height permet au menu de s’agrandir jusqu’à ce qu’il atteigne une hauteur maximum. Dès lors que le contenu du menu dépasse cette hauteur, on pourra faire défiler les propositions grâce à la propriété overflow-y: scroll.

La dernière propriété (non standard) active le défilement momentum (momentum scrolling) sur iOS, ce qui permet à l’auto-complétion d’avoir un défilement qui fonctionne de la même façon partout.

Sélectionner une option

On utilisera la méthode de la délégation d’événement pour détecter un clic sur une option. C’est une méthode plus efficace que celle qui consisterait à ajouter un événement click à chaque option.

Autocomplete.prototype.createMenu = function() {
 this.menu.on('click', '[role=option]', $.proxy(this, 'onOptionClick'));
};

Autocomplete.prototype.onOptionClick = function(e) {
 var option = $(e.currentTarget);
 this.selectOption(option);
};

Le gestionnaire d’événement récupère l’option (avec e.currentTarget) et la transmet à selectOption().

Autocomplete.prototype.selectOption = function(option) {
 var value = option.attr('data-option-value');
 this.setValue(value);
 this.hideMenu();
 this.focusTextBox();
};

La méthode selectOption() prend l’option devant être sélectionnée et extrait la valeur de son attribut data-option-value. Cette valeur est alors transmise vers setValue(), et s’affiche dans la zone de saisie ainsi que dans la liste déroulante masquée. Enfin, le menu est masqué et la zone de saisie prend le focus.

Le même fonctionnement est enclenché dès lors qu’on sélectionne une option à l’aide des touches Espace ou Entrée.

Interagir avec le menu à l’aide d’un clavier

Une fois que le focus arrive à l’intérieur du menu, on peut permettre aux utilisateurs et utilisatrices de parcourir le menu au clavier grâce à l’écoute de l’événement keydown (touche enfoncée).

Autocomplete.prototype.createMenu = function() {
 this.menu.on('keydown', $.proxy(this, 'onMenuKeyDown'));
};

Autocomplete.prototype.onMenuKeyDown = function(e) {
 switch (e.keyCode) {
   case this.keys.up:
     // Réaliser une action
     break;
   case this.keys.down:
     // Réaliser une action
     break;
   case this.keys.enter:
     // Réaliser une action
     break;
   case this.keys.space:
     // Réaliser une action
     break;
   case this.keys.esc:
     // Réaliser une action
     break;
   case this.keys.tab:
     // Réaliser une action
     break;
   default:
     this.textBox.focus();
 }
};
Liste des actions déclenchées en fonction des touches activées.
Touche Action
Flèche Haut Si le focus est positionné sur la première option, cette touche positionne alors le focus sur la zone de saisie. Sinon, le focus se positionne sur l’option précédente.
Flèche Bas Positionne le focus sur l’option suivante du menu. Si le focus est sur la dernière option du menu, il ne se passe rien.
Tabulation Masque le menu.
Entrée ou Espace Sélectionne l’option déjà en surbrillance et déplace le focus sur la zone de saisie.
Echap Masque le menu et positionne le focus sur la zone de saisie.
Toutes les autres touches Positionnent le focus sur la zone de saisie (pour permettre de continuer à taper).

Mettre en surbrillance les options recevant le focus

Lorsque l’utilisateur ou l’utilisatrice déplace le focus sur une option à l’aide de la touche Flèche Haut ou Flèche Bas, la fonction highlightOption() est appelée.

Autocomplete.prototype.highlightOption = function(option) {
 // si une option est actuellement sélectionnée
 if(this.activeOptionId) {

   // récupérer l’option
   var activeOption = this.getOptionById(this.activeOptionId);

   // désélectionner l’option
   activeOption.attr('aria-selected', 'false');
 }

 // définir la nouvelle option comme étant sélectionnée
 option.attr('aria-selected', 'true');

 // Si l’option n’est pas visible dans le menu
 if(!this.isElementVisible(option.parent(), option)) {

   // la rendre visible en définissant sa position au sein du menu
   option.parent().scrollTop(option.parent().scrollTop() + option.position().top);
 }

 // conserver une nouvelle option pour les fois suivantes
 this.activeOptionId = option[0].id;

 // positionner le focus sur l’option
 option.focus();
};

Cette méthode effectue plusieurs tâches.

Premièrement, elle vérifie si une option avait déjà été sélectionnée. Le cas échéant, sa propriété aria-selected est alors définie à false, ce qui permet de s’assurer que cet état est bien restitué aux personnes utilisant un lecteur d’écran.

Deuxièmement, la propriété aria-selected de la nouvelle option sélectionnée passe à true.

Étant donné que la hauteur du menu est fixe, il se peut que la nouvelle option ne s’affiche pas dans la zone visible du menu. On vérifiera si c’est le cas ou non en utilisant isElementVisible().

Si l’option n’est pas visible, on ajustera le niveau de défilement du menu avec scrollTop() pour s’assurer que l’option est bien visible.

Ensuite, on mémorise l’option sélectionnée pour pouvoir la réutiliser lorsque la fonction sera de nouveau appelée pour une option différente. Enfin, l’option reçoit le focus pour garantir la restitution de sa valeur par les lecteurs d’écran.

Pour informer les personnes voyant l’écran, on peut utiliser le même sélecteur d’attribut CSS [aria-selected=true] de la manière suivante :

.autocomplete [role=option][aria-selected="true"] {
 background-color: #005EA5;
 border-color: #005EA5;
 color: #ffffff;
}

Associer état et style est une bonne initiative ; ça permet d’assurer que les changements d’état sont restitués de manière interopérable. La forme devrait découler de la fonction, assurant ainsi directement leur synchronisation.

Filtrer les options

Un filtre performant passe outre les erreurs typographiques mineures ou l’utilisation d’une casse hétérogène.

Petit rappel : n’oubliez pas que les données qui génèrent les suggestions se trouvent à l’intérieur des éléments option.

<select>
 <option value="">Sélectionner</option>
 <option value="">France</option>
 <option value="">Allemagne</option>
</select>

Comme je l’ai mentionné plus haut, la fonction getOptions() est appelée lorsqu’il faut remplir le menu avec des suggestions pertinentes.

Autocomplete.prototype.getOptions = function(value) {
 var matches = [];

 // Parcourir la liste des options
 this.select.find('option').each(function(i, el) {
   el = $(el);
   // si l’option est associée à une valeur et que le nœud texte de l’option correspond à la valeur saisie
   if(el.val().trim().length > 0 && el.text().toLowerCase().indexOf(value.toLowerCase()) > -1) {

     // envoyer une représentation d’objet à la matrice de correspondances
     matches.push({ text: el.text(), value: el.val() });
   }
 });

 return matches;
};

Cette méthode prend la valeur saisie par l’utilisateur ou l’utilisatrice comme paramètre. Elle parcourt ensuite chaque option avant de comparer les caractères saisis avec le contenu de l’option (c’est-à-dire le texte indiqué à l’intérieur de la balise).

C’est indexOf() qui vérifie si la saisie contient une occurrence de la valeur renseignée. Dans la pratique, ça permet de saisir des noms de pays incomplets et quand même se voir proposer des suggestions pertinentes.

La valeur est extraite et transposée en minuscules, ce qui veut dire que les options s’afficheront même si, par exemple, le verrouillage de la majuscule a été activé. Les utilisateurs et utilisatrices ne devraient pas avoir à résoudre ces problèmes s’il est possible d’y remédier automatiquement.

Chaque saisie correspondant à une option existante est ajoutée à un tableau de correspondances, qui sera utilisé à l’appel de la fonction pour remplir le menu. Chaque option avec une correspondance est ajoutée à la matrice des correspondances, qui sera utilisée par la fonction d’appel pour remplir le menu.

Admettre les endonymes et les incohérences typographiques

Un endonyme est un nom qu’utilisent les personnes originaires d’une région ou d’un lieu donné pour désigner cette région ou ce lieu (ou la langue ou le peuple associés).

Par exemple, « Allemagne » en allemand se dit « Deutschland ». En laissant les utilisateurs et utilisatrices saisir un endonyme, on respecte le cinquième principe de conception inclusive (« Donner le choix »).

Pour ce faire, il faut d’abord répertorier ces endonymes quelque part. On peut les référencer dans un attribut de données au niveau de l’élément option.

<select>
 <!-- plus d’options -->
 <option value="2" data-alt="Deutschland">Allemagne</option>
 <!-- plus d’options -->
</select>

On peut maintenant modifier la fonction filter pour vérifier la valeur alternative :

Autocomplete.prototype.getOptions = function(value) {
 var matches = [];

 // Parcourir la liste des options
 this.select.find('option').each(function(i, el) {
   el = $(el);

   // si l’option est associée à une valeur et que le nœud texte de l’option correspond à la saisie ou si la valeur de l’attribut data-alt de l’option correspond à la saisie
   if( el.val().trim().length > 0
         && el.text().toLowerCase().indexOf(value.toLowerCase()) > -1
         || el.attr('data-alt')
         && el.attr('data-alt').toLowerCase().indexOf(value.toLowerCase()) > -1 ) {

     // envoyer une représentation d’objet à la matrice de correspondances
     matches.push({ text: el.text(), value: el.val() });
   }
 });

 return matches;
};

Si vous voulez, vous pouvez aussi utiliser cet attribut pour référencer les erreurs typographiques les plus courantes.

Démonstration

Maintenant que vous savez tout, voici une démonstration.

Traduction : Eleanor Hac.