ResizeObserver: Es similar a document.onresize para los elementos.

ResizeObserver te permite saber cuando cambia el tamaño de un elemento.

Antes de ResizeObserver, tenías que adjuntar un objeto de escucha al evento resize del documento para recibir notificaciones sobre cualquier cambio en las dimensiones del viewport. En el controlador de eventos, tendrías que descubrir qué elementos se vieron afectados por ese cambio y llamar a una rutina específica para que reaccione de forma adecuada. Si necesitabas las dimensiones nuevas de un elemento después de cambiar el tamaño, tenías que llamar a getBoundingClientRect() o getComputedStyle(), lo que puede causar una hiperpaginación del diseño si no te encargas de agrupar en lotes todas las lecturas y todas las escrituras.

Esto ni siquiera cubrió casos en los que los elementos cambian su tamaño sin haber cambiado el tamaño de la ventana principal. Por ejemplo, agregar elementos secundarios nuevos, establecer el estilo de display de un elemento en none o realizar acciones similares pueden cambiar el tamaño de un elemento, sus elementos del mismo nivel o sus elementos principales.

Por eso ResizeObserver es una primitiva útil. Reacciona a los cambios de tamaño de cualquiera de los elementos observados, independientemente de lo que causó el cambio. También proporciona acceso al nuevo tamaño de los elementos observados.

Navegadores compatibles

  • 64
  • 79
  • 69
  • 13.1

Origen

API

Todas las APIs con el sufijo Observer que mencionamos antes 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 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 informa?

Por lo general, un objeto ResizeObserverEntry informa el cuadro de contenido de un elemento a través de una propiedad llamada contentRect, que muestra un objeto DOMRectReadOnly. El cuadro de contenido es el cuadro en el que se puede colocar el contenido. Es el cuadro de borde menos el padding.

Diagrama del modelo de caja de CSS.

Es importante tener en cuenta que, si bien ResizeObserver informa las dimensiones de contentRect y del padding, solo controla el contentRect. No confundas contentRect con el cuadro delimitador del elemento. El cuadro de límite, como lo informa getBoundingClientRect(), es el que contiene todo el elemento y sus elementos subordinados. Los SVG son una excepción a la regla, en la 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 muestra un objeto ResizeObserverSize que contiene una propiedad blockSize y una inlineSize. Esta información se refiere al elemento observado en el momento en que se invoca la devolución de llamada.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Todos estos elementos muestran arrays de solo lectura porque en el futuro se espera que puedan admitir elementos que tienen varios fragmentos, lo que sucede en situaciones de varias columnas. Por ahora, estos arrays solo contendrán un elemento.

La compatibilidad de la plataforma para estas propiedades es limitada, pero Firefox ya es compatible con las dos primeras.

¿Cuándo se informará?

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. De esta manera, la devolución de llamada de un objeto ResizeObserver es el lugar ideal para realizar cambios en el diseño de la página. Debido a que el procesamiento de ResizeObserver ocurre entre el diseño y la pintura, esto solo invalidará el diseño, no la pintura.

Entendido

Quizás te estés preguntando, ¿qué sucede si cambio 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 de devolución de llamada infinitos y las dependencias cíclicas. Los cambios solo se procesarán en el mismo marco si el elemento con el que se cambió el tamaño está más profundo en el árbol del DOM que el elemento superficial procesado en la devolución de llamada anterior. De lo contrario, se diferirán al siguiente fotograma.

Aplicación

Algo que ResizeObserver te permite hacer es implementar consultas de medios por elemento. Al observar los elementos, puedes definir de manera imperativa los puntos de interrupción de diseño y cambiar los estilos de un elemento. En el siguiente ejemplo, el segundo cuadro cambiará su radio del 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 es una ventana de chat. El problema que surge en un diseño típico de conversación de arriba a abajo es el posicionamiento de desplazamiento. Para evitar confundir al usuario, es útil que la ventana se quede en la parte inferior de la conversación, donde aparecen los mensajes más recientes. Además, cualquier tipo de cambio de diseño (piensa en un teléfono que pasa del modo horizontal al vertical, o viceversa) debería lograr lo mismo.

ResizeObserver te permite escribir un único fragmento de código que se ocupe de ambas situaciones. 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), porque necesita liberar espacio para los elementos nuevos. Con esto en mente, 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);

Estupendo, ¿no?

Desde aquí, podría agregar más código para manejar el caso en el que el usuario se desplazó hacia arriba de forma manual y quiera desplazarse para mirar ese mensaje cuando llega un mensaje nuevo.

Otro caso de uso es para cualquier tipo de elemento personalizado que realice su propio diseño. Hasta el ResizeObserver, no había una manera confiable de recibir notificaciones cuando sus dimensiones cambiaban para que se pudieran volver a diseñar sus elementos secundarios.

Efectos en la interacción con el siguiente procesamiento de imagen (INP)

Interaction to Next Paint (INP) es una métrica que mide la capacidad de respuesta general de una página ante las interacciones del usuario. Si el INP de una página está en el umbral “bueno”, es decir, 200 milisegundos o menos, se puede decir que una página responde de forma 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 de INP que se debe considerar. El INP también considera la cantidad de tiempo que tarda el siguiente procesamiento de imagen de la interacción. Esta es la cantidad de tiempo que lleva completar el trabajo de renderización necesario para actualizar la interfaz de usuario en respuesta a una interacción.

En lo que respecta a ResizeObserver, esto es importante, ya que la devolución de llamada que ejecuta una instancia de ResizerObserver se produce justo antes del trabajo de renderización. Se diseñó de este modo, ya que se debe tener en cuenta el trabajo que se realiza en la devolución de llamada, ya que es muy probable que requiera un cambio en la interfaz de usuario como resultado.

Asegúrate de realizar la menor cantidad de trabajo de renderización necesaria en una devolución de llamada de ResizeObserver, ya que el procesamiento excesivo puede crear situaciones en las que el navegador se retrasa para realizar tareas importantes. 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 los selectores CSS sean lo más simples posible para evitar un trabajo excesivo de recalcular el estilo. Los recálculos de diseño ocurren justo antes del diseño, y los selectores CSS complejos pueden retrasar las operaciones de diseño.
  • Evita realizar cualquier trabajo en la devolución de llamada ResizeObserver que pueda activar reprocesamientos forzados.
  • El tiempo necesario para actualizar el diseño de una página generalmente aumenta con la cantidad de elementos del DOM que tiene una página. Si bien esto se aplica independientemente de que las páginas usen o no ResizeObserver, el trabajo realizado en una devolución de llamada a ResizeObserver puede 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 manera eficiente de supervisar los cambios de tamaño de los elementos a nivel del elemento. Ten cuidado de no retrasar demasiado la renderización con esta potente API.