acerca de HTML

Referencia de etiquetas

Etiqueta menuitem en HTML: obsoleta y acciones actuales

La etiqueta <menuitem> está obsoleta y no debe usarse en HTML nuevo. Definía un comando que podía invocarse desde un menú emergente, incluida la propuesta de menús contextuales. Hoy utiliza controles nativos, como <button>, y programa la acción que corresponda. Esta guía explica su uso histórico y cómo migrarlo; el HTML Living Standard la incluye entre las funciones no conformes.

Sintaxis y contexto históricos

Este ejemplo antiguo sirve para reconocer la etiqueta. Se conserva como código de referencia y no debe ejecutarse como una demostración actual:

<menu type="context" id="acciones">
  <menuitem label="Guardar" type="command">
</menu>

Pretendía declarar el comando «Guardar» de un menú contextual. En HTML 5.1 de noviembre de 2016, <menuitem> era vacío: no tenía hijos ni etiqueta de cierre. Su padre debía ser un <menu> en estado de menú emergente. El texto se expresaba mediante label.

El bloque conserva la variante type="context" para reconocer código heredado; no coincide con el valor type="popup" definido en esa edición de HTML 5.1. Ninguno de esos valores de <menu> es válido en la autoría actual.

El fragmento no incluía una acción de guardado. La antigua asociación con el objeto de la página usaba contextmenu="acciones"; esa declaración también está obsoleta. Su interoperabilidad dependía de la implementación: no funcionaba en todos los navegadores.

<menu> sigue vigente: representa una lista de comandos, normalmente mediante hijos <li> que contienen controles. Admite contenido de flujo como contexto; no coloques una lista dentro de <p>. Sus hijos son <li> y elementos de apoyo a scripts, no botones directamente. Consulta su modelo de contenido actual.

role="menuitem" pertenece a WAI-ARIA. Es un rol para una opción de un widget de menú, no una etiqueta HTML ni un reemplazo que se añade a cualquier botón. Necesita el contexto accesible de un menu o menubar y la interacción correspondiente. Declarar el rol comunica semántica; no programa el teclado.

Una lista de botones nativos permite llegar a cada acción con Tab y activarla con Enter o Espacio. <menu> por sí solo no crea un menú desplegable ni navegación automática con flechas. Para dos acciones visibles, esta solución evita implementar un widget compuesto.

Atributos antiguos y sus reemplazos

Estos atributos específicos se documentan para interpretar código heredado, según la sección histórica de HTML 5.1 enlazada arriba:

Atributo antiguoFunción históricaDecisión actual
labelNombre; si aparecía, no podía estar vacío.Texto visible dentro del botón.
typecommand por defecto, checkbox o radio.Botón para acciones; casilla o radio para selección.
iconURL válida y no vacía de una imagen.Imagen o SVG dentro del control; conserva un nombre textual.
disabledBooleano: comando no disponible.disabled en un control nativo que lo admita.
checkedBooleano; solo para checkbox o radio.Estado del control de selección adecuado.
radiogroupNombre no vacío; solo para radios hermanos.Radios nativos con el mismo name.
defaultBooleano: comando predeterminado.Diseña explícitamente la acción principal.

disabled="false" conserva el atributo booleano y deshabilita un botón. Para habilitarlo, retira el atributo o asigna false a la propiedad DOM disabled, como en el ejemplo siguiente. En controles actuales de selección, el atributo checked fija el estado inicial; la propiedad DOM checked refleja el estado actual. Un botón de alternancia puede usar aria-pressed; no admite checked.

id, class, lang y title son atributos globales. El antiguo title ofrecía una pista, sin sustituir al nombre. display y gap son propiedades CSS; addEventListener() es un método de JavaScript para registrar manejadores. Copiar los atributos antiguos a un botón no reproduce su comportamiento.

Migración a dos acciones accesibles

Guarda este documento completo como acciones-notas.html en UTF-8 y ábrelo en el navegador. No requiere un servidor ni archivos adicionales. «Duplicar nota» añade una copia visible; «Restablecer» vuelve a una sola nota. Ambas acciones modifican únicamente esta página: no guardan archivos, no envían datos y se pierden al recargar.

<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Acciones de las notas</title>
    <style>
      body {
        max-width: 36rem;
        margin-inline: auto;
        padding: 1rem;
        font-family: sans-serif;
        line-height: 1.5;
      }
      #menuitem-ejemplo menu {
        display: flex;
        flex-wrap: wrap;
        gap: 0.75rem;
        padding: 0;
        list-style: none;
      }
      #menuitem-ejemplo button {
        padding: 0.6rem 0.8rem;
        font: inherit;
      }
    </style>
  </head>
  <body>
    <section id="menuitem-ejemplo">
      <h1>Acciones de las notas</h1>
      <ul data-notas>
        <li>Revisar el borrador.</li>
      </ul>
      <menu aria-label="Acciones de las notas">
        <li><button type="button" data-duplicar disabled>Duplicar nota</button></li>
        <li><button type="button" data-restablecer disabled>Restablecer</button></li>
      </menu>
      <p role="status" data-estado>Una nota disponible.</p>
      <noscript>
        <p>Las acciones necesitan JavaScript. Puedes leer la nota y copiar su texto manualmente.</p>
      </noscript>
    </section>
    <script>
      (() => {
        const ejemplo = document.getElementById("menuitem-ejemplo");
        const notas = ejemplo.querySelector("[data-notas]");
        const estado = ejemplo.querySelector("[data-estado]");
        const duplicar = ejemplo.querySelector("[data-duplicar]");
        const restablecer = ejemplo.querySelector("[data-restablecer]");
        const textoInicial = notas.firstElementChild.textContent;

        function crearNota() {
          const nota = document.createElement("li");
          nota.textContent = textoInicial;
          return nota;
        }

        duplicar.addEventListener("click", () => {
          notas.append(crearNota());
          estado.textContent = `Se añadió una copia. Hay ${notas.children.length} notas.`;
        });
        restablecer.addEventListener("click", () => {
          notas.replaceChildren(crearNota());
          estado.textContent = "Lista restablecida. Una nota disponible.";
        });
        duplicar.disabled = false;
        restablecer.disabled = false;
      })();
    </script>
  </body>
</html>

Prueba Tab hasta «Duplicar nota» y pulsa Enter: aparecerán dos notas y un mensaje. Pulsa Espacio para añadir otra; después, Tab y Enter en «Restablecer» recuperan la lista inicial. El foco permanece en el botón activado. role="status" expone la actualización a tecnologías de asistencia; el anuncio concreto depende de su combinación con el navegador.

Sin JavaScript, los botones permanecen deshabilitados y aparece la explicación de la alternativa manual. La nota sigue disponible. Retirar CSS cambia la disposición, sin eliminar controles ni contenido. Al migrar, cambia también los selectores que buscaban menuitem: renombrar la etiqueta o retirarla sin crear los controles y sus manejadores pierde la acción.

Menús contextuales y errores frecuentes

Un menú contextual personalizado exige más trabajo. Antes de interceptar contextmenu, proporciona una apertura por teclado, por ejemplo un botón visible, y aplica el patrón de menú de WAI-ARIA APG: mueve el foco al abrir, recorre las opciones con flechas, activa con Enter y cierra con Escape devolviendo el foco al origen. Tab debe salir del widget; los submenús requieren su propia gestión. Los estados de selección usan menuitemcheckbox o menuitemradio y aria-checked cuando corresponda.

No bloquees el menú del navegador sin una razón y una alternativa accesible. El ejemplo anterior mantiene sus acciones visibles y no intercepta ese evento. Evita asimismo añadir role="menu" a una lista que solo funciona con Tab, convertir texto en un supuesto botón con CSS o depender de un icono sin nombre comprensible.

Preguntas frecuentes y guías relacionadas

No. Sigue vigente como lista de comandos. Lo obsoleto es <menuitem> y el antiguo modelo declarativo de menús contextuales, incluidos type="context" y el atributo contextmenu.

¿Role menuitem es la misma etiqueta?

No. role="menuitem" expresa semántica ARIA para un widget con requisitos de foco y teclado. No convierte una etiqueta obsoleta en HTML conforme ni aporta interacción automáticamente.

Para ampliar la migración, consulta las guías de <menu>, que agrupa comandos; <li>, que estructura cada entrada; y <button>, que ofrece la activación nativa. <script> contiene aquí la lógica local; HTML por sí solo no duplica ni restablece la nota.

Compatibilidad y fuentes

Referencia revisada el 5 de octubre de 2026. Las notas oficiales de Firefox 85, publicado el 26 de enero de 2021, documentan que <menuitem> dejó de estar disponible por defecto y quedó detrás de una preferencia. Es evidencia de una implementación histórica, no soporte actual interoperable.

Contrasta el código heredado con HTML 5.1 y la autoría vigente con el Living Standard. Para widgets compuestos, consulta WAI-ARIA y su patrón APG, enlazados en las secciones anteriores. Que un navegador conserve algún comportamiento antiguo no elimina la obsolescencia.