Skip to Content

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 fait location.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émentFocus (Tab)Activation clavierRôle annoncé
<a href>OuiEntréelien
<button>OuiEntrée et Espacebouton

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 un onclick sur une <div> — le signal qu’il faut revenir à un élément natif. Un onclick sur 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’onclick de 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> ?

<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 ?

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 ?

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>.

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> ?

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.

Last updated on