IntersectionObserver's aparecendo

IntersectionObservers, que avisa quando um elemento observado entra ou sai da janela de visualização do navegador.

Compatibilidade com navegadores

  • 51
  • 15
  • 55
  • 12.1

Origem

Digamos que você queira acompanhar quando um elemento no DOM entra na janela de visualização visível. Faça isso para carregar as imagens lentamente a tempo ou porque você precisa saber se o usuário está realmente olhando para um determinado banner de anúncio. Para fazer isso, ative o evento de rolagem ou use um timer periódico e chame getBoundingClientRect(). nesse elemento.

No entanto, essa abordagem é muito lenta, já que cada chamada para getBoundingClientRect() força o navegador a recriar o layout da página inteira e causa uma instabilidade considerável no site. Os casos ficam quase impossível quando você sabe que o site está sendo carregado dentro de um iframe e quer saber quando o usuário consegue 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 IntersectionObserver foi projetado para tornar esse teste de visibilidade mais eficiente, e ele funciona em todos os navegadores modernos. IntersectionObserver informa quando um elemento observado entra ou sai da janela de visualização do navegador.

Visibilidade do iframe

Como criar uma IntersectionObserver

A API é bem pequena. Para ser descrita, usamos 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();

Ao usar as opções padrão de IntersectionObserver, seu callback será chamado quando o elemento ficar parcialmente visível e quando ele sair completamente da janela de visualização.

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

Um parâmetro entries é transmitido ao 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 da chamada de getBoundingClientRect() 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. Com eficiência.

Proporção de interseção.

As 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 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 à janela de visualização como sua raiz. É importante ter em mente que root precisa ser um ancestral de todos os elementos observados.

Se cruzar com todas as coisas!

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 próxima ao último item no botão de rolagem 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

Receba mais atualizações

Como mencionado anteriormente, o callback será acionado uma única vez quando o elemento observado ficar parcialmente visível e outro momento em que ele sair da janela de visualização. Dessa forma, IntersectionObserver fornece uma resposta para a pergunta: "O elemento X está visível?". Em alguns casos de uso, no entanto, 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 mais um quarto do elemento se tornar visível:

Animação de limite.

Alguma outra opção?

Por enquanto, há apenas uma opção adicional 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, 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 projetados especificamente para considerar os serviços de publicidade e os widgets de redes sociais, que usam com frequência elementos <iframe> e podem se beneficiar se saberem se eles 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 origens.

Qual é o objetivo de IntersectionObserver Não?

É importante 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. 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

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 terá os benefícios de desempenho que uma implementação nativa ofereceria usando esse polyfill.

Você pode começar a usar o IntersectionObserver agora mesmo! Conte o que você criou.