IntersectionObserver's aparecendo

Os IntersectionObservers informam quando um elemento observado entra ou sai da janela de visualização do navegador.

Compatibilidade com navegadores

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

Origem

Digamos que você queira acompanhar quando um elemento no DOM entra na janela de visualização visível. Você pode fazer isso para carregar imagens de forma lazy-load no momento certo ou porque precisa saber se o usuário está realmente olhando um determinado banner de anúncio. Para fazer isso, conecte o evento de rolagem ou use um timer periódico e chame getBoundingClientRect() nesse elemento.

No entanto, essa abordagem é muito lenta, porque cada chamada para getBoundingClientRect() força o navegador a refazer o layout da página inteira e causa um grande atraso no site. As coisas ficam quase impossíveis quando você sabe que seu site está sendo carregado em um iframe e quer saber quando o usuário pode ver um elemento. O modelo de origem única e o navegador não vão permitir que você acesse nenhum dado da página da Web que contém o iframe. Esse é um problema comum para anúncios, por exemplo, que são carregados com frequência usando iframes.

O objetivo do IntersectionObserver é tornar esse teste de visibilidade mais eficiente, e ele foi lançado em todos os navegadores modernos. IntersectionObserver informa quando um elemento observado entra ou sai da viewport do navegador.

Visibilidade do iframe

Como criar um IntersectionObserver

A API é bastante pequena e é melhor descrita usando um exemplo:

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();

Usando as opções padrão de IntersectionObserver, seu callback será chamado quando o elemento aparecer parcialmente e quando sair completamente da janela de visualização.

Se você precisar observar vários elementos, é possível e recomendável observar vários elementos usando a mesma instância IntersectionObserver chamando observe() várias vezes.

Um parâmetro entries é transmitido para o callback, que é uma matriz de objetos IntersectionObserverEntry. Cada um desses objetos contém dados de interseção atualizados para um dos 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 é o resultado da chamada de getBoundingClientRect() no elemento raiz, que é a janela de visualização por padrão. boundingClientRect é o resultado de getBoundingClientRect() chamado no elemento observado. intersectionRect é a interseção desses dois retângulos e informa qual parte do elemento observado está visível. intersectionRatio está intimamente relacionado e informa quanto do elemento está visível. Com essas informações à sua disposição, é possível implementar recursos como o carregamento just-in-time de recursos antes que eles se tornem visíveis na tela. de forma eficiente.

Proporção de interseção.

Os IntersectionObservers entregam os dados de forma assíncrona, e o código de callback é executado na linha de execução principal. Além disso, a especificação diz que as implementações de IntersectionObserver precisam usar requestIdleCallback(). Isso significa que a chamada para o retorno de chamada informado tem baixa prioridade e será feita pelo navegador durante o tempo ocioso. Essa é uma decisão de design consciente.

Divs de rolagem

Não sou muito fã de rolagem dentro de um elemento, mas não estou aqui para julgar, e nem IntersectionObserver. O objeto options usa uma opção root que permite definir uma alternativa à janela de visualização como sua raiz. É importante ter em mente que root precisa ser um ancestral de todos os elementos observados.

Interseccione tudo!

Não! Desenvolvedor ruim! Isso não é um uso consciente dos ciclos de CPU do usuário. Vamos pensar em um scroller infinito como exemplo: nesse cenário, é recomendável adicionar sentinelas ao DOM e observá-las (e reciclá-las!). Adicione uma sentinela perto do último item no controle deslizante infinito. Quando essa sentinela aparecer, você poderá usar o callback para carregar dados, criar os próximos itens, anexá-los ao DOM e reposicionar a sentinela de acordo. Se você reciclar a sentinela corretamente, não será necessário fazer outra chamada para observe(). O IntersectionObserver continua funcionando.

Controle de rolagem infinito

Mais atualizações, por favor

Como mencionado anteriormente, o callback será acionado uma única vez quando o elemento observado aparecer parcialmente e outra vez quando ele sair da viewport. Dessa forma, IntersectionObserver responde à pergunta "O elemento X está visível?". No entanto, em alguns casos de uso, isso pode não ser suficiente.

É aí que entra a opção threshold. Ele permite definir uma matriz de limites intersectionRatio. Seu callback será chamado sempre que intersectionRatio cruzar um desses valores. O valor padrão de threshold é [0], o que explica o comportamento padrão. Se mudarmos threshold para [0, 0.25, 0.5, 0.75, 1], vamos receber uma notificação sempre que um quarto adicional do elemento ficar visível:

Animação de limite.

Alguma outra opção?

No momento, há apenas uma opção além das listadas acima. rootMargin permite especificar as margens da raiz, aumentando ou reduzindo a área usada para interseções. Essas margens são especificadas usando uma string estilo CSS, à la "10px 20px 30px 40px", especificando as margens superior, direita, inferior e esquerda, respectivamente. Para resumir, a struct de opções IntersectionObserver oferece as seguintes opções:

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> magia

Os IntersectionObservers foram criados especificamente para serviços de anúncios e widgets de redes sociais, que usam com frequência elementos <iframe> e podem se beneficiar ao saber se estão visíveis. Se uma <iframe> observa um dos elementos, tanto a rolagem da <iframe> quanto a da janela que contém a <iframe> acionam o callback nos momentos adequados. No último caso, no entanto, rootBounds será definido como null para evitar o vazamento de dados entre as origens.

O que é IntersectionObserver Not?

Vale lembrar que o IntersectionObserver não tem a intenção de ser perfeito nem de ter baixa latência. Usá-los para implementar iniciativas como animações dependentes de rolagem provavelmente vai falhar, porque os dados vão estar desatualizados no momento em que você for usá-los. A explicação tem mais detalhes sobre os casos de uso originais de IntersectionObserver.

Quanto trabalho posso fazer no callback?

Curto e simples: passar muito tempo no retorno de chamada fará com que seu aplicativo fique atrasado. Todas as práticas comuns se aplicam.

Vá em frente e cruze teus elementos

O suporte do navegador para IntersectionObserver é bom, já que está disponível em todos os navegadores modernos. Se necessário, um polyfill pode ser usado em navegadores mais antigos e está disponível no repositório do WICG. Obviamente, você não vai ter os benefícios de desempenho usando esse polyfill que uma implementação nativa teria.

Você já pode começar a usar o IntersectionObserver. Conte o que você pensou.