O Intersection Observer v2 adiciona a capacidade de não apenas observar interseções em si, mas também detectar se o elemento de interseção estava visível no momento da interseção.
A Intersection Observer v1 é uma das APIs que provavelmente são universalmente adoradas. Agora que o Safari também oferece suporte a ela, ela finalmente pode ser usada em todos os principais navegadores. Para relembrar rapidamente a API, recomendo assistir o Supercharged Microtip de Surma no Intersection Observer v1 incorporado abaixo. Você também pode ler o artigo detalhado de Surma. As pessoas usaram o Intersection Observer v1 para uma ampla gama de casos de uso, como carregamento lento de imagens e vídeos, notificações quando os elementos atingem position: sticky
, eventos de análise acionados e muito mais.
Para conferir todos os detalhes, consulte a documentação do Intersection Observer no MDN. Mas, como lembrete, a API Intersection Observer v1 tem a seguinte aparência no caso mais básico:
const onIntersection = (entries) => { for (const entry of entries) { if (entry.isIntersecting) { console.log(entry); } } }; const observer = new IntersectionObserver(onIntersection); observer.observe(document.querySelector('#some-target'));
O que é desafiador no Intersection Observer v1?
O Intersection Observer v1 é ótimo, mas não é perfeito. Há alguns casos em que a API não funciona. Vamos dar uma olhada mais de perto. A API Intersection Observer v1 pode informar quando um elemento é rolado para a viewport da janela, mas não informa se o elemento está coberto por qualquer outro conteúdo da página (ou seja, quando o elemento está obstruído) ou se a exibição visual do elemento foi modificada por efeitos visuais, como transform
, opacity
, filter
etc., que efetivamente podem torná-lo invisível.
Para um elemento no documento de nível superior, essas informações podem ser determinadas analisando o DOM por JavaScript, por exemplo, usando DocumentOrShadowRoot.elementFromPoint()
e depois aprofundando. Por outro lado, não é possível acessar as mesmas informações se o elemento em questão estiver localizado em um iframe de terceiros.
Por que a visibilidade real é tão importante?
Infelizmente, a Internet é um lugar que atrai usuários de má-fé com intenções ainda piores. Por exemplo, um editor duvidoso que veicula anúncios PPC em um site de conteúdo pode ser incentivado a enganar as pessoas para que cliquem nos anúncios e aumentem o pagamento do editor (pelo menos por um curto período, até que a rede de publicidade os detecte). Normalmente, esses anúncios são veiculados em iframes. Agora, se o editor quisesse fazer com que os usuários clicassem nesses anúncios, ele poderia tornar os iframes de anúncios completamente transparentes aplicando uma regra CSS iframe { opacity: 0; }
e sobrepondo os iframes em cima de algo atraente, como um vídeo de gato fofo que os usuários realmente gostariam de clicar. Isso é chamado de clickjacking. É possível conferir um ataque de clickjacking na seção de cima desta demonstração (tente "assistir" o vídeo do gato e ativar o "modo truque"). Você vai notar que o anúncio no iframe "acha" que recebeu cliques legítimos, mesmo que ele estivesse completamente transparente quando você clicou nele (fingindo ser involuntário).
Como o Intersection Observer v2 corrige isso?
O Intersection Observer v2 introduz o conceito de rastreamento da "visibilidade" real de um elemento alvo, como um ser humano o definiria. Ao definir uma opção no construtor IntersectionObserver
, as instâncias IntersectionObserverEntry
que se cruzam vão conter um novo campo booleano chamado isVisible
. Um valor true
para isVisible
é uma garantia forte da implementação de que o elemento de destino não é totalmente ocluído por outro conteúdo e não tem efeitos visuais aplicados que alterem ou distorçam a exibição na tela. Por outro lado, um valor false
significa que a implementação não pode fazer essa garantia.
Um detalhe importante da especificação é que a implementação é permitida para informar falsas negativas, ou seja, definir isVisible
como false
mesmo quando o elemento de destino está completamente visível e não modificado. Por motivos de desempenho ou outros, os navegadores se limitam a trabalhar com caixas limitantes e geometria retilínea. Eles não tentam alcançar resultados perfeitos para modificações como border-radius
.
No entanto, falsas correspondências não são permitidas em nenhuma circunstância. Isso significa que isVisible
não pode ser definido como true
quando o elemento de destino não está completamente visível e não foi modificado.
Como é o novo código na prática?
O construtor IntersectionObserver
agora usa duas propriedades de configuração adicionais: delay
e trackVisibility
. O delay
é um número que indica o atraso mínimo em milissegundos entre as notificações do observador para um determinado destino. O trackVisibility
é um valor booleano que indica se o observador vai rastrear mudanças na visibilidade de um destino.
É importante observar que, quando trackVisibility
é true
, delay
precisa ser, pelo menos, 100
(ou seja, não mais de uma notificação a cada 100 ms). Como observado anteriormente, a visibilidade é cara para calcular, e esse requisito é uma precaução contra a degradação de desempenho e o consumo de bateria. O desenvolvedor responsável vai usar o maior valor tolerável para atraso.
De acordo com a especificação atual, a visibilidade é calculada da seguinte maneira:
Se o atributo
trackVisibility
do observador forfalse
, o destino será considerado visível. Isso corresponde ao comportamento atual da v1.Se o destino tiver uma matriz de transformação efetiva diferente de uma tradução 2D ou aumento de escala 2D proporcional, ele será considerado invisível.
Se o destino ou qualquer elemento na cadeia de blocos que o contém tiver uma opacidade efetiva diferente de 1,0, ele será considerado invisível.
Se o destino ou qualquer elemento na cadeia de blocos que o contém tiver filtros aplicados, ele será considerado invisível.
Se a implementação não puder garantir que o alvo não esteja completamente oculto por outro conteúdo da página, ele será considerado invisível.
Isso significa que as implementações atuais são bastante conservadoras ao garantir a visibilidade. Por exemplo, aplicar um filtro de escala de cinza quase imperceptível, como filter: grayscale(0.01%)
, ou definir uma transparência quase invisível com opacity: 0.99
renderizaria o elemento invisível.
Confira abaixo um exemplo de código curto que ilustra os novos recursos da API. Você pode conferir a lógica de rastreamento de cliques em ação na segunda seção da demonstração (mas agora, tente "assistir" o vídeo do filhote). Ative o "modo de truque" novamente para se tornar imediatamente um editor duvidoso e conferir como o Intersection Observer v2 impede que cliques em anúncios não legítimos sejam rastreados. Desta vez, o Intersection Observer v2 está aqui para ajudar. 🎉
<!DOCTYPE html> <!-- This is the ad running in the iframe --> <button id="callToActionButton">Buy now!</button>
// This is code running in the iframe. // The iframe must be visible for at least 800ms prior to an input event // for the input event to be considered valid. const minimumVisibleDuration = 800; // Keep track of when the button transitioned to a visible state. let visibleSince = 0; const button = document.querySelector('#callToActionButton'); button.addEventListener('click', (event) => { if ((visibleSince > 0) && (performance.now() - visibleSince >= minimumVisibleDuration)) { trackAdClick(); } else { rejectAdClick(); } }); const observer = new IntersectionObserver((changes) => { for (const change of changes) { // ⚠️ Feature detection if (typeof change.isVisible === 'undefined') { // The browser doesn't support Intersection Observer v2, falling back to v1 behavior. change.isVisible = true; } if (change.isIntersecting && change.isVisible) { visibleSince = change.time; } else { visibleSince = 0; } } }, { threshold: [1.0], // 🆕 Track the actual visibility of the element trackVisibility: true, // 🆕 Set a minimum delay between notifications delay: 100 })); // Require that the entire iframe be visible. observer.observe(document.querySelector('#ad'));
Links relacionados
- Rascunho mais recente do editor da especificação do Intersection Observer.
- Intersection Observer v2 no Status da plataforma do Chrome.
- Intersection Observer v2 bug do Chromium.
- Blink Intent to Implement posting.
Agradecimentos
Agradecemos a Simeon Vincent, Yoav Weiss e Mathias Bynens por revisar este artigo, bem como a Stefan Zager por revisar e implementar o recurso no Chrome. Imagem principal de Sergey Semin no Unsplash.