Ahora se muestra IntersectionObserver

Los IntersectionObservers te permiten saber cuándo un elemento observado entra o sale del viewport del navegador.

Browser Support

  • Chrome: 51.
  • Edge: 15.
  • Firefox: 55.
  • Safari: 12.1.

Source

Supongamos que quieres hacer un seguimiento de cuándo un elemento de tu DOM ingresa al viewport visible. Es posible que quieras hacer esto para cargar imágenes de forma diferida justo a tiempo o porque necesitas saber si el usuario realmente está mirando un banner de anuncio determinado. Para ello, puedes conectar el evento de desplazamiento o usar un temporizador periódico y llamar a getBoundingClientRect() en ese elemento.

Sin embargo, este enfoque es extremadamente lento, ya que cada llamada a getBoundingClientRect() obliga al navegador a volver a diseñar toda la página y generará un retraso considerable en tu sitio web. Las cosas se vuelven casi imposibles cuando sabes que tu sitio se carga dentro de un iframe y quieres saber cuándo el usuario puede ver un elemento. El modelo de origen único y el navegador no te permitirán acceder a ningún dato de la página web que contiene el iframe. Este es un problema común para los anuncios, por ejemplo, que se cargan con frecuencia usando iframes.

IntersectionObserver se diseñó para que esta prueba de visibilidad sea más eficiente, y se incluye en todos los navegadores actualizados. IntersectionObserver te permite saber cuándo un elemento observado entra o sale del viewport del navegador.

Visibilidad del iframe

Cómo crear un IntersectionObserver

La API es bastante pequeña y se describe mejor con un ejemplo:

const io = new IntersectionObserver(entries => {
  console.log(entries);
}, {
  /* Using default options. Details below */
});

// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

Si usas las opciones predeterminadas para IntersectionObserver, se llamará a tu devolución de llamada cuando el elemento aparezca parcialmente en la vista y cuando desaparezca por completo del viewport.

Si necesitas observar varios elementos, es posible y recomendable hacerlo con la misma instancia de IntersectionObserver llamando a observe() varias veces.

Se pasa un parámetro entries a tu devolución de llamada, que es un array de objetos IntersectionObserverEntry. Cada uno de estos objetos contiene datos de intersección actualizados para uno de los elementos observados.

🔽[IntersectionObserverEntry]
    time: 3893.92
    🔽rootBounds: ClientRect
        bottom: 920
        height: 1024
        left: 0
        right: 1024
        top: 0
        width: 920
    🔽boundingClientRect: ClientRect
    // ...
    🔽intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
    🔽target: div#observee
    // ...

rootBounds es el resultado de llamar a getBoundingClientRect() en el elemento raíz, que es el viewport de forma predeterminada. boundingClientRect es el resultado de getBoundingClientRect() al que se llama en el elemento observado. intersectionRect es la intersección de estos dos rectángulos y te indica de manera eficaz qué parte del elemento observado es visible. intersectionRatio está estrechamente relacionado y te indica qué parte del elemento es visible. Con esta información a tu disposición, ahora puedes implementar funciones como la carga de recursos justo a tiempo antes de que se vuelvan visibles en la pantalla. Eficientemente

Es la proporción de intersección.

Los IntersectionObserver entregan sus datos de forma asíncrona, y el código de devolución de llamada se ejecutará en el subproceso principal. Además, la especificación indica que las implementaciones de IntersectionObserver deben usar requestIdleCallback(). Esto significa que la llamada a la devolución de llamada que proporcionaste tiene prioridad baja y el navegador la realizará durante el tiempo de inactividad. Esta es una decisión de diseño consciente.

Divs desplazables

No me gusta mucho desplazarme dentro de un elemento, pero no estoy aquí para juzgar, y IntersectionObserver tampoco. El objeto options toma una opción root que te permite definir una alternativa al viewport como tu raíz. Es importante tener en cuenta que root debe ser un elemento superior de todos los elementos observados.

Intersecta todo lo que quieras

¡No! ¡Mal desarrollador! Eso no es un uso consciente de los ciclos de CPU de tu usuario. Pensemos en un desplazador infinito como ejemplo: en ese caso, definitivamente se recomienda agregar centinelas al DOM y observarlos (y reciclarlos). Debes agregar un centinela cerca del último elemento del desplazador infinito. Cuando ese centinela aparece, puedes usar la devolución de llamada para cargar datos, crear los siguientes elementos, adjuntarlos al DOM y volver a colocar el centinela según corresponda. Si reciclas correctamente el centinela, no se necesita ninguna llamada adicional a observe(). El IntersectionObserver sigue funcionando.

Desplazamiento infinito

Más actualizaciones, por favor

Como se mencionó anteriormente, la devolución de llamada se activará una sola vez cuando el elemento observado aparezca parcialmente en la vista y otra vez cuando haya salido del viewport. De esta manera, IntersectionObserver te da una respuesta a la pregunta "¿Está el elemento X a la vista?". Sin embargo, en algunos casos de uso, es posible que eso no sea suficiente.

Aquí es donde entra en juego la opción threshold. Te permite definir un array de umbrales de intersectionRatio. Se llamará a tu devolución de llamada cada vez que intersectionRatio cruce uno de estos valores. El valor predeterminado de threshold es [0], lo que explica el comportamiento predeterminado. Si cambiamos threshold por [0, 0.25, 0.5, 0.75, 1], recibiremos una notificación cada vez que se haga visible un cuarto adicional del elemento:

Animación de umbral.

¿Hay otras opciones?

Por el momento, solo hay una opción adicional a las que se mencionaron anteriormente. rootMargin te permite especificar los márgenes de la raíz, lo que te permite aumentar o reducir el área que se usa para las intersecciones. Estos márgenes se especifican con una cadena de estilo CSS, como "10px 20px 30px 40px", que especifica los márgenes superior, derecho, inferior e izquierdo, respectivamente. En resumen, el struct de opciones IntersectionObserver ofrece las siguientes opciones:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document's viewport.
  root: null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a
  // percentage is an error.
  rootMargin: "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the
  // visible ratio of the observed element crosses a threshold in the list.
  threshold: [0],
});

<iframe> de magia

Los IntersectionObserver se diseñaron específicamente teniendo en cuenta los servicios de anuncios y los widgets de redes sociales, que suelen usar elementos <iframe> y podrían beneficiarse de saber si están a la vista. Si un <iframe> observa uno de sus elementos, tanto el desplazamiento del <iframe> como el de la ventana que contiene el <iframe> activarán la devolución de llamada en los momentos adecuados. Sin embargo, en el último caso, rootBounds se establecerá en null para evitar que se filtren datos entre orígenes.

¿De qué no se trata IntersectionObserver?

Ten en cuenta que IntersectionObserver no es, de forma intencional, perfecto en cuanto a píxeles ni de baja latencia. Si los usas para implementar tareas como animaciones que dependen del desplazamiento, fallarán, ya que los datos estarán, estrictamente hablando, desactualizados para cuando los uses. La explicación tiene más detalles sobre los casos de uso originales de IntersectionObserver.

¿Cuánto trabajo puedo realizar en la devolución de llamada?

Breve y conciso: Si pasas demasiado tiempo en la devolución de llamada, tu app se retrasará. Se aplican todas las prácticas habituales.

Avanza y cruza tus elementos

La compatibilidad del navegador con IntersectionObserver es buena, ya que está disponible en todos los navegadores modernos. Si es necesario, se puede usar un polyfill en navegadores más antiguos, que está disponible en el repositorio de WICG. Obviamente, no obtendrás los beneficios de rendimiento que te brindaría una implementación nativa con ese polyfill.

Puedes comenzar a usar IntersectionObserver ahora mismo. Cuéntanos qué se te ocurrió.