Back

Detectando Quando Elementos Entram no Viewport com Intersection Observer

Detectando Quando Elementos Entram no Viewport com Intersection Observer

Rastrear a visibilidade de elementos com event listeners de scroll pode prejudicar drasticamente a performance do seu site. Cada scroll dispara múltiplos eventos, cada um chamando getBoundingClientRect() e forçando reflows caros no navegador. A API Intersection Observer resolve esse problema de forma elegante, fornecendo otimização nativa do navegador para detectar quando elementos entram ou saem do viewport.

Pontos-Chave

  • O Intersection Observer elimina gargalos de performance causados por event listeners de scroll
  • A API executa de forma assíncrona, prevenindo bloqueio da thread principal
  • Um único observer pode monitorar múltiplos elementos de forma eficiente
  • A otimização nativa do navegador oferece melhor performance que cálculos manuais

Por Que os Eventos de Scroll Tradicionais São Insuficientes

Event listeners de scroll disparam continuamente durante a rolagem, frequentemente acionando mais de 60 vezes por segundo. Cada event handler que chama getBoundingClientRect() força o navegador a recalcular layouts, criando experiências de scroll travadas. Quando múltiplas bibliotecas rastreiam visibilidade independentemente—para anúncios, analytics e lazy loading—o impacto na performance se agrava dramaticamente.

A abordagem do Intersection Observer move esses cálculos para fora da thread principal, permitindo que o navegador otimize quando e como as verificações de intersecção ocorrem.

Entendendo os Fundamentos do Intersection Observer

A API Intersection Observer observa de forma assíncrona elementos-alvo cruzando fronteiras com um elemento raiz (tipicamente o viewport). Em vez de polling constante, ela te notifica apenas quando limites de visibilidade são cruzados.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Elemento está visível');
    }
  });
});

observer.observe(document.querySelector('.target'));

O navegador gerencia todos os cálculos de intersecção internamente, entregando resultados através de um callback com objetos IntersectionObserverEntry. Cada entry fornece isIntersecting (boolean) e intersectionRatio (porcentagem de visibilidade de 0-1).

Criando Seu Primeiro Observer

Configurar um Intersection Observer requer apenas uma função callback e configuração opcional:

const callback = (entries, observer) => {
  entries.forEach(entry => {
    // Propriedades-chave disponíveis
    console.log({
      isVisible: entry.isIntersecting,
      visibilityRatio: entry.intersectionRatio,
      targetElement: entry.target
    });
  });
};

const options = {
  root: null,        // viewport
  rootMargin: '0px', // sem offset
  threshold: 0.5     // 50% visível
};

const observer = new IntersectionObserver(callback, options);
observer.observe(document.querySelector('.target'));

O callback recebe um array de entries porque observers podem rastrear múltiplos elementos simultaneamente. O isIntersecting de cada entry indica visibilidade atual, enquanto intersectionRatio fornece a porcentagem precisa de visibilidade.

Configurando Opções do Observer

Três opções controlam quando os callbacks de intersecção disparam:

root: Define a área scrollável a ser observada. null usa o viewport; qualquer elemento scrollável funciona como raiz customizada.

rootMargin: Expande ou encolhe a bounding box da raiz. Use sintaxe de margin CSS: "50px" ou "10% 0px". Valores negativos encolhem; valores positivos expandem a área de detecção.

threshold: Porcentagem(ns) de visibilidade que acionam callbacks. Valor único: 0.5 (50%). Array para múltiplos gatilhos: [0, 0.25, 0.5, 0.75, 1].

Observando Múltiplos Elementos de Forma Eficiente

Uma instância de observer pode monitorar elementos ilimitados:

const observer = new IntersectionObserver(callback, options);
const targets = document.querySelectorAll('.lazy-load');

targets.forEach(target => observer.observe(target));

// Parar de observar elementos específicos
// observer.unobserve(element);

// Parar de observar todos os elementos
// observer.disconnect();

Esse padrão maximiza a eficiência—o navegador otimiza um único observer observando centenas de elementos melhor que múltiplos observers.

Exemplos de Implementação do Mundo Real

Lazy Loading de Imagens

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      imageObserver.unobserve(img);
    }
  });
}, { rootMargin: '50px' });

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

Disparando Animações no Scroll

const animationObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate');
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('.animate-on-scroll').forEach(element => {
  animationObserver.observe(element);
});

Auto-Play de Vídeos em Visualização

const videoObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const video = entry.target;
    if (entry.isIntersecting) {
      video.play();
    } else {
      video.pause();
    }
  });
}, { threshold: 0.5 });

document.querySelectorAll('video').forEach(video => {
  videoObserver.observe(video);
});

Conclusão

A API Intersection Observer transforma a detecção de viewport de um gargalo de performance em uma funcionalidade otimizada do navegador. Ao substituir event listeners de scroll por intersection observers, você elimina o bloqueio da thread principal enquanto ganha controle de visibilidade mais preciso. Comece a migrar seu código de visibilidade baseado em scroll hoje—os navegadores dos seus usuários vão agradecer.

FAQs

Sim, simplesmente chame observer.observe() nos novos elementos após eles serem adicionados ao DOM. A mesma instância do observer pode monitorar elementos adicionados a qualquer momento durante o ciclo de vida da página.

O callback dispara imediatamente com o estado atual de intersecção do elemento. Isso garante que você sempre saiba o status inicial de visibilidade sem precisar de verificações separadas.

Verifique se entry.isIntersecting é false no seu callback. O observer te notifica tanto quando elementos entram quanto saem da área observada baseado nas suas configurações de threshold.

Navegadores modernos o suportam nativamente. Para navegadores mais antigos como Internet Explorer, use o polyfill oficial do W3C que fornece funcionalidade idêntica através de JavaScript.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay