- Las listas de HTML deben elegirse por su significado y forma de interacción, no por su apariencia visual, y se dividen en listas de control, ordenadas, descriptivas, de menú y desordenadas
- Para opciones fijas, lo correcto es usar
<select>/<option>; para entradas con sugerencias, <datalist>, y hay que distinguir cómo funcionan multiple, optgroup, size y value
<ol> se usa para procedimientos, eventos o continuos cuyo significado cambia si se altera el orden, y reversed solo invierte la numeración, no el orden real de los elementos
<dl> en HTML5 se amplió más allá de las listas de definiciones y ahora sirve como lista descriptiva, adecuada para pares clave-valor, metadatos y depuración de JSON
<menu> se usa para listas de comandos como botones de herramientas; su significado y contenido permitido difieren de nav, y las demás listas generales quedan a cargo de <ul>
Listas de control: <select>/<option> y <datalist>
- También se pueden construir listas interactivas para el usuario dentro de formularios
-
Las opciones fijas van con <select> y <option>
- Si el usuario debe poder elegir solo entre los elementos de la lista, se usan
<select> y <option>
- El ejemplo de lista de idiomas coloca opciones como
Select a Language, English, French, Spanish, Portuguese dentro de <select name="languages">
- Un
<select> básico permite elegir exactamente una sola opción
- Para permitir seleccionar varios elementos, se agrega el atributo
multiple
- Al usar
multiple, cambia la presentación de la lista, se muestran todas las opciones y el usuario puede elegir varias con Shift o Cmd + clic
- Si se usan
select y option reales, la semántica nativa del navegador se encarga de todo, sin necesidad de agregar manualmente aria-multiselectable a un elemento de lista con role="listbox"
-
Agrupar opciones relacionadas con <optgroup>
- Si quieres agrupar idiomas por familia lingüística, puedes agrupar la lista de opciones con
<optgroup>
- El ejemplo crea
<optgroup> con label como Germanic, Romance, Celtic, y coloca dentro English, French, Spanish, Portuguese, Irish, Welsh
- Si quieres impedir la selección de un subgrupo específico, agrega el atributo
disabled a ese <optgroup>
- En el ejemplo, se agrega
disabled al grupo Celtic para desactivar el conjunto que incluye Irish y Welsh
-
Mejorar con funciones nativas de HTML
- Si necesitas una separación visual entre grupos, puedes usar
<hr> dentro de <select>, donde está permitido
- El atributo
size controla cuántos elementos se muestran a la vez, así que es útil para listas largas
- Si usas
size junto con optgroup, las etiquetas de grupo también ocupan espacio de visualización
- El ejemplo usa
<select name="languages" size="4" multiple> con grupos Germanic, Romance, Celtic, Afroasiatic, separadores <hr /> entre ellos, e incluye también Hebrew y Arabic
Lista de sugerencias: <datalist>
- Si no quieres que el usuario elija obligatoriamente solo de la lista, sino que esta sugiera opciones, se usa
<datalist>
<datalist> se conecta en dos pasos
- Se crea un
<datalist> y se le asigna un id
- Ese valor de
id se coloca en el atributo list del <input> correspondiente
- El ejemplo de sugerencias de idiomas pone opciones como
English, French, Spanish, Portuguese, Irish, Welsh, Hebrew, Arabic dentro de <datalist id="languages">, y lo conecta al campo de entrada con <input name="language" list="languages">
-
Cómo funciona <option value>
- El valor por defecto de
<option> es el texto que envuelve, y si existe el atributo value, ese valor sobrescribe el predeterminado mientras el texto actúa como etiqueta
- En
<select>, esto no suele ser un gran problema porque el usuario solo ve el texto, pero en <datalist> puede causar confusión porque el usuario ve la etiqueta, la elige y luego en el campo aparece el value
- En el ejemplo, si se selecciona
<option value="cy">Welsh</option>, el usuario ve Welsh, pero en el campo se inserta cy
- Al usar
<datalist>, hay que asumir que el valor insertado no es la etiqueta, sino value
-
Combinación con varios tipos de entrada
<datalist> no sirve solo para opciones de texto; también puede usarse con otros tipos de input
- El ejemplo de selección por semana conecta
<datalist id="preferred-weeks"> con <input type="week" name="week" id="camp-week" min="2026-W2" max="2026-W51" list="preferred-weeks" />
- Las semanas sugeridas son
2026-W22, 2026-W23, 2026-W24, 2026-W25
-
Combinación con <input type="range">
<datalist> no se limita a valores de cadena; también funciona con números, por lo que puede combinarse con una entrada de rango para crear puntos etiquetados sobre el rango
- El ejemplo de porcentaje de propina conecta
<datalist id="recommended-tips"> con <input type="range" name="tips" id="tips" min="0" max="50" step="1" list="recommended-tips" /> y añade etiquetas 10%, 18%, 30%, 45%
- En navegadores basados en Chrome, se usa
@supports (x: attr(x type(percentage))) para leer el valor de label con attr(), declarar el valor como porcentaje con type() y luego posicionar las opciones con position: absolute
- El enfoque para Firefox usa
@supports not (x: attr(x type(percentage))), y el valor se muestra con ::before
- Este enfoque no garantiza que todos los navegadores funcionen igual ni que se muestre de forma idéntica en pantalla
Lista ordenada: <ol>
- Para un conjunto de elementos que deben leerse en un orden específico, se usa
<ol>
- El criterio no es si deben verse números al lado de los elementos, sino si el significado de la lista cambia cuando se altera el orden
- Las colecciones adecuadas para
<ol> incluyen algoritmos, secuencias de eventos, elementos sobre un continuo creciente o decreciente, recetas y listas en orden alfabético
- El ejemplo de receta de pan de plátano expresa con
<ol> la secuencia de precalentar el horno y engrasar el molde, mezclar ingredientes, verter la mezcla, hornear 60 minutos o hasta que un palillo salga limpio, y dejar enfriar sobre una rejilla
- Una lista de ingredientes en orden alfabético también corresponde a
<ol>, porque sigue el continuo del alfabeto, y se ordena como baking soda, bananas, brown sugar, butter, eggs, flour, salt
-
Anidar listas ordenadas y desordenadas
- Una lista ordenada bien estructurada debe permitir entender qué ocurre y en qué orden incluso sin ver cómo la renderiza el navegador
- En el ejemplo de la receta, se usa
<ol> para los pasos principales y, dentro de cada paso, <ul> para elementos cuyo orden no importa, y nuevamente <ol> para subpasos donde el orden sí importa
- La estructura mantiene el orden superior de
Prepare, Mix, Pour, Bake, Cool, mientras que los elementos paralelos dentro de Prepare y Bake se expresan con <ul>, y los procedimientos dentro de Mix y Cool con <ol>
-
reversed
- El atributo
reversed cambia la numeración de ascendente a descendente
- No cambia el orden real de los elementos de la lista
- Puede usarse en una lista de ingredientes y cantidades mostrada de
most to least, es decir, de mayor a menor
- El ejemplo coloca en
<ol reversed> los elementos eggs (2), flour (2 cups), bananas (2) (mashed), brown sugar (¾ cup), butter (½ cup), baking soda (1 teaspoon), salt (¼ teaspoon)
-
Invertir el orden real de los elementos con JavaScript
- Si quieres invertir realmente la lista, puedes reordenar en JavaScript los hijos
li en sentido inverso y alternar el atributo reversed
- La función de ejemplo convierte el resultado de
list.querySelectorAll('li') en un arreglo, aplica .reverse(), luego vacía con list.innerHTML = '' y vuelve a insertar con list.append(...children)
- Al final llama a
list.toggleAttribute('reversed')
- El evento de ejemplo ejecuta la inversión al hacer doble clic con
orderedList.addEventListener('dblclick', (evt) => { reverseList(orderedList) })
-
start
- El atributo
start sirve para mantener la continuidad de la numeración cuando se divide una sola lista enorme en varias listas ordenadas
- En el ejemplo de la receta de banana bread,
Prepare se deja como ul, Mix usa <ol start=2>, Pour usa <ol start=5>, y Cool usa <ol start=7> para conservar la secuencia de pasos entre listas
- Aunque haya una sección intermedia expresada como lista no ordenada, como
6: Bake, luego se puede iniciar el ol de Cool en start=7 para mantener el flujo numérico del procedimiento completo
Lista descriptiva: <dl>, <dt>, <dd>
- Una lista descriptiva (description list) es un tipo de lista que evita forzar todo el contenido dentro de
ol o ul
-
Lista de definiciones en HTML 4
- En HTML 4 no se llamaba
description list, sino lista de definiciones (definition list), y estaba orientada a un uso más limitado de proporcionar definiciones
- La estructura se componía del término a definir en
<dt> y el contenido de la definición en <dd>; para un uso semánticamente preciso, el término definido se envolvía en <dfn>
- El ejemplo conecta
throw y yeet con una misma definición, y asigna definiciones por separado a no cap y 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>
-
Significado ampliado en HTML5
- En HTML5 dejó de limitarse a definiciones y pasó a ser una lista descriptiva que puede usarse cuando hay un “conjunto de términos y valores”
- En HTML5 se permite un envoltorio no semántico
<div> para agrupar <dt> y <dd> relacionados
- En el ejemplo de motores de navegador,
Chrome, Opera, Brave, Edge se agrupan bajo Blink-based browsers, y Firefox, Tor, Librewolf bajo 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>
-
Metadatos y depuración de JSON
- Si tienes una secuencia de hechos y etiquetas, una lista descriptiva es adecuada para mostrar metadatos
- Un perfil de usuario también puede ir en
<dl>; el ejemplo expresa First Frank, Last Taylor, Age 44, Job Writer, Handle Paceaux como pares <dt> y <dd>
- En aplicaciones de una sola página también puede usarse una lista descriptiva para depuración de JSON
- El ejemplo
DebugJson recorre cada key, value del objeto con Object.entries(obj) y renderiza la clave como <dt><var>...</var></dt> y el valor como <dd><code>...</code></dd>
- Si el valor es un objeto y no un arreglo, vuelve a llamar a
DebugJson.createDl(value) para crear un <dl> anidado; en cualquier otro caso, coloca value.toString() dentro de <code>
const debugJson = new DebugJson({foo: 'bar', arr: ['a', 'b'], car: 1}, '.container')
debugJson.render();
- La lista descriptiva se adapta ampliamente al requisito de pares clave-valor
Menú: <menu>
- El elemento
menu representa una lista de comandos y está más cerca de la web interactiva que de la simple renderización de contenido
menu encaja para una lista de botones de “herramientas”, como los controles de edición de texto en un editor de texto enriquecido
- El ejemplo del editor de texto enriquecido coloca los botones
Strong, Emphasize, Strike como li dentro de 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>
- Según la especificación de HTML, esto corresponde al uso de una barra de herramientas (toolbar), y en una página interactiva, donde haya botones de herramientas, es muy probable que correspondan a
menu
- El ejemplo de controles de video también va en
menu, con button que usan commandfor="vid-123" y 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>
Lista desordenada: <ul>
ul es la lista comodín para las necesidades de listado que no resuelven otros tipos de lista ni nav
- En el HTML antiguo, la diferencia entre lista ordenada y desordenada se parecía más a una diferencia visual, como números frente a viñetas
- Hoy se consideran accesibilidad, lectores de pantalla y optimización para motores de búsqueda, así que hay que enfocarse menos en la apariencia visual y más en si el orden tiene significado
- La lista de integrantes de una banda no depende del orden, así que corresponde usar
ul
<h3>Beatles</h3>
<ul>
<li>John Lennon</li>
<li>Paul McCartney</li>
<li>Ringo Star</li>
<li>George Harrison</li>
</ul>
- Una lista de nombres de bandas también puede expresarse como lista desordenada
<ul>
<li>Beatles</li>
<li>Rolling Stones</li>
<li>Van Halen</li>
<li>Foo Fighters</li>
</ul>
1 comentarios
Comentarios de Hacker News
Con solo probar los ejemplos, parece que datalist no funciona bien en Mobile Safari
Si hay problemas de compatibilidad en un mercado tan grande, podría decirse que en la práctica casi no hay escenarios en los que valga la pena usarlo
Es el tipo de balde de agua fría de la realidad que no quería, pero sí necesitaba
Hace más de 10 años trabajé en un proyecto que usaba un widget de sugerencias de entrada bastante agresivo en la UI, y en ese entonces usamos un plugin de jQuery
Era la parte más compleja del frontend y, en la práctica, la razón principal por la que usábamos jQuery en ese proyecto
Al leer el artículo pensé que sería facilísimo volver a implementar ese frontend con una versión mínima y ligera en JS, pero la realidad no es así a menos que puedas entregarles a los usuarios exactamente tu mismo entorno en sus dispositivos
Aun así, las funciones que hoy están incluidas en la especificación de HTML son bastante impresionantes
Desde que leí XHTML en la preparatoria casi no he seguido los cambios de la especificación, así que de vez en cuando debería revisar qué ha cambiado
Pero la compatibilidad entre navegadores sigue siendo un dolor de cabeza, tanto antes como ahora
El ejemplo de datalist definitivamente funciona en mi iPhone
Se integra en el área de sugerencias de autocompletado encima del teclado nativo de iOS
Eso sí, no hay manera de recorrer todas las sugerencias, y probablemente tampoco era esa la forma prevista de usar datalist
Pero el atributo
disableddegroupdefinitivamente no funcionaTampoco funciona en Firefox para Android
Hace mucho tiempo, en mi primer trabajo, datalist en Firefox no funcionaba, y por eso Firefox quedó fuera de la lista de navegadores compatibles
Si quieres dar soporte a navegadores que no sean Chrome, esta función ha sido problemática desde hace mucho tiempo
Con GBoard en iOS no funciona bien
El ejemplo que agrega el atributo
disabledaoptgrouppara impedir seleccionar algunas opciones parece estar roto en Mobile SafariEn realidad no se desactiva y los elementos deshabilitados siguen pudiéndose seleccionar
Debería funcionar en Safari reciente, así que no diría que está roto, más bien está en un estado extraño
https://caniuse.com/mdn-html_elements_optgroup_disabled
Parece posible que sea un bug de Safari
Aquí se ve por qué es difícil usar HTML nativo para algo más que lo básico
Aunque leas bastante y sepas lo suficiente como para escribir un artículo así con confianza, en los comentarios siempre terminan apareciendo comportamientos raros, limitaciones y faltas de soporte según cada combinación de navegador y dispositivo
El exceso de
divpuede ser una elección que se fue demasiado al otro extremo, pero al menos sus rarezas y límites suelen ser bastante consistentes y visiblesPorque encajan de manera más uniforme con lo que yo mismo escribí o con lo que generó el framework
Es un artículo interesante y bastante completo
Por desgracia, hoy hay muchos desarrolladores que no aprenden HTML y entran directo a React, y ahora con los LLM es aún más probable que no lleguen a aprender HTML en absoluto
Por eso terminan buscando primero un componente de React incluso en lugares donde HTML simple sería suficiente
Aun así, me parece bien
Cuando tuve que usar XML por primera vez, había que aprender la especificación de XML y generarlo a mano
Era una época en la que prácticamente no existían librerías de serialización
Después vi a generaciones más jóvenes usar XML y luego JSON como formatos de intercambio sin aprenderlos por completo, y no pasó nada grave
AJAX también pasó de ser una tecnología nueva y emocionante a una etapa donde la gente ya ni sabía qué significaban sus siglas, y ahora la mayoría ni siquiera reconoce bien el término
AJAX no murió; simplemente se volvió tan común que ya no hacía falta una palabra aparte para describirlo
Mi problema es que aprendí HTML a fondo hace 20 años y desde entonces solo me he ido enterando por casualidad, poco a poco, de cómo ha cambiado y mejorado
Con CSS me pasa aún más
Si soy honesto, HTML es engorroso
Por ejemplo, el enfoque “a lo HTML” para estilizar partes de un control consiste en usar pseudoclases, pero a veces los selectores cambian según el navegador
Entonces tienes que probar por navegador porque no sabes si realmente va a funcionar bien
React no solo es más fácil, también es más confiable
Si lo construyes con React y
div, puedes esperar que se comporte igual en todos los navegadoresBuen contenido, pero no conviene esperar demasiado de
datalistEn realidad le faltan puntos de integración para que sea útil de verdad, así que no sirve para mucho más que prototipos pequeños
Me pregunto si un linter de HTML realmente ayuda a distinguir este tipo de diferencias
También me gustaría saber si existe algún linter que pueda hacer cumplir este tipo de elección de etiquetas semánticas
No me termina de convencer eso de “hacer cumplir” etiquetas semánticas
HTML está hecho, ante todo, para que quien lo escribe lo use de forma creativa, y no me parece que tenga sentido forzar unas etiquetas sobre otras
La accesibilidad es importante, pero ya hay suficientes restricciones
Lo más cercano que conozco es https://github.com/kristoff-it/superhtml#diagnostics
SuperHTML valida no solo la sintaxis, sino también el anidamiento de elementos y los valores de atributos
No hay otro language server que implemente en código de validación toda la especificación de HTML
Hoy aprendí algo nuevo
Me pregunto por qué más frameworks no aprovechan esto
Desde la perspectiva de la experiencia de usuario, es lo mismo que una lista común
Puedes usarlo si ayuda a entender el código, pero en el árbol de accesibilidad del navegador y en todo lo demás no deja de ser una lista desordenada
Si quieres comunicar que algo es una lista de acciones, tienes que agregar atributos ARIA
El artículo menciona
role=menu, pero eso solo no basta; cada elemento también necesita el rolmenuitemLa WAI Authoring Practices Guide explica los roles y las expectativas de interacción, pero no copies los ejemplos de código tal cual, y en particular no uses ese rol para menús de navegación
https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
La gente lista resuelve eso con varios
diven lugar de aprender HTML complicadoHoy en día HTML tiene funciones interesantes
Me gusta preguntarle a la gente qué creen que hace cierto elemento
Yo tampoco le atiné para nada la primera vez
Incluso después de haber trabajado varios años como líder de frontend, aquí había mucha información útil que no conocía
En la empresa seguramente empezaremos a probarlo de verdad
Ojalá a los diseñadores les gustara la apariencia predeterminada de datalist
El post del blog está muy bueno, pero no logro encontrar una forma de ver de una sola vez la lista de todos los artículos del blog
https://blog.frankmtaylor.com/archive no funciona
https://blog.frankmtaylor.com/archives tampoco
https://blog.frankmtaylor.com/posts no sirve
https://blog.frankmtaylor.com/all no existe
https://blog.frankmtaylor.com/blog tampoco
Como hay mucha gente con más de 10 mil marcadores, sería realmente práctico tener una única página de lista que enumere todo lo que ha escrito hasta ahora, sin explicaciones ni el texto completo de cada artículo
Creo que te refieres a un sitemap
La mayoría de los blogs tienen un
sitemap.xmlcon todos los artículos listadosY también me da curiosidad por qué querrías revisar los 235 posts completos de principio a fin