Hoy · Hoja de ruta
- 1. Aprende: Qué rompe JavaScript cuando se usa sin cuidado, y cómo se evita.
- 2. Haz: Audita tu interfaz con el teclado y arregla lo que falle.
- 3. Comprueba: Todo lo que se puede hacer con ratón se puede hacer sin él.
Antes de empezar · 5 minutos, sin apuntes
- Recorre tu página entera con el tabulador. ¿Sabes siempre dónde estás?
- Cuando el catálogo se vuelve a pintar, ¿dónde queda el foco?
- ¿Qué pasa si alguien tiene activada la reducción de movimiento?
Lo que JavaScript rompe con facilidad
| Problema | Cómo se ve | Cómo se arregla |
|---|---|---|
| Controles falsos | Un contenedor genérico que hace de botón | Usar el elemento correcto |
| Foco perdido | Tras pintar, el foco vuelve al principio | Devolverlo a un punto con sentido |
| Cambios mudos | El contenido cambia y nadie lo anuncia | Una región activa |
| Foco invisible | El contorno se quitó en el CSS | :focus-visible de la UD2 |
| Trampa de foco | Un panel del que no se sale con el tabulador | Gestionar el foco al abrir y cerrar |
| Movimiento forzado | Animaciones para quien pidió que no las hubiera | Respetar la preferencia del sistema |
El foco después de pintar
function actualizar() {
const activo = document.activeElement?.dataset.js;
pintarCatalogo(aplicarFiltros(estado), elementos.catalogo);
if (activo) elementos[activo]?.focus();
}
Al sustituir el contenido de un contenedor, el elemento que tenía el foco deja de existir y el foco vuelve al documento. Para quien navega con teclado eso significa empezar de cero cada vez que escribe una letra.
El foco es la posición de quien no ve la pantalla
Cuando cambias contenido, pregúntate dónde debería quedar el foco: en el control que se usó, en el primer resultado nuevo, o en el mensaje de error que acaba de aparecer.
Un panel que se abre lleva el foco dentro; al cerrarse, lo devuelve al control que lo abrió. Es la regla que hace usable un diálogo sin ratón.
Respetar las preferencias
const sinMovimiento = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!sinMovimiento) elemento.classList.add("con-animacion");
La misma consulta que usaste en el CSS de la UD2, ahora desde el código. Las decisiones de la persona usuaria se respetan en las tres capas.
Rendimiento: las tres cosas que importan
- No trabajar de más. Un
debounceen lo que se dispara en ráfaga. - No tocar el DOM en bucle. Construir aparte, insertar una vez.
- No mezclar leer y escribir. Leer una medida obliga al navegador a recalcular; hacerlo dentro de un bucle que además escribe multiplica ese coste.
// Costoso: lee y escribe alternativamente
for (const tarjeta of tarjetas) {
tarjeta.style.setProperty("--alto", `${tarjeta.offsetHeight}px`);
}
// Mejor: primero se lee todo, después se escribe todo
const alturas = tarjetas.map((t) => t.offsetHeight);
tarjetas.forEach((t, i) => t.style.setProperty("--alto", `${alturas[i]}px`));
Con doscientas tarjetas la diferencia es perceptible; con dos mil, separa una interfaz fluida de una inutilizable.
Tarea 16 · Auditoría de accesibilidad
- Recorre toda la interfaz con el tabulador y anota cada punto donde te pierdes.
- Comprueba que ningún control es un contenedor genérico disfrazado.
- Arregla el foco tras cada render.
- Verifica que el resumen de resultados se anuncia.
- Comprueba el contraste y la visibilidad del foco.
- Mide con la pestaña Performance cuánto tarda un render de cien tarjetas.
Checkpoint · fin de la sesión 16
- Todo se puede usar sin ratón.
- El foco no se pierde al volver a pintar.
- Los cambios importantes se anuncian.
- Respetas la preferencia de movimiento reducido.