Chapitre 4.3 — Bien utiliser tabindex
⏱️ TL;DR — L’attribut
tabindexcontrôle si — et comment — un élément reçoit le focus. Trois cas seulement à retenir :tabindex="0"ajoute l’élément à l’ordre de tabulation naturel (utile pour un composant custom qui doit être focusable) ;tabindex="-1"le rend focusable par JavaScript (element.focus()) mais pas viaTab(utile pour déplacer le focus par programme) ; et toute valeur positive (tabindex="1",2…) est un anti-pattern qui casse l’ordre du DOM. Règle d’or : n’ajoutetabindexque si tu as une vraie raison, et jamais de valeur positive.
🎯 Objectifs
- Comprendre les trois usages de
tabindex:0,-1, positif. - Savoir quand rendre un élément focusable — et quand surtout ne pas le faire.
- Comprendre pourquoi les valeurs positives sont à bannir.
- Découvrir le principe du roving tabindex (détaillé en Partie 10).
Les trois valeurs de tabindex
tabindex prend une valeur numérique, mais seuls trois comportements existent réellement.
| Valeur | Focusable au Tab ? | Focusable en JS (.focus()) ? | Usage |
|---|---|---|---|
tabindex="0" | ✅ oui, à sa place dans le DOM | ✅ oui | Rendre focusable un élément non interactif natif (rare). |
tabindex="-1" | ❌ non | ✅ oui | Cible de focus programmatique (modale, message, section). |
tabindex="1" et + | ✅ mais hors ordre DOM | ✅ oui | À bannir — casse le parcours du focus. |
tabindex="0" — entrer dans l’ordre naturel
tabindex="0" place l’élément dans l’ordre de tabulation à la position que lui donne le DOM, comme s’il était nativement interactif. Tu n’en as besoin que pour un élément qui n’est pas déjà focusable mais qui doit l’être — typiquement un composant custom.
<!-- ✅ Un composant custom qui doit être focusable (faute de mieux qu'un élément natif) -->
<div role="button" tabindex="0" onclick="basculer()">Options avancées</div>Mais attention : dès que tu écris ça, souviens-toi du Chapitre 4.1 — tu dois aussi gérer Entrée/Espace et le rôle à la main. 90 % du temps, un <button> natif est le bon choix et rend ce tabindex="0" inutile.
tabindex="-1" — focusable, mais pas par Tab
tabindex="-1" retire l’élément de l’ordre de tabulation (Tab le saute) tout en le rendant ciblable par JavaScript via element.focus(). C’est l’outil clé du Chapitre 4.4 : déplacer le focus sur une modale qu’on vient d’ouvrir, sur un titre après un changement de route, sur un message d’erreur.
<!-- ✅ On ne veut pas que Tab s'arrête ici, mais on veut pouvoir y ENVOYER le focus en JS -->
<div id="dialogue" role="dialog" tabindex="-1" aria-modal="true">
<h2>Confirmer la suppression</h2>
…
</div>// À l'ouverture, on déplace le focus sur le conteneur du dialogue
document.getElementById("dialogue").focus();Les valeurs positives — l’anti-pattern
tabindex="1", tabindex="2"… forcent un ordre de tabulation prioritaire, indépendant du DOM. Le navigateur visite d’abord tous les éléments à tabindex positif (dans l’ordre croissant), puis les éléments dans l’ordre normal. Résultat : un ordre de focus imprévisible, ingérable, qui se désynchronise dès qu’on ajoute un champ.
<!-- ❌ Ordre de focus artificiel et fragile : maintenance cauchemardesque -->
<input tabindex="3" name="email">
<input tabindex="1" name="nom">
<input tabindex="2" name="prenom"><!-- ✅ Aucun tabindex : l'ordre du focus suit l'ordre du DOM, logique et robuste -->
<input name="nom">
<input name="prenom">
<input name="email">Le bon ordre de focus s’obtient en ordonnant correctement ton HTML, pas en numérotant les champs. Si l’ordre te déplaît, corrige le DOM — voir Chapitre 4.2.
💡 Réflexe — Retiens la formule :
0pour ajouter,-1pour cibler en JS, positif jamais. Et avant même d’écrire untabindex, pose-toi la vraie question : « Est-ce que je ne devrais pas simplement utiliser un<button>, un<a>ou un<input>natif ? » La plupart destabindex="0"sont le symptôme d’une<div>qui aurait dû être un vrai élément interactif.
Rendre focusable… ou pas
Deux erreurs symétriques, aussi fréquentes l’une que l’autre :
- Ne pas rendre focusable ce qui devrait l’être : un contrôle custom (
<div>faisant office de bouton/onglet/case) sanstabindex→ l’utilisateur clavier ne l’atteint jamais. Solution : élément natif, ou à défauttabindex="0"+ rôle + gestion des touches. - Rendre focusable ce qui ne devrait pas l’être : coller
tabindex="0"sur des titres, des paragraphes, des<div>de mise en page → l’utilisateur clavier subit une avalanche d’arrêts inutiles sur du contenu non interactif. Le texte se lit avec le lecteur d’écran, il ne se tabule pas.
La cible se résume à : exactement les éléments interactifs sont dans l’ordre de tabulation — ni plus, ni moins.
Aperçu : le roving tabindex
Un motif que tu croiseras dans les composants riches (menus, listes d’onglets, grilles) : le roving tabindex (« tabindex baladeur »). Principe : dans un groupe d’éléments qui se parcourt aux flèches (et non au Tab), un seul élément porte tabindex="0" à un instant donné — celui qui a « le tour » — et tous les autres portent tabindex="-1". Quand l’utilisateur presse une flèche, on déplace le tabindex="0" sur l’élément voisin (et on lui donne le focus).
Effet : Tab entre et sort du composant comme d’un tout (un seul arrêt), et les flèches naviguent à l’intérieur. C’est exactement la distinction « entre / dans » du Chapitre 4.1. On l’implémente en détail avec les patterns APG en Partie 10 ; retiens juste le principe ici.
🧭 Sur A11yLearn — Sur A11yLearn, la barre d’onglets d’un cours (« Aperçu », « Leçons », « Ressources », « Discussion ») utilise le roving tabindex. Avant, chaque onglet était un lien :
Tabs’arrêtait quatre fois pour traverser la barre. Désormais, un seul onglet atabindex="0", les trois autrestabindex="-1":Tabfait un arrêt sur la barre, et les flèches passent d’un onglet à l’autre — le comportement attendu d’un jeu d’onglets, et celui que décrit l’APG.
✏️ Exercices
Exercice 1 — Corrige les tabindex. Un développeur a « forcé l’ordre » d’un formulaire. Explique le problème et propose la version correcte.
<input tabindex="2" name="mot_de_passe" type="password">
<input tabindex="1" name="identifiant">
<button tabindex="3" type="submit">Se connecter</button>✅ Solution
Les tabindex positifs créent un ordre artificiel, fragile et prioritaire sur le reste de la page. On les supprime et on ordonne le HTML logiquement :
<input name="identifiant">
<input name="mot_de_passe" type="password">
<button type="submit">Se connecter</button>L’ordre du focus suit alors le DOM (identifiant → mot de passe → bouton), sans aucun tabindex. Robuste et sans maintenance.
Exercice 2 — Quel tabindex ? Tu ouvres une modale par programme et tu veux y envoyer le focus, mais sans que Tab s’arrête sur son conteneur en temps normal. Quelle valeur de tabindex mets-tu sur le conteneur, et pourquoi ?
✅ Solution
tabindex="-1". Il rend le conteneur ciblable en JavaScript (conteneur.focus()) pour y déplacer le focus à l’ouverture, sans l’insérer dans l’ordre de tabulation Tab. C’est précisément le cas d’usage du -1 : une cible de focus programmatique. On verra la mécanique complète au Chapitre 4.4.
🧠 Quiz de révision
1. Que fait tabindex=“0” ?
Il ajoute l’élément à l’ordre de tabulation naturel, à la position que lui donne le DOM — comme s’il était nativement interactif. Utile pour rendre focusable un élément custom qui ne l’est pas nativement (rare : préfère un élément natif).
2. Quelle est la différence entre tabindex=“-1” et tabindex=“0” ?
tabindex="0" rend l’élément focusable au Tab ET en JS. tabindex="-1" le rend focusable uniquement en JS (.focus()), mais Tab le saute. Le -1 sert à créer des cibles de focus programmatiques (modale, message, section).
3. Pourquoi ne jamais utiliser de tabindex positif ?
Parce qu’il crée un ordre de tabulation artificiel et prioritaire sur l’ordre du DOM : le focus visite d’abord tous les éléments à valeur positive, puis le reste. C’est imprévisible, ingérable et ça se casse au moindre ajout. Le bon ordre s’obtient en ordonnant le HTML.
4. Faut-il mettre tabindex=“0” sur un titre pour qu’il soit lu ?
Non. Un titre se lit via le lecteur d’écran et la navigation par titres ; il n’a pas à être dans l’ordre de tabulation. Mettre tabindex="0" sur du contenu non interactif noie l’utilisateur clavier sous des arrêts inutiles.
5. En une phrase, qu’est-ce que le roving tabindex ?
Une technique où, dans un groupe navigable aux flèches, un seul élément porte tabindex="0" (les autres tabindex="-1") : Tab entre et sort du composant comme d’un tout, et les flèches naviguent à l’intérieur. Détaillé en Partie 10.
Chapitre suivant : Gérer le focus en JS — déplacer le focus après une action, le focus trap des modales, et les liens d’évitement.