IntersectionObserver's aparecendo

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

Browser Support

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

Source

Digamos que você queira rastrear quando um elemento no DOM entra na janela de visualização visível. Você pode fazer isso para carregar imagens com carregamento lento 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 engasgo considerável no site. As coisas ficam quase impossíveis quando você sabe que seu site está sendo carregado dentro de um iframe e quer saber quando o usuário pode ver um elemento. O modelo de origem única e o navegador não permitem que você acesse dados 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 viewport.

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, agora é possível implementar recursos como o carregamento de recursos no momento certo antes que eles apareçam 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 callback fornecido tem baixa prioridade e será feita pelo navegador durante o tempo de inatividade. Essa é uma decisão de design consciente.

divs de rolagem

Não sou muito fã de rolar 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 à viewport como raiz. É importante lembrar 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.

Rolagem infinita

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 janela de visualização. 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 de 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 diminuindo a área usada para interseções. Essas margens são especificadas usando uma string no estilo CSS, "10px 20px 30px 40px", especificando a margem superior, direita, inferior e esquerda, respectivamente. Em resumo, 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> magic

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 em exibição. Se um <iframe> observar um dos elementos, a rolagem da <iframe> e da janela que contém a <iframe> vão acionar o callback nos momentos adequados. No último caso, no entanto, rootBounds será definido como null para evitar o vazamento de dados entre origens.

O que é IntersectionObserver Not?

Vale lembrar que o IntersectionObserver não é intencionalmente perfeito para pixels nem tem baixa latência. Usá-los para implementar iniciativas como animações dependentes de rolagem provavelmente vai falhar, já que os dados vão estar desatualizados no momento em que você for usá-los. O texto explicativo tem mais detalhes sobre os casos de uso originais de IntersectionObserver.

Quanto trabalho posso fazer no callback?

Breve e direta: passar muito tempo no callback vai fazer com que o app fique lento. Todas as práticas comuns se aplicam.

Vá em frente e cruze os 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.