Atributos HTML que deberías conocer

Guía de atributos HTML avanzados y poco conocidos: defer, async, rel, hidden, inert, contenteditable, tabindex, translate, spellcheck y más.

HTML tiene atributos que van mucho más allá de class, id o href. Algunos afectan al rendimiento, otros a la accesibilidad y otros al comportamiento del navegador. Esta lección recorre los más útiles y desconocidos.


Scripts: defer y async

Cuando el navegador encuentra un <script> en el HTML, detiene el análisis del documento para descargarlo y ejecutarlo. Eso puede hacer que la página tarde más en mostrarse. defer y async evitan ese bloqueo.

<!-- Bloquea el análisis del HTML (comportamiento por defecto) -->
<script src="app.js"></script>

<!-- Se descarga en paralelo y se ejecuta al terminar el HTML -->
<script src="app.js" defer></script>

<!-- Se descarga en paralelo y se ejecuta en cuanto esté listo -->
<script src="app.js" async></script>

¿Cuándo usar cada uno?

AtributoDescargaEjecuciónUso recomendado
NingunoBloquea HTMLInmediataEvitar siempre que sea posible
deferEn paraleloAl terminar el HTML, en ordenScripts que necesitan el DOM completo
asyncEn paraleloEn cuanto termina la descargaScripts independientes (analytics, ads)

Regla práctica: usa defer para tus scripts de aplicación. Usa async solo para scripts de terceros que no dependen del DOM ni de otros scripts.


rel en enlaces y recursos

El atributo rel (relationship) describe la relación entre la página actual y el recurso enlazado. Es esencial para <link> y <a>.

<!-- Hoja de estilos -->
<link rel="stylesheet" href="estilos.css" />

<!-- Icono de la pestaña -->
<link rel="icon" href="/favicon.ico" />

<!-- Precargar un recurso crítico antes de que se necesite -->
<link rel="preload" href="fuente.woff2" as="font" crossorigin />

<!-- Preconectar al dominio de una API o CDN -->
<link rel="preconnect" href="https://fonts.googleapis.com" />

<!-- DNS prefetch: resolver el dominio antes de necesitarlo -->
<link rel="dns-prefetch" href="https://api.ejemplo.com" />

<!-- Feed RSS -->
<link rel="alternate" type="application/rss+xml" href="/rss.xml" />

En <a>

<!-- Enlace externo: no transmite autoridad SEO -->
<a href="https://externo.com" rel="nofollow">Enlace externo</a>

<!-- Abre enlace externo sin dar acceso a window.opener -->
<a href="https://externo.com" target="_blank" rel="noopener noreferrer">
  Enlace seguro
</a>

<!-- Patrocinado -->
<a href="https://sponsor.com" rel="sponsored">Patrocinador</a>

Seguridad: siempre usa rel="noopener noreferrer" en enlaces con target="_blank". Sin él, la página destino puede acceder al objeto window de tu página.


hidden: ocultar sin CSS

El atributo booleano hidden oculta un elemento completamente (equivalente a display: none):

<div id="mensaje" hidden>
  Este mensaje está oculto inicialmente.
</div>

<button onclick="document.getElementById('mensaje').hidden = false">
  Mostrar mensaje
</button>

A diferencia de una clase CSS, hidden es semántico: comunica que el contenido no es relevante en este momento, no solo que no es visible. Los lectores de pantalla lo respetan.

hidden="until-found"

Una variante especial: el elemento está oculto pero los motores de búsqueda y la búsqueda en página (Ctrl+F) pueden encontrarlo. Se revela automáticamente si el usuario busca texto en su interior:

<details>
  <summary>Información adicional</summary>
  <div hidden="until-found" id="extra">
    Este contenido aparecerá si el usuario lo busca con Ctrl+F.
  </div>
</details>

inert: bloquear toda interacción

inert desactiva completamente un elemento y todo su contenido: no recibe clics, no es navegable con teclado y los lectores de pantalla lo ignoran.

<div id="formulario" inert>
  <input type="text" placeholder="No se puede editar" />
  <button>Tampoco funciona</button>
</div>

Es ideal para:

  • Deshabilitar una sección mientras se carga contenido.
  • Garantizar que un modal bloquea el fondo de forma accesible.
  • Deshabilitar formularios temporalmente.
// Activar/desactivar con JavaScript
const formulario = document.getElementById('formulario');
formulario.inert = true;  // bloquear
formulario.inert = false; // desbloquear

contenteditable: editar contenido en el navegador

Hace que el contenido de un elemento sea editable directamente en el navegador:

<p contenteditable="true">
  Haz clic aquí para editar este texto directamente.
</p>

<!-- Edición de código con resaltado correcto -->
<pre contenteditable="true" spellcheck="false">
  const saludo = "Hola mundo";
</pre>

Valores:

  • contenteditable="true" o simplemente contenteditable → editable.
  • contenteditable="false" → no editable (por defecto).
  • contenteditable="plaintext-only" → editable pero solo acepta texto plano (sin HTML).

spellcheck: control del corrector ortográfico

<!-- Activo (por defecto en inputs y contenteditable) -->
<textarea spellcheck="true"></textarea>

<!-- Desactivado: ideal para código, contraseñas, campos técnicos -->
<input type="text" name="codigo" spellcheck="false" />
<pre contenteditable spellcheck="false">código aquí</pre>

tabindex: control del orden de foco

Controla si un elemento es focusable y en qué orden con la tecla Tab:

<!-- Excluir del orden de tabulación -->
<button tabindex="-1">No se puede enfocar con Tab</button>

<!-- Incluir en el orden natural del DOM (valor recomendado para elementos no interactivos) -->
<div role="button" tabindex="0">Div clickable accesible</div>

<!-- Orden explícito (desaconsejado, difícil de mantener) -->
<input tabindex="3" />
<input tabindex="1" />
<input tabindex="2" />

Regla práctica: usa tabindex="0" para hacer focusable un elemento no interactivo (div, span). Evita valores positivos porque rompen el orden natural de navegación.


translate: control de traducción automática

Indica a los traductores automáticos (como Google Translate) si deben traducir el contenido:

<!-- No traducir: código, nombres de marca, comandos -->
<code translate="no">npm install</code>

<span translate="no">GitHub Copilot</span>

<!-- Traducir (comportamiento por defecto) -->
<p translate="yes">Este texto sí se traducirá.</p>

autocomplete: control del autocompletado de formularios

<!-- Autocompletado estándar del navegador -->
<input type="email" autocomplete="email" />
<input type="tel"   autocomplete="tel" />
<input type="text"  autocomplete="name" />

<!-- Desactivar el autocompletado (para campos sensibles) -->
<input type="text" name="codigo_acceso" autocomplete="off" />

<!-- Contraseña nueva: evita que el navegador sugiera la actual -->
<input type="password" autocomplete="new-password" />

<!-- Contraseña actual -->
<input type="password" autocomplete="current-password" />

Los valores estándares de autocomplete permiten al navegador rellenar los formularios correctamente y mejoran la experiencia en móvil.


inputmode: teclado en móvil

Indica qué teclado mostrar en dispositivos móviles sin cambiar el tipo del input:

<!-- Teclado numérico para un campo de texto libre -->
<input type="text" inputmode="numeric" pattern="[0-9]*" />

<!-- Teclado con punto decimal para precios -->
<input type="text" inputmode="decimal" />

<!-- Teclado de teléfono (con +, * y #) -->
<input type="text" inputmode="tel" />

<!-- Teclado con @ y .com para emails -->
<input type="text" inputmode="email" />

<!-- Teclado optimizado para URLs -->
<input type="text" inputmode="url" />

loading en iframes

Igual que en imágenes, loading="lazy" también funciona en <iframe>:

<!-- El iframe no se carga hasta que esté cerca del viewport -->
<iframe
  src="https://www.youtube.com/embed/dQw4w9WgXcQ"
  loading="lazy"
  title="Vídeo incrustado"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media"
>
</iframe>

Resumen rápido

AtributoPara qué sirve
deferScript no bloqueante, ejecuta tras el HTML
asyncScript no bloqueante, ejecuta al descargar
rel="noopener noreferrer"Seguridad en enlaces con target="_blank"
rel="preload"Precarga un recurso crítico
rel="preconnect"Preconecta a un dominio externo
hiddenOculta semánticamente un elemento
hidden="until-found"Oculto pero indexable y buscable con Ctrl+F
inertBloquea toda interacción en el elemento y sus hijos
contenteditableHace editable el contenido en el navegador
spellcheck="false"Desactiva el corrector (ideal en campos de código)
tabindex="0"Hace focusable un elemento no interactivo
tabindex="-1"Excluye del orden de tabulación
translate="no"Impide la traducción automática del contenido
autocompleteControla el autocompletado del navegador
inputmodeDefine el teclado en móvil

¿Qué hemos aprendido?

  • defer es el comportamiento correcto para la mayoría de scripts.
  • rel="noopener noreferrer" es obligatorio en enlaces externos con target="_blank".
  • inert es la forma correcta de bloquear una sección completa (más potente que disabled).
  • inputmode mejora la experiencia en móvil sin cambiar la validación del campo.
  • translate="no" protege nombres de marca y código de los traductores automáticos.

Siguiente paso

En la última lección veremos SEO on-page con HTML: cómo escribir el <head> correcto, la importancia del H1 único, el canonical, las Open Graph tags y el structured data.