React Academy · Lección

Gestión del foco y trampas de teclado en modales

Mantenga el foco dentro de los modales abiertos por motivos de accesibilidad mediante useRef y escuchas de eventos.

Lección 3 de 413 pasos

Gestión del foco y trampas de teclado en modales es una lección gratuita de React Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de React Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de React Academy incluye 4 lecciones en total.

Bienvenida

En esta lección implementará una trampa de foco dentro de un modal para que los usuarios del teclado no puedan salir de un modal abierto al pulsar Tab, un requisito clave de accesibilidad (WCAG 2.4.3).

Por qué son necesarias las trampas de foco

Cuando se abre un modal, los usuarios del teclado solo deberían poder interactuar con su contenido. Sin una trampa de foco, al pulsar Tab el foco se desplaza a los elementos que están detrás de la superposición del modal; esos elementos están ocultos visualmente, pero siguen siendo accesibles.

¿Qué es una trampa de foco?

Una trampa de foco intercepta los eventos de teclado Tab y Shift+Tab y, cuando el foco está a punto de salir del modal, lo devuelve al primer o al último elemento enfocable dentro del modal.

Búsqueda de elementos enfocables

Use querySelectorAll con un selector que incluya todos los elementos interactivos. Filtre los elementos deshabilitados y ocultos.
const FOCUSABLE = [
  'a[href]', 'button:not([disabled])',
  'input:not([disabled])', 'select', 'textarea',
  '[tabindex]:not([tabindex="-1"])'
].join(',');

const focusable = Array.from(
  modalRef.current.querySelectorAll(FOCUSABLE)
);

Implementación de la trampa

Escuche el evento keydown en el contenedor del modal. Al pulsar Tab, si el foco está en el último elemento, muévalo al primero. Al pulsar Shift+Tab, si el foco está en el primero, muévalo al último.
function handleTab(e) {
  const focusable = /* array of focusable elements */;
  const first = focusable[0];
  const last = focusable[focusable.length - 1];
  if (e.key !== 'Tab') return;
  if (e.shiftKey) {
    if (document.activeElement === first) {
      e.preventDefault(); last.focus();
    }
  } else {
    if (document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  }
}

Movimiento del foco al abrir

Cuando se abra el modal, mueva el foco al primer elemento enfocable que haya dentro de él (o al propio contenedor del modal mediante `tabIndex=-1`). Esto evita que el foco permanezca en el botón que abrió el modal.
useEffect(() => {
  if (isOpen) {
    const first = modalRef.current?.querySelector(FOCUSABLE);
    first?.focus();
  }
}, [isOpen]);

Restauración del foco al cerrar

Cuando se cierre el modal, devuelva el foco al elemento que estaba activo antes de abrirlo. Guarde una ref a `document.activeElement` en el momento en que se abra el modal.
const previousFocus = useRef(null);

useEffect(() => {
  if (isOpen) {
    previousFocus.current = document.activeElement;
  } else {
    previousFocus.current?.focus();
  }
}, [isOpen]);

Uso de la biblioteca focus-trap-react

Para uso en producción, considere la biblioteca **focus-trap-react**. Gestiona todos los casos límite: shadow DOM, iframes, elementos enfocables agregados dinámicamente y desactivación mediante Escape.
import FocusTrap from 'focus-trap-react';

{isOpen && (
  <FocusTrap>
    <div role="dialog" aria-modal="true">
      {children}
    </div>
  </FocusTrap>
)}

Atributos ARIA para modales

Agregue `role="dialog"` y `aria-modal="true"` al contenedor del modal. Agregue `aria-labelledby` apuntando al encabezado del modal. Esto indica a los lectores de pantalla que el diálogo es un modal y permite anunciarlo correctamente.
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">Confirm Delete</h2>
  ...
</div>

Atributo inert para el fondo

El atributo HTML `inert` en un contenedor elimina todos sus elementos del orden de foco y de la accesibilidad de los lectores de pantalla. Establezca `document.getElementById('root').inert = true` mientras el modal esté abierto para bloquear el fondo de forma sólida.
useEffect(() => {
  const root = document.getElementById('root');
  if (isOpen) root.setAttribute('inert', '');
  else root.removeAttribute('inert');
}, [isOpen]);

Comprobación rápida

¿Qué debería ocurrir cuando un usuario del teclado pulsa Tab en el último elemento enfocable dentro de un modal con una trampa de foco?

Resumen

Ha implementado una trampa de foco: buscar elementos enfocables, interceptar Tab/Shift+Tab, mover el foco al abrir y restaurarlo al cerrar. Use focus-trap-react en producción y agregue role='dialog' y aria-modal para los lectores de pantalla.

A continuación

Siguiente lección: **Tooltips & Dropdown Menus with Portals**: renderizará tooltips y menús desplegables que escapan de contenedores con overflow-hidden mediante createPortal.
Gratis para empezar

Aprende React con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
88
Lecciones
324

Preguntas frecuentes

¿La lección «Gestión del foco y trampas de teclado en modales» es gratis?

Sí — el texto completo de «Gestión del foco y trampas de teclado en modales» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de React Academy, actualiza a CoddyKit PRO. El curso de React Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Gestión del foco y trampas de teclado en modales»?

Mantenga el foco dentro de los modales abiertos por motivos de accesibilidad mediante useRef y escuchas de eventos. Practicas React Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar React Academy?

No se requiere experiencia previa. React Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Gestión del foco y trampas de teclado en modales»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de React Academy?

Sí. Cada lección de React Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. ¿Qué son los React Portals?
  2. Construcción de un componente modal con Portals
  3. Gestión del foco y trampas de teclado en modales
  4. Tooltips y menús desplegables con Portals
← Volver a React Academy