- Les listes HTML doivent être choisies en fonction de leur sens et de leur mode d’interaction, plutôt que de leur apparence visuelle, et se divisent en listes de contrôle, ordonnées, descriptives, de menu et non ordonnées
- Pour des choix fixes, il faut utiliser
<select>/<option>, tandis que <datalist> convient à la saisie avec suggestions ; il faut aussi distinguer le comportement de multiple, optgroup, size et value
<ol> s’utilise pour les procédures, événements et continuums dont le sens change si l’on modifie l’ordre, et reversed n’inverse que la numérotation sans changer l’ordre réel des éléments
<dl> s’est étendu en HTML5 au-delà de la liste de définitions pour devenir une liste descriptive, adaptée aux paires clé-valeur comme les termes-valeurs, les métadonnées ou le débogage JSON
<menu> sert aux listes de commandes comme des boutons d’outils ; son sens et son contenu autorisé diffèrent de nav, tandis que les autres listes générales relèvent de <ul>
Listes de contrôle : <select>/<option> et <datalist>
- Il est aussi possible de construire des listes interactives dans un formulaire
-
Les choix fixes avec <select> et <option>
- Si l’utilisateur doit uniquement pouvoir choisir parmi les éléments de la liste, on utilise
<select> et <option>
- L’exemple de liste de langues place des
<option> comme Select a Language, English, French, Spanish, Portuguese dans <select name="languages">
- Un
<select> par défaut ne permet de choisir qu’une seule option
- Pour autoriser la sélection de plusieurs éléments, il faut ajouter l’attribut
multiple
- Avec
multiple, l’affichage de la liste change et toutes les options sont visibles ; l’utilisateur peut sélectionner plusieurs éléments avec Shift ou Cmd + clic
- Avec de vrais éléments
select et option, la sémantique native du navigateur gère cela sans qu’il soit nécessaire d’ajouter manuellement aria-multiselectable à un élément de liste avec role="listbox"
-
Regrouper des options liées avec <optgroup>
- Si l’on veut regrouper les langues par familles linguistiques, on groupe la liste d’options avec
<optgroup>
- L’exemple crée des
<optgroup> avec le label Germanic, Romance, Celtic et y place English, French, Spanish, Portuguese, Irish, Welsh
- Pour empêcher la sélection d’un sous-groupe donné, il faut ajouter l’attribut
disabled à l’<optgroup> concerné
- Dans l’exemple, le groupe
Celtic reçoit disabled, ce qui désactive le groupe contenant Irish et Welsh
-
Améliorer avec les fonctionnalités natives de HTML
- S’il faut une séparation visuelle entre les groupes, on peut utiliser
<hr> autorisé dans <select>
- L’attribut
size contrôle le nombre d’éléments affichés en même temps, ce qui est utile pour les longues listes
- Quand on combine
size et optgroup, les libellés de groupe occupent eux aussi de l’espace d’affichage
- L’exemple ajoute à
<select name="languages" size="4" multiple> les groupes Germanic, Romance, Celtic, Afroasiatic, des <hr /> entre eux, ainsi que Hebrew et Arabic
Listes de suggestions : <datalist>
- Si l’utilisateur ne doit pas obligatoirement choisir uniquement dans la liste mais qu’on veut suggérer des éléments, on utilise
<datalist>
<datalist> se relie en deux étapes
- on crée un
<datalist> et on lui attribue un id
- on met ensuite cette valeur d’
id dans l’attribut list du <input> correspondant
- Dans l’exemple de suggestion de langues,
<datalist id="languages"> contient les options English, French, Spanish, Portuguese, Irish, Welsh, Hebrew, Arabic, puis est relié au champ de saisie via <input name="language" list="languages">
-
Fonctionnement de <option value>
- La valeur par défaut de
<option> est le texte qu’il contient et, s’il existe, l’attribut value remplace cette valeur par défaut, tandis que le texte agit comme un libellé
- Dans
<select>, cela pose peu de problème puisque l’utilisateur ne voit que le texte, mais dans <datalist>, l’utilisateur peut voir le libellé, le choisir, puis voir value inséré dans le champ, ce qui peut être déroutant
- Dans l’exemple, si l’on choisit
<option value="cy">Welsh</option>, l’utilisateur voit Welsh mais cy est inséré dans le champ
- Avec
<datalist>, il faut partir du principe que la valeur insérée n’est pas le libellé mais bien value
-
Combinaison avec plusieurs types d’entrée
<datalist> ne sert pas seulement aux options textuelles ; il peut aussi être utilisé avec d’autres types d’input
- L’exemple de sélection par semaine relie
<datalist id="preferred-weeks"> à <input type="week" name="week" id="camp-week" min="2026-W2" max="2026-W51" list="preferred-weeks" />
- Les semaines suggérées sont
2026-W22, 2026-W23, 2026-W24, 2026-W25
-
Combinaison avec <input type="range">
<datalist> n’est pas limité aux chaînes de caractères et fonctionne aussi avec des valeurs numériques ; il peut donc être combiné à un champ range pour créer des points étiquetés sur une plage
- L’exemple de pourcentage de pourboire relie
<datalist id="recommended-tips"> à <input type="range" name="tips" id="tips" min="0" max="50" step="1" list="recommended-tips" /> et ajoute les libellés 10%, 18%, 30%, 45%
- Dans les navigateurs de type Chrome,
@supports (x: attr(x type(percentage))) permet de lire la valeur label via attr(), de déclarer la valeur en pourcentage via type(), puis de positionner les options en position: absolute
- L’approche pour Firefox utilise
@supports not (x: attr(x type(percentage))), et la valeur est affichée avec ::before
- Cette méthode ne garantit pas que tous les navigateurs se comportent de la même manière ni qu’ils affichent la même chose à l’écran
Listes ordonnées : <ol>
- Pour un ensemble d’éléments qui doivent être lus dans un ordre précis, on utilise
<ol>
- Le critère n’est pas de savoir si un numéro doit apparaître à côté des éléments, mais si le sens de la liste change quand on modifie leur ordre
- Les collections adaptées à
<ol> sont les algorithmes, les suites d’événements, les éléments sur un continuum croissant ou décroissant, les recettes et les listes alphabétiques
- L’exemple de recette de banana bread exprime avec
<ol> l’ordre suivant : préchauffer le four et graisser le moule, mélanger les ingrédients, verser la pâte, cuire 60 minutes ou jusqu’à ce qu’un cure-dent ressorte propre, puis laisser refroidir sur une grille
- Une liste alphabétique d’ingrédients relève aussi de
<ol>, puisqu’elle suit le continuum de l’alphabet, avec l’ordre baking soda, bananas, brown sugar, butter, eggs, flour, salt
-
Imbrication de listes ordonnées et non ordonnées
- Une liste ordonnée bien structurée doit permettre de comprendre l’ordre des actions même sans voir le rendu du navigateur
- L’exemple de recette utilise
<ol> pour les étapes principales ; à l’intérieur d’une étape, les éléments dont l’ordre n’a pas d’importance sont placés dans <ul>, et les sous-étapes où l’ordre compte à nouveau dans <ol>
- La structure conserve l’ordre principal de
Prepare, Mix, Pour, Bake, Cool, tandis que les éléments parallèles dans Prepare et Bake sont représentés avec <ul>, et les procédures internes de Mix et Cool avec <ol>
-
reversed
- L’attribut
reversed fait passer la numérotation d’un ordre croissant à un ordre décroissant
- Il ne modifie pas l’ordre réel des éléments de la liste
- Il peut s’utiliser pour une liste d’ingrédients et de quantités présentée du plus au moins, comme
most to least
- L’exemple met dans
<ol reversed> les éléments eggs (2), flour (2 cups), bananas (2) (mashed), brown sugar (¾ cup), butter (½ cup), baking soda (1 teaspoon), salt (¼ teaspoon)
-
Inverser réellement l’ordre des éléments avec JavaScript
- Pour réellement inverser la liste, on peut réorganiser les enfants
li dans l’ordre inverse avec JavaScript, puis basculer l’attribut reversed
- La fonction d’exemple transforme le résultat de
list.querySelectorAll('li') en tableau, applique .reverse(), vide la liste avec list.innerHTML = '', puis réattache les éléments avec list.append(...children)
- Elle appelle ensuite
list.toggleAttribute('reversed')
- L’événement d’exemple déclenche l’inversion au double-clic via
orderedList.addEventListener('dblclick', (evt) => { reverseList(orderedList) })
-
start
- L’attribut
start sert à conserver la continuité de la numérotation lorsqu’on découpe une très grande liste en plusieurs listes ordonnées au lieu d’une seule
- Dans l’exemple de recette de banana bread,
Prepare reste en ul, Mix utilise <ol start=2>, Pour utilise <ol start=5>, et Cool utilise <ol start=7> afin de préserver la continuité des numéros d’étapes
- Même s’il existe au milieu une section exprimée en liste non ordonnée comme
6: Bake, la liste ol de Cool peut reprendre à start=7 pour maintenir le fil de numérotation de l’ensemble de la procédure
Listes descriptives : <dl>, <dt>, <dd>
- Une liste descriptive (description list) est un type de liste qui évite de forcer tout contenu dans
ol ou ul
-
Les listes de définition en HTML 4
- En HTML 4, on parlait non pas de
description list mais de liste de définitions (definition list), pensée pour un usage plus étroit consistant à fournir des définitions
- La structure se composait de
<dt> pour le terme à définir et de <dd> pour la définition, et pour une utilisation sémantiquement exacte, le terme défini était enveloppé dans <dfn>
- L’exemple relie
throw et yeet à une même définition, et associe séparément une définition à no cap et bet
<dl>
<dt><dfn>throw</dfn></dt>
<dt><dfn>yeet</dfn></dt>
<dd>Verb. To discard at a high velocity</dd>
<dt><dfn>no cap</dfn></dt>
<dd>Interjection. Expresses authenticity and truthfulness, sometimes surprise.</dd>
<dt><dfn>bet</dfn></dt>
<dd>Interjection. Expresses agreement and affirmation.</dd>
</dl>
-
Un sens élargi en HTML5
- En HTML5, cela devient une liste descriptive qui ne se limite plus aux définitions et peut être utilisée dès qu’il existe un « ensemble de termes et de valeurs »
- HTML5 autorise un wrapper non sémantique
<div> pour regrouper des <dt> et <dd> liés entre eux
- Dans l’exemple sur les moteurs de navigateur,
Chrome, Opera, Brave, Edge sont regroupés sous Blink-based browsers, tandis que Firefox, Tor, Librewolf le sont sous Gecko-based browsers
<dl>
<div class="dl-item">
<dt>Chrome</dt>
<dt>Opera</dt>
<dt>Brave</dt>
<dt>Edge</dt>
<dd>Blink-based browsers</dd>
</div>
<div class="dl-item">
<dt>Firefox</dt>
<dt>Tor</dt>
<dt>Librewolf</dt>
<dd>Gecko-based browsers</dd>
</div>
</dl>
-
Métadonnées et débogage JSON
- S’il s’agit d’une suite de faits accompagnés de leurs libellés, la liste descriptive convient bien à l’affichage de métadonnées
- Un profil utilisateur peut aussi être représenté avec
<dl> ; l’exemple exprime First Frank, Last Taylor, Age 44, Job Writer, Handle Paceaux sous forme de paires <dt> et <dd>
- Une liste descriptive peut aussi servir au débogage JSON dans une application monopage
- L’exemple
DebugJson parcourt chaque paire key, value d’un objet via Object.entries(obj) et affiche la clé dans <dt><var>...</var></dt> et la valeur dans <dd><code>...</code></dd>
- Si la valeur est un objet et non un tableau,
DebugJson.createDl(value) est rappelé pour créer un <dl> imbriqué ; sinon, value.toString() est inséré dans <code>
const debugJson = new DebugJson({foo: 'bar', arr: ['a', 'b'], car: 1}, '.container')
debugJson.render();
- Les listes descriptives conviennent très largement aux besoins en paires clé-valeur
Menus : <menu>
- L’élément
menu représente une liste de commandes et se rapproche davantage d’un web interactif que du simple rendu de contenu
menu convient à une liste de boutons d’outils, comme les contrôles de mise en forme d’un éditeur de texte enrichi
- L’exemple d’éditeur de texte enrichi place les boutons
Strong, Emphasize, Strike dans des li à l’intérieur d’un menu
<menu>
<li><button onclick="strong()">Strong</button></li>
<li><button onclick="emphasize()">Emphasize</button></li>
<li><button onclick="strike()">Strike</button></li>
</menu>
- D’après la spécification HTML, cela correspond à un usage de toolbar, et dans une page interactive, les groupes de boutons d’outils ont de fortes chances d’être des
menu
- L’exemple de contrôles vidéo relève aussi de
menu, avec des button portant commandfor="vid-123" et command="--play", --mute, --fullscreen
<div class="player player--video">
<video source="whatever.mp4" id="vid-123"></video>
<menu>
<li><button commandfor="vid-123" command="--play">Play</button></li>
<li><button commandfor="vid-123" command="--mute">Mute</button></li>
<li><button commandfor="vid-123" command="--fullscreen">Fullscreen</button></li>
</menu>
</div>
Listes non ordonnées : <ul>
ul est la liste fourre-tout pour tous les besoins de liste qui ne sont couverts ni par les autres types de listes ni par nav
- Dans les anciens HTML, la différence entre liste ordonnée et non ordonnée relevait surtout d’une distinction visuelle, comme des numéros contre des puces
- Aujourd’hui, avec l’accessibilité, les lecteurs d’écran et le référencement, il faut se concentrer moins sur l’affichage visuel que sur le fait de savoir si l’ordre porte du sens
- Une liste des membres d’un groupe de musique relève de
ul, car l’ordre n’a pas d’importance
<h3>Beatles</h3>
<ul>
<li>John Lennon</li>
<li>Paul McCartney</li>
<li>Ringo Star</li>
<li>George Harrison</li>
</ul>
- Une liste de noms de groupes peut elle aussi être exprimée en liste non ordonnée
<ul>
<li>Beatles</li>
<li>Rolling Stones</li>
<li>Van Halen</li>
<li>Foo Fighters</li>
</ul>
1 commentaires
Commentaires sur Hacker News
Même en testant seulement les exemples, on dirait que datalist ne fonctionne pas bien dans Mobile Safari
S’il y a des problèmes de compatibilité sur un marché aussi important, on peut presque dire qu’il n’existe en pratique que très peu de scénarios où cela vaut la peine de l’utiliser
C’est le genre d’information qui remet les pieds sur terre dont on n’a pas envie, mais dont on a besoin
Il y a plus de dix ans, j’ai travaillé sur un projet qui utilisait un widget de suggestion de saisie assez agressif dans l’UI, et à l’époque on passait par un plugin jQuery
C’était la partie la plus complexe du frontend, et en pratique la raison principale pour laquelle ce projet utilisait jQuery
En lisant l’article, je me suis dit que réimplémenter ce frontend avec une version minimale et légère en JS serait un jeu d’enfant, mais la réalité est différente dès lors qu’on ne livre pas son propre environnement tel quel sur l’appareil de l’utilisateur
Cela dit, les fonctionnalités incluses aujourd’hui dans la spécification HTML sont assez impressionnantes
Depuis que j’ai lu XHTML au lycée, je n’ai presque pas suivi l’évolution de la spec, donc il faudrait sans doute que je jette parfois un œil à ce qui a changé
Mais la compatibilité navigateur reste, aujourd’hui comme à l’époque, un vrai casse-tête
L’exemple de datalist fonctionne clairement sur mon iPhone
Il s’intègre à la zone de suggestions d’autocomplétion au-dessus du clavier iOS natif
En revanche, il n’y a aucun moyen de parcourir toutes les suggestions, mais ce n’est probablement pas non plus l’usage prévu de datalist
Par contre, l’attribut
disableddegroupne fonctionne clairement pasÇa ne fonctionne pas non plus sur Firefox pour Android
Il y a longtemps, dans mon premier poste, datalist dans Firefox ne fonctionnait pas, et c’est pour ça que Firefox avait été retiré de la liste des navigateurs pris en charge
C’est une fonctionnalité problématique depuis longtemps dès qu’on veut supporter autre chose que Chrome
Avec GBoard sur iOS, ça ne fonctionne pas bien
L’exemple qui ajoute l’attribut
disabledàoptgrouppour empêcher la sélection de certaines options semble cassé dans Mobile SafariEn pratique, ce n’est pas désactivé, et on peut toujours sélectionner les éléments désactivés
Cela devrait fonctionner dans les versions récentes de Safari, donc ce n’est pas vraiment cassé, plutôt dans un état étrange
https://caniuse.com/mdn-html_elements_optgroup_disabled
Ça ressemble à un bug de Safari
C’est exactement pour ce genre de raison qu’il est difficile d’utiliser du HTML natif au-delà du strict minimum
Même quand on a suffisamment lu et qu’on est assez sûr de soi pour écrire ce genre d’article, les commentaires finissent toujours par lister des comportements bizarres, des limitations et des absences de prise en charge selon les combinaisons navigateur/appareil
Le
divà outrance est peut-être un excès dans l’autre sens, mais au moins les comportements étranges et les limites sont assez cohérents et visiblesParce que cela s’imbrique de façon plus uniforme avec ce que j’ai écrit moi-même ou avec ce que le framework a généré
Article intéressant et complet
Malheureusement, aujourd’hui beaucoup de développeurs passent directement à React sans apprendre HTML, et avec les LLM il est probable qu’ils ne l’apprennent plus du tout
Du coup, même quand un simple HTML suffit, ils commencent par chercher un composant React
Je pense que ce n’est pas grave
Quand j’ai dû utiliser XML pour la première fois, il fallait apprendre la spécification XML et produire le résultat à la main
C’était avant l’existence de vraies bibliothèques de sérialisation
J’ai ensuite vu les générations suivantes utiliser XML puis JSON comme format d’échange sans vraiment les apprendre à fond, et cela ne posait pas de problème particulier
AJAX est passé du statut de technologie brûlante à celui d’acronyme dont plus personne ne connaît le sens, et aujourd’hui la plupart des gens ne reconnaissent même plus vraiment le terme
AJAX n’est pas mort, il est juste devenu si banal qu’on n’a plus besoin d’un mot distinct pour en parler
Mon problème, c’est que j’ai appris HTML à fond il y a vingt ans, puis je n’ai découvert ses évolutions et améliorations qu’au hasard, petit à petit
Pour le CSS, c’est encore plus vrai
Honnêtement, HTML est pénible
Par exemple, l’approche HTML pour styler une partie d’un contrôle consiste à utiliser des pseudo-classes, mais selon les navigateurs les sélecteurs peuvent être différents
On est alors obligé de tester navigateur par navigateur, puisqu’on ne peut pas savoir si cela fonctionne réellement correctement
React est non seulement plus simple, mais aussi plus fiable
Quand on le construit avec React et des
div, on peut s’attendre à ce que cela fonctionne pareil dans tous les navigateursBon contenu, mais il ne faut pas trop compter sur
datalistEn pratique, il manque de points d’intégration pour être vraiment utile, donc ce n’est pas adapté à grand-chose au-delà d’un petit prototype
Je me demande si un linter HTML aide vraiment à distinguer ce genre de différences
J’aimerais aussi savoir s’il existe un linter capable d’imposer ce type de choix de balises sémantiques
L’idée même d’imposer des balises sémantiques ne me parle pas trop
HTML est avant tout fait pour laisser de la liberté créative à l’auteur, et forcer une balise plutôt qu’une autre n’a pas vraiment de sens selon moi
L’accessibilité est importante, mais il y a déjà assez de contraintes comme ça
La chose la plus proche que je connaisse est https://github.com/kristoff-it/superhtml#diagnostics
SuperHTML vérifie non seulement la syntaxe, mais aussi l’imbrication des éléments et les valeurs d’attributs
Il n’existe pas d’autre serveur de langage qui implémente en code de validation l’ensemble de la spécification HTML
Je l’ai appris aujourd’hui
Je me demande pourquoi davantage de frameworks n’en tirent pas parti
Du point de vue de l’expérience utilisateur, c’est identique à une liste ordinaire
On peut l’utiliser si cela aide à comprendre le code, mais dans l’arbre d’accessibilité du navigateur et dans tous les autres aspects, cela reste simplement une liste non ordonnée
Pour indiquer qu’il s’agit d’une liste d’actions, il faut ajouter des attributs ARIA
L’article mentionne
role=menu, mais cela ne suffit pas à lui seul : chaque élément doit aussi avoir le rôlemenuitemLe WAI Authoring Practices Guide explique les rôles et les attentes en matière d’interaction, mais il ne faut pas copier les exemples de code tels quels, et il ne faut surtout pas utiliser ce rôle pour des menus de navigation
https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
Les gens intelligents n’apprennent pas le HTML compliqué et règlent le problème avec plusieurs
divLe HTML moderne a plein de fonctionnalités intéressantes
J’aime demander aux gens ce qu’ils pensent qu’un élément fait
Moi non plus, au début, je n’en devinais aucune correctement
Même après plusieurs années comme lead frontend, j’y ai trouvé beaucoup d’informations utiles que je ne connaissais pas
Je pense qu’on va clairement commencer à en essayer certaines dans l’entreprise aussi
Ce serait bien si les designers aimaient simplement l’apparence par défaut de datalist
Le billet de blog est vraiment très bien, mais je n’arrive pas à trouver un moyen de voir d’un coup la liste complète de tous les articles du blog
https://blog.frankmtaylor.com/archive ne marche pas
https://blog.frankmtaylor.com/archives non plus
https://blog.frankmtaylor.com/posts ne marche pas non plus
https://blog.frankmtaylor.com/all n’existe pas non plus
https://blog.frankmtaylor.com/blog non plus
Comme beaucoup de gens ont plus de 10�a0000 favoris, ce serait vraiment pratique d’avoir une page de liste unique qui énumère tout ce qui a été publié jusqu’ici, sans description ni texte complet
Tu veux sans doute parler d’un plan du site
La plupart des blogs ont un
sitemap.xmlqui liste tous les articlesEt je me demande aussi pourquoi tu voudrais parcourir les 235 articles en entier