IntersectionObservers, que avisa 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. 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, 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. 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.
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.
As 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 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.
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:
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 IntersectionObserver
s 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 oferece ao usar esse polyfill.
Você pode começar a usar o IntersectionObserver
agora mesmo! Conte o que você criou.