El escenario es casi siempre el mismo, que es una tabla de datos dentro de un contenedor desplazable. Cada fila tiene un menú de acciones, un pequeño menú desplegable con algunas opciones, como Editar, Duplicar y Eliminar. Lo construyes, parece funcionar perfectamente de forma aislada, y luego alguien lo coloca dentro de ese div desplazable y todo se desmorona. He visto exactamente este error en tres bases de código diferentes: el contenedor, la pila y el marco, todos diferentes. El error, sin embargo, es totalmente idéntico. El menú desplegable se recorta en el borde del contenedor. O aparece detrás de un contenido que lógicamente debería estar debajo. O funciona bien hasta que el usuario se desplaza y luego se desplaza. Busca el índice z: 9999. A veces ayuda, pero otras veces no hace absolutamente nada. Esa inconsistencia es la primera pista de que algo más profundo está sucediendo. La razón por la que sigue regresando es que están involucrados tres sistemas de navegador separados, y la mayoría de los desarrolladores entienden cada uno por separado, pero nunca piensan en lo que sucede cuando los tres chocan: desbordamiento, contextos de apilamiento y bloques de contención.

Una vez que comprenda cómo interactúan los tres, los modos de falla dejarán de parecer aleatorios. De hecho, se vuelven predecibles. Las tres cosas que realmente causan esto Veamos cada uno de esos elementos en detalle. El problema del desbordamiento Cuando configura overflow: hide, overflow: scroll o overflow: auto en un elemento, el navegador recortará todo lo que se extienda más allá de sus límites, incluidos los descendientes en posición absoluta. .contenedor de desplazamiento { desbordamiento: automático; altura: 300 píxeles; /* Esto recortará el menú desplegable y punto */ }

.desplegable { posición: absoluta; /* No importa, todavía recortado por .scroll-container */ }

Eso me sorprendió la primera vez que lo encontré. Asumí la posición: absoluta permitiría que un elemento escapara del recorte de un contenedor. No es así. En la práctica, eso significa que un menú absolutamente posicionado puede ser cortado por cualquier ancestro que tenga un valor de desbordamiento no visible, incluso si ese ancestro no es el bloque que contiene el menú. El recorte y el posicionamiento son sistemas separados. Simplemente chocan de maneras que parecen completamente aleatorias hasta que entiendes ambas.

Aquí hay un ejemplo de React usando createPortal:

importar {createPortal} desde 'react-dom'; importar {useState, useEffect, useRef} desde 'reaccionar';

función desplegable ({ AnchorRef, isOpen, niños }) { const [posición, establecerPosición] = useState({ arriba: 0, izquierda: 0 });

utilizarEfecto(() => { si (isOpen && AnchorRef.current) { const rect = AnchorRef.current.getBoundingClientRect(); establecerPosición({ arriba: rect.bottom + ventana.scrollY, izquierda: rect.izquierda + ventana.scrollX, }); } }, [isOpen, AnchorRef]);

si (!isOpen) devuelve nulo;

devolver crearPortal(

{niños}
, documento.cuerpo ); }

Y, por supuesto, no podemos ignorar la accesibilidad. Los elementos fijos que aparecen sobre el contenido aún deben ser accesibles mediante el teclado. Si el orden de enfoque no se mueve naturalmente al menú desplegable fijo, deberá administrarlo mediante código. También vale la pena comprobar que no se coloca sobre otro contenido interactivo sin posibilidad de descartarlo. Ese te muerde en las pruebas de teclado. Posicionamiento del anclaje CSS: hacia dónde creo que se dirige esto El posicionamiento de anclaje CSS es la dirección que más me interesa en este momento. No estaba seguro de qué parte de la especificación era realmente utilizable cuando la miré por primera vez. Le permite declarar la relación entre un menú desplegable y su activador directamente en CSS, y el navegador maneja las coordenadas. .disparador { nombre-ancla: --my-trigger; }

.menú desplegable { posición: absoluta; ancla de posición: --mi-gatillo; arriba: ancla (abajo); izquierda: ancla (izquierda); posiciones-intentar-respaldos: flip-block, flip-inline; }

La propiedad position-try-fallbacks es lo que hace que valga la pena utilizarlo en lugar de un cálculo manual. El navegador prueba ubicaciones alternativas antes de darse por vencido, por lo que un menú desplegable en la parte inferior de la ventana gráfica se mueve automáticamente hacia arriba en lugar de cortarse. La compatibilidad con navegadores es sólida en los navegadores basados ​​en Chromium y está creciendo en Safari. Firefox necesita un polirelleno. El paquete @oddbird/css-anchor-positioning cubre las especificaciones principales. He llegado a casos extremos de diseño que requerían alternativas que no anticipé, así que trátelo como una mejora progresiva o combínelo con unaRespaldo de JavaScript para Firefox. En resumen, prometedor pero aún no universal. Pruebe en sus navegadores de destino. Y en lo que respecta a la accesibilidad, declarar una relación visual en CSS no le dice nada al árbol de accesibilidad. aria-controls, aria-expanded, aria-haspopup: esa parte todavía depende de ti. A veces la solución es simplemente mover el elemento Antes de alcanzar un portal o hacer cálculos de coordenadas, siempre hago una pregunta primero: ¿Este menú desplegable realmente necesita estar dentro del contenedor de desplazamiento? Si no es así, mover el marcado a un contenedor de nivel superior elimina el problema por completo, sin JavaScript ni cálculos de coordenadas. Esto no siempre es posible. Si el botón y el menú desplegable están encapsulados en el mismo componente, mover uno sin el otro significa repensar toda la API. Pero cuando puedes hacerlo, no hay nada que depurar. El problema simplemente no existe. Lo que el CSS moderno aún no resuelve CSS ha recorrido un largo camino aquí, pero todavía hay lugares en los que te decepciona. La posición: los problemas solucionados y transformados siguen ahí. Está en la especificación intencionalmente, lo que significa que no existe ninguna solución alternativa para CSS. Si está utilizando una biblioteca de animación que envuelve su diseño en un elemento transformado, volverá a necesitar portales o posicionamiento de anclaje. CSS Anchor Positioning es prometedor, pero nuevo. Como mencioné anteriormente, Firefox todavía necesita un polyfill en el momento en que escribo esto. He llegado a casos extremos de diseño que requerían alternativas que no anticipé. Si necesita un comportamiento consistente en todos los navegadores hoy en día, todavía está recurriendo a JavaScript para las partes difíciles. La adición por la que realmente cambié mi flujo de trabajo es la API HTML Popover, ahora disponible en todos los navegadores modernos. Los elementos con el atributo popover se muestran en la capa superior del navegador, encima de todo, sin necesidad de posicionamiento de JavaScript.

El manejo de escape, el descarte al hacer clic afuera y la semántica de accesibilidad sólida son gratuitos para elementos como información sobre herramientas, widgets de divulgación y superposiciones simples. Es la primera herramienta a la que recurro por ahora. Dicho esto, no resuelve el posicionamiento. Resuelve las capas. Aún necesitas el posicionamiento del ancla o JavaScript para alinear un popover con su disparador. La API Popover maneja las capas. El posicionamiento del anclaje se encarga de la colocación. Usados ​​en conjunto, cubren la mayor parte de lo que anteriormente haría una biblioteca. Una guía de decisiones para su situación Después de pasar por todo esto de la manera más difícil, así es como pienso ahora sobre la elección.

Utilice un portal. Lo usaría cuando el disparador se encuentre en lo profundo de contenedores de desplazamiento anidados. Utilicé este patrón para menús de acciones de tablas y lo combiné con restauración de enfoque y comprobaciones de accesibilidad. Es la opción más confiable, pero dedique tiempo al cableado adicional. Utilice posicionamiento fijo. Esto es para cuando está en JavaScript básico o en un marco liviano y puede verificar que ningún antepasado aplica transformaciones o filtros. Es fácil de configurar y de depurar, siempre que se cumpla esa restricción. Utilice CSS Anchor Positioning. Utilice esta opción cuando la compatibilidad de su navegador lo permita. Si se requiere compatibilidad con Firefox, emparéjelo con @oddbird polyfill. Aquí es hacia donde se dirige la plataforma y eventualmente se convertirá en su enfoque preferido. Reestructurar el DOM. Utilice esto cuando la arquitectura lo permita y desee una complejidad de tiempo de ejecución cero. Creo que probablemente sea la opción más subestimada. Combine patrones. Haga esto cuando desee que el posicionamiento del ancla sea su enfoque principal, junto con un respaldo de JavaScript para navegadores no compatibles. O un portal para la colocación de DOM combinado con getBoundingClientRect() para obtener precisión de coordenadas.

Conclusión Solía ​​tratar este error como un problema único, algo que parchear y superar. Pero una vez que me senté con él el tiempo suficiente para comprender los tres sistemas involucrados (recorte de desbordamiento, contextos de apilamiento y bloques de contención), dejó de parecer aleatorio. Podría mirar un menú desplegable roto e inmediatamente rastrear qué ancestro fue el responsable. Ese cambio en la forma en que leo el DOM fue lo que realmente me llevé. No existe una única respuesta correcta. Lo que busqué dependió de lo que podía controlar en el código base: portales cuando el árbol ancestral era impredecible; posicionamiento fijo cuando era limpio y sencillo; moviendo el elemento cuando nada me detenía; y posicionamiento del ancla ahora,donde puedo. Independientemente de lo que elija, no trate la accesibilidad como el último paso. En mi experiencia, ahí es exactamente cuando se omite. Las relaciones ARIA, la gestión del enfoque, el comportamiento del teclado, eso no es pulido. Son parte de lo que hace que la cosa realmente funcione. Consulte el código fuente completo en mi repositorio de GitHub. Lectura adicional Estas son las referencias a las que volvía constantemente mientras trabajaba en esto:

El contexto de apilamiento (MDN) “Guía de posicionamiento de anclajes CSS”, Juan Diego Rodríguez “Comenzando con la API Popover”, Godstime Aburu UI flotante (floating-ui.com) Desbordamiento de CSS (MDN)

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free