ResizeObserver te informa cuando cambia el tamaño de un elemento.
Antes de ResizeObserver, debías adjuntar un objeto de escucha al evento resize del documento para recibir notificaciones sobre cualquier cambio en las dimensiones de la ventana gráfica. En el controlador de eventos, deberías determinar qué elementos se vieron afectados por ese cambio y llamar a una rutina específica para reaccionar de manera adecuada. Si necesitabas las nuevas dimensiones de un elemento después de un cambio de tamaño, debías llamar a getBoundingClientRect() o getComputedStyle(), lo que puede causar un diseño inestable si no te encargas de agrupar todas tus lecturas y todas tus escrituras.
Esto ni siquiera abarcaba los casos en los que los elementos cambiaban de tamaño sin que se hubiera cambiado el tamaño de la ventana principal. Por ejemplo, agregar nuevos elementos secundarios, establecer el estilo display de un elemento en none o acciones similares pueden cambiar el tamaño de un elemento, sus elementos secundarios o sus elementos superiores.
Por eso, ResizeObserver es un elemento primitivo útil. Reacciona a los cambios de tamaño de cualquiera de los elementos observados, independientemente de la causa del cambio.
También proporciona acceso al nuevo tamaño de los elementos observados.
API
Todas las APIs con el sufijo Observer que mencionamos anteriormente comparten un diseño de API simple. ResizeObserver no es una excepción. Creas un objeto ResizeObserver y pasas una devolución de llamada al constructor. La devolución de llamada recibe un array de objetos ResizeObserverEntry (una entrada por cada elemento observado) que contiene las nuevas dimensiones del elemento.
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
Algunos detalles
¿Qué se denuncia?
En general, un objeto
ResizeObserverEntry
informa el cuadro de contenido de un elemento a través de una propiedad llamada
contentRect, que devuelve un objeto
DOMRectReadOnly. El cuadro de contenido es el cuadro en el que se puede colocar contenido. Es el cuadro de borde menos el padding.
Es importante tener en cuenta que, si bien ResizeObserver registra las dimensiones del contentRect y el padding, solo observa el contentRect.
No confundas contentRect con el cuadro delimitador del elemento. El cuadro de límite, según lo informa getBoundingClientRect(), es el que contiene todo el elemento y sus elementos secundarios. Los SVG son una excepción a la regla, ya que ResizeObserver informará las dimensiones del cuadro delimitador.
A partir de Chrome 84, ResizeObserverEntry tiene tres propiedades nuevas para proporcionar información más detallada. Cada una de estas propiedades devuelve un objeto ResizeObserverSize que contiene una propiedad blockSize y una propiedad inlineSize. Esta información se refiere al elemento observado en el momento en que se invoca la devolución de llamada.
borderBoxSizecontentBoxSizedevicePixelContentBoxSize
Todos estos elementos devuelven arrays de solo lectura porque, en el futuro, se espera que puedan admitir elementos que tengan varios fragmentos, lo que ocurre en situaciones de varias columnas. Por ahora, estos arrays solo contendrán un elemento.
La compatibilidad de la plataforma con estas propiedades es limitada, pero Firefox ya admite las dos primeras.
¿Cuándo se informa?
La especificación indica que ResizeObserver debe procesar todos los eventos de cambio de tamaño antes de la pintura y después del diseño. Esto hace que la devolución de llamada de un ResizeObserver sea el lugar ideal para realizar cambios en el diseño de tu página. Dado que el procesamiento de ResizeObserver se produce entre el diseño y la pintura, hacerlo solo invalidará el diseño, no la pintura.
Gotcha
Quizás te preguntes qué sucede si cambias el tamaño de un elemento observado dentro de la devolución de llamada a ResizeObserver. La respuesta es que activarás otra llamada a la devolución de llamada de inmediato. Afortunadamente, ResizeObserver tiene un mecanismo para evitar los bucles infinitos de devolución de llamada y las dependencias cíclicas. Los cambios solo se procesarán en el mismo fotograma si el elemento redimensionado está más abajo en el árbol del DOM que el elemento más superficial procesado en la devolución de llamada anterior.
De lo contrario, se diferirán para el siguiente fotograma.
Aplicación
Una de las cosas que te permite hacer ResizeObserver es implementar consultas de medios por elemento. Al observar elementos, puedes definir de forma imperativa los puntos de interrupción de tu diseño y cambiar los estilos de un elemento. En el siguiente ejemplo, el segundo cuadro cambiará el radio de su borde según su ancho.
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
Otro ejemplo interesante para observar es una ventana de chat. El problema que surge en un diseño de conversación típico de arriba hacia abajo es el posicionamiento del desplazamiento. Para evitar confundir al usuario, es útil que la ventana se mantenga en la parte inferior de la conversación, donde aparecen los mensajes más recientes. Además, cualquier tipo de cambio de diseño (por ejemplo, cuando un teléfono pasa de horizontal a vertical o viceversa) debería lograr el mismo resultado.
ResizeObserver te permite escribir un solo fragmento de código que se encarga de ambos casos. Cambiar el tamaño de la ventana es un evento que un ResizeObserver puede capturar por definición, pero llamar a appendChild() también cambia el tamaño de ese elemento (a menos que se establezca overflow: hidden), ya que necesita hacer espacio para los elementos nuevos. Teniendo esto en cuenta, se necesitan muy pocas líneas para lograr el efecto deseado:
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
Ingenioso, ¿no?
Desde aquí, podría agregar más código para controlar el caso en el que el usuario se desplazó hacia arriba de forma manual y quiere que el desplazamiento se mantenga en ese mensaje cuando llegue uno nuevo.
Otro caso de uso es para cualquier tipo de elemento personalizado que realice su propio diseño.
Hasta ResizeObserver, no había una forma confiable de recibir notificaciones cuando cambiaban sus dimensiones para que sus elementos secundarios se pudieran volver a diseñar.
Efectos en Interaction to Next Paint (INP)
La métrica Interaction to Next Paint (INP) mide la capacidad de respuesta general de una página a las interacciones del usuario. Si el INP de una página se encuentra en el umbral de "bueno", es decir, 200 milisegundos o menos, se puede decir que la página responde de manera confiable a las interacciones del usuario con ella.
Si bien la cantidad de tiempo que tardan en ejecutarse las devoluciones de llamada de eventos en respuesta a una interacción del usuario puede contribuir de manera significativa a la latencia total de una interacción, ese no es el único aspecto del INP que se debe tener en cuenta. El INP también tiene en cuenta la cantidad de tiempo que tarda en producirse la siguiente pintura de la interacción. Es la cantidad de tiempo que tarda en completarse el trabajo de renderización necesario para actualizar la interfaz de usuario en respuesta a una interacción.
En el caso de ResizeObserver, esto es importante porque la devolución de llamada que ejecuta una instancia de ResizerObserver se produce justo antes del trabajo de renderización. Esto es así a propósito, ya que el trabajo que se realiza en la devolución de llamada debe tenerse en cuenta, ya que el resultado de ese trabajo muy probablemente requerirá un cambio en la interfaz de usuario.
Ten cuidado de no realizar demasiado trabajo de renderización en una devolución de llamada de ResizeObserver, ya que el trabajo de renderización excesivo puede crear situaciones en las que el navegador se retrasa en la realización de trabajo importante. Por ejemplo, si alguna interacción tiene una devolución de llamada que hace que se ejecute una devolución de llamada ResizeObserver, asegúrate de hacer lo siguiente para facilitar la experiencia más fluida posible:
- Asegúrate de que tus selectores CSS sean lo más simples posible para evitar un trabajo excesivo de recálculo de estilos. Los recálculos de estilo se producen justo antes del diseño, y los selectores CSS complejos pueden retrasar las operaciones de diseño.
- Evita realizar cualquier trabajo en tu devolución de llamada
ResizeObserverque pueda activar reflujos forzados. - Por lo general, el tiempo necesario para actualizar el diseño de una página aumenta con la cantidad de elementos DOM que contiene. Si bien esto es cierto independientemente de si las páginas usan
ResizeObserver, el trabajo realizado en una devolución de llamada deResizeObserverpuede volverse significativo a medida que aumenta la complejidad estructural de una página.
Conclusión
ResizeObserver está disponible en todos los navegadores principales y proporciona una forma eficiente de supervisar los cambios de tamaño de los elementos a nivel del elemento. Solo ten cuidado de no retrasar demasiado la renderización con esta potente API.