Chapitre 3.4 — Boutons, liens & contrôles
⏱️ TL;DR — Deux éléments interactifs, deux intentions à ne jamais confondre :
<button>agit sur place (soumettre, ouvrir une modale, basculer un état) ;<a href>navigue (change d’URL, mène ailleurs). Le lecteur d’écran les annonce différemment (« bouton » vs « lien ») et l’utilisateur en attend des comportements distincts. Et surtout : jamais de<div>/<span>cliquable — tu perdrais focus, clavier et rôle. Les contrôles de formulaire natifs (<input>,<select>,<textarea>) suivent la même logique : ils apportent le clavier gratuitement (on les approfondit en Partie 9).
🎯 Objectifs
- Trancher entre
<button>(action) et<a href>(navigation) sans hésiter. - Comprendre ce que chaque natif apporte au clavier gratuitement.
- Bannir les
<div>/<span>cliquables et savoir pourquoi. - Situer les contrôles de formulaire natifs (aperçu avant la Partie 9).
<button> vs <a href> : action ou navigation
La distinction tient en une phrase : un lien t’emmène quelque part, un bouton fait quelque chose ici.
<a href="…">— navigation. Il change l’URL, mène vers une autre page ou une ancre. On peut l’ouvrir dans un nouvel onglet, le mettre en favori, le copier. Annoncé « lien ».<button>— action dans la page courante : soumettre un formulaire, ouvrir/fermer une modale, ajouter au panier, basculer un thème. Pas d’URL, rien à mettre en favori. Annoncé « bouton ».
Le test décisif : « est-ce que ça a une adresse (href) où l’on peut aller ? » Si oui → lien. Si l’action n’a de sens que dans cette page, ici et maintenant → bouton.
<!-- ❌ Rôles inversés : un lien pour agir, un bouton pour naviguer -->
<a href="#" onclick="ajouterAuPanier()">Ajouter au panier</a>
<button onclick="location.href='/cours'">Voir les cours</button>
<!-- ✅ Un bouton pour l'action, un lien pour la navigation -->
<button type="button" onclick="ajouterAuPanier()">Ajouter au panier</button>
<a href="/cours">Voir les cours</a>Le <a href="#"> piégé sur un onclick est un anti-pattern classique : c’est un lien qui ne mène nulle part (souvent il fait remonter la page en haut), détourné pour agir. Si c’est une action, c’est un <button>.
⚠️ Piège — Le lien qui devrait être un bouton, et l’inverse. Un
<a href="#">qui déclenche du JavaScript sans changer de page trompe l’utilisateur : le lecteur d’écran annonce « lien », il s’attend à naviguer, et rien ne se passe (ou la page saute en haut). Symétriquement, un<button>qui faitlocation.href = …prive l’utilisateur des comportements de lien (nouvel onglet, favori, clic milieu). Le rôle annoncé doit correspondre à ce qui se passe vraiment.
Ce que chaque natif apporte au clavier
C’est là que la sémantique paie comptant. Les deux éléments sont focusables et activables au clavier — mais pas de la même façon, et c’est une convention que les utilisateurs connaissent :
| Élément | Focus (Tab) | Activation clavier | Rôle annoncé |
|---|---|---|---|
<a href> | Oui | Entrée | lien |
<button> | Oui | Entrée et Espace | bouton |
Un <button> s’active à l’Espace et à Entrée ; un lien seulement à Entrée. Ces comportements sont natifs et gratuits. Sur une <div class="btn">, tu n’as rien : pas de focus, pas d’activation clavier, pas de rôle. Il faudrait tout recoder (tabindex="0", gestion de keydown pour Entrée ET Espace, prévention du scroll de l’Espace, role="button")… pour un résultat plus fragile que la balise d’origine.
Pourquoi jamais de <div> / <span> cliquable
On l’a posé au chapitre 3.1, on le martèle ici parce que c’est l’erreur la plus fréquente : une <div onclick> ou un <span onclick> n’est cliquable qu’à la souris. Récapitulatif de ce qui manque :
- Pas de focus : hors de l’ordre de tabulation, invisible au clavier.
- Pas d’activation clavier : ni Entrée ni Espace.
- Pas de rôle : le lecteur d’écran n’annonce ni « bouton » ni « lien ».
- Pas d’états :
disabled,aria-pressed… tout est à gérer soi-même.
<!-- ❌ Cliquable à la souris uniquement ; muet et inatteignable au clavier -->
<span class="link" onclick="ouvrirCours()">Ouvrir le cours</span>
<!-- ✅ Selon l'intention -->
<a href="/cours/1">Ouvrir le cours</a> <!-- navigation -->
<button type="button" onclick="ouvrirModale()">Aperçu</button> <!-- action -->💡 Réflexe — Chaque fois que tu écris
onclick, arrête-toi et demande : est-ce une navigation ou une action ? Navigation →<a href>. Action →<button>. Si la réponse est « euh, ni l’un ni l’autre », c’est presque toujours que tu vas poser unonclicksur une<div>— le signal qu’il faut revenir à un élément natif. Unonclicksur autre chose qu’un<a>, un<button>ou un vrai contrôle doit t’alerter.
Aperçu : les contrôles de formulaire natifs
Les mêmes principes s’étendent aux champs. Les contrôles natifs — <input> (texte, case, radio, date…), <select>, <textarea>, <button> — apportent chacun, gratuitement, leur rôle, leur comportement clavier et leurs états :
- Une case à cocher
<input type="checkbox">se coche à l’Espace, s’annonce « case à cocher, cochée / non cochée ». - Un groupe de radios
<input type="radio">se parcourt aux flèches (comportement de groupe natif). - Un
<select>s’ouvre et se parcourt au clavier, s’annonce « liste déroulante ».
Refaire un <select> custom en <div> — ce que font tant de bibliothèques — oblige à réimplémenter tout ce clavier (flèches, Home/End, recherche par frappe, Échap) à la main. Un immense chantier pour ré-obtenir le natif. On traite les formulaires en profondeur (labels, erreurs, autocomplete, validation) en Partie 9 ; retiens ici la même règle : le contrôle natif d’abord.
<!-- ✅ Contrôles natifs : clavier, rôle et états gratuits -->
<label>
<input type="checkbox" name="newsletter" />
Recevoir la newsletter
</label>
<label for="niveau">Niveau</label>
<select id="niveau" name="niveau">
<option>Débutant</option>
<option>Intermédiaire</option>
<option>Avancé</option>
</select>🧭 Sur A11yLearn — Sur A11yLearn, la carte de cours mélangeait tout : la carte entière était une
<div onclick>de navigation, et le « S’inscrire » à l’intérieur un<span onclick>— un piège au clavier et à la souris (cliquer « S’inscrire » déclenchait aussi la navigation de la carte). On tranche par l’intention : le titre devient un<a href="/cours/1">(navigation), « S’inscrire » un<button>(action), et on retire l’onclickde la carte. Plus de conflit, tout est atteignable au clavier, et le lecteur d’écran annonce clairement « lien » d’un côté, « bouton » de l’autre.
📚 Aller plus loin — Le bon rôle annoncé pour chaque contrôle relève du critère 4.1.2 (Nom, rôle et valeur). Le pattern des faux boutons/liens est si répandu que l’APG (ARIA Authoring Practices Guide) rappelle en préambule de préférer
<button>et<a>natifs. Pour tout ce qui touche le focus, l’ordre de tabulation et les pièges clavier, la suite est en Partie 4.
✏️ Exercices
Exercice 1 — Bouton ou lien ? Pour chacun, dis si c’est un <button> ou un <a href> : (a) « Télécharger le PDF » ; (b) « Supprimer ce commentaire » ; (c) « Page suivante » (autre URL) ; (d) « Ouvrir le menu des filtres » (panneau dans la même page).
✅ Solution
(a) <a href> — mène vers un fichier (une ressource à une adresse). (b) <button> — action destructrice dans la page. (c) <a href> — navigation vers une autre URL. (d) <button> — ouvre/ferme un panneau sur place, aucune URL. Le critère : y a-t-il une adresse où l’on va (lien), ou une action ici et maintenant (bouton) ?
Exercice 2 — Corrige le faux bouton. Rends ce contrôle accessible sans changer le comportement (une action dans la page).
<div class="btn-primary" onclick="publierCommentaire()">Publier</div>✅ Solution
C’est une action → <button>. On garde la classe pour le style et on retire le bricolage clavier devenu inutile :
<button type="button" class="btn-primary" onclick="publierCommentaire()">
Publier
</button>On obtient gratuitement le focus, l’activation Entrée/Espace, le rôle « bouton » et la gestion de disabled.
🧠 Quiz de révision
1. Quelle est la différence fondamentale entre <button> et <a href> ?
<button> et <a href> ?<a href> navigue (change d’URL, mène ailleurs, favorisable, ouvrable dans un onglet). <button> agit dans la page courante (soumettre, ouvrir une modale, basculer un état). Le test : y a-t-il une adresse où aller (lien) ou une action locale (bouton) ?
2. Pourquoi <a href="#" onclick="…"> est-il un anti-pattern ?
<a href="#" onclick="…"> est-il un anti-pattern ?Parce que c’est un lien qui ne mène nulle part détourné pour agir. Le lecteur d’écran annonce « lien » (l’utilisateur attend une navigation), et le # fait souvent sauter la page en haut. Si c’est une action, il faut un <button>.
3. Comment s’active un <button> au clavier, comparé à un lien ?
<button> au clavier, comparé à un lien ?Un <button> s’active avec Entrée et Espace ; un <a href> seulement avec Entrée. Ces comportements sont natifs. Une <div> cliquable n’en offre aucun : ni focus, ni activation clavier.
4. Cite deux choses qui manquent à une <div onclick>.
<div onclick>.Parmi : le focus (elle n’est pas dans l’ordre de tabulation), l’activation clavier (Entrée/Espace), le rôle annoncé (« bouton »/« lien »), et la gestion des états (disabled, etc.). Elle n’est cliquable qu’à la souris.
5. Qu’apporte un contrôle de formulaire natif comme <select> ?
<select> ?Son rôle (« liste déroulante »), son comportement clavier complet (ouverture, flèches, Home/End, recherche par frappe, Échap) et ses états, gratuitement. Le refaire en <div> oblige à réimplémenter tout ce clavier à la main. On détaille en Partie 9.
Chapitre suivant : Tableaux de données — <caption>, <th scope>, et la distinction cruciale entre tableau de données et tableau de mise en page.