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.
  • Edge: 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 recriar o layout da página inteira e causa uma instabilidade considerável 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 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 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 de chamar 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 você pode 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 gosto muito de rolar dentro de elementos, mas não estou aqui para julgar, assim como 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 significa considerar o uso dos ciclos de CPU do usuário. Pense no botão de rolagem infinito como exemplo: nesse cenário, é definitivamente aconselhável adicionar sentinelas ao DOM e observar (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 corretamente a sentinela, nenhuma outra chamada para observe() será necessária. 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 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 mais um quarto do elemento se tornar 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, á la "10px 20px 30px 40px", especificando a margem superior, direita, inferior e esquerda, respectivamente. Para resumir, o 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 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 as origens.

O que é IntersectionObserver Not?

É preciso lembrar que o IntersectionObserver não é intencionalmente um pixel perfeito nem tem baixa latência. Usá-las para implementar ações como animações dependentes de rolagem está fadado ao fracasso, já que os dados estarão desatualizados no momento em que forem usados. 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

A compatibilidade com IntersectionObserver é boa, já que está disponível em todos os navegadores mais recentes. Se necessário, um polyfill pode ser usado em navegadores mais antigos e está disponível no repositório da WICG (link em inglês). 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.