Os IntersectionObservers informam quando um elemento observado entra ou sai da janela de visualização do navegador.
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.
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.
Os IntersectionObserver
s 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.
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:
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 IntersectionObserver
s 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.