Back

Quando Executar Seu Código: Eventos de Carregamento de Página Explicados

Quando Executar Seu Código: Eventos de Carregamento de Página Explicados

Quando seu JavaScript deve realmente ser executado? É uma questão que todo desenvolvedor frontend enfrenta, seja manipulando o DOM em JavaScript vanilla ou gerenciando ciclos de vida de componentes no React. A resposta depende de compreender os eventos de carregamento de página do navegador e escolher o hook certo para suas necessidades específicas.

Pontos-Chave

  • DOMContentLoaded dispara quando a análise do HTML é concluída, enquanto load aguarda todos os recursos
  • Navegadores modernos usam APIs de Page Visibility e Lifecycle em vez de eventos unload não confiáveis
  • React e outros frameworks lidam com o timing através de métodos de ciclo de vida de componentes
  • Verifique document.readyState para evitar perder eventos que já foram disparados

Entendendo o Ciclo de Vida Clássico do Navegador

O navegador dispara eventos em pontos específicos durante o carregamento da página. Saber quando cada um dispara—e o que está disponível naquele momento—determina onde seu código deve estar.

DOMContentLoaded vs load: A Distinção Crítica

DOMContentLoaded dispara quando o HTML está totalmente analisado e a árvore DOM está construída. Recursos externos como imagens, folhas de estilo e iframes ainda estão carregando. Esta é sua primeira oportunidade de consultar e manipular elementos DOM com segurança:

document.addEventListener('DOMContentLoaded', () => {
    // DOM está pronto, mas imagens ainda podem estar carregando
    const button = document.querySelector('#submit');
    button.addEventListener('click', handleSubmit);
});

O evento load aguarda tudo—imagens, folhas de estilo, iframes e outros recursos externos. Use-o quando precisar de informações completas sobre recursos:

window.addEventListener('load', () => {
    // Todos os recursos carregados - dimensões de imagem estão disponíveis
    const img = document.querySelector('#hero');
    console.log(`Tamanho da imagem: ${img.naturalWidth}x${img.naturalHeight}`);
});

Como os Scripts Afetam o Timing

Tags <script> regulares bloqueiam o DOMContentLoaded—o navegador deve executá-las antes de continuar. No entanto, scripts com defer executam após a análise do DOM, mas antes do DOMContentLoaded disparar. Scripts com async carregam em paralelo e executam imediatamente quando baixados, potencialmente antes ou depois do DOMContentLoaded.

Para recursos específicos de elementos, use eventos load individuais:

const img = new Image();
img.addEventListener('load', () => console.log('Imagem pronta'));
img.src = 'photo.jpg';

Abordagens Modernas: document.readyState e Além

Usando document.readyState

Em vez de esperar que seu event listener seja anexado a tempo, verifique o estado atual:

function initialize() {
    // Seu código de inicialização
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initialize);
} else {
    // DOMContentLoaded já disparou
    initialize();
}

Os três estados são:

  • 'loading' - Documento está sendo analisado
  • 'interactive' - Análise completa, DOMContentLoaded prestes a disparar
  • 'complete' - Todos os recursos carregados

O $(document).ready() do jQuery era essencialmente um wrapper cross-browser para este padrão. Hoje, APIs nativas lidam com isso de forma confiável em todos os navegadores.

Page Visibility e Lifecycle API: A Nova Realidade

Por Que beforeunload e unload São Problemáticos

Navegadores modernos otimizam agressivamente o desempenho através de recursos como o cache de navegação para frente e para trás (bfcache). Páginas entrando no bfcache não são descarregadas—elas são suspensas. Isso significa que eventos beforeunload e unload não disparam de forma confiável quando usuários navegam para longe. Além disso, navegadores agora limitam ou ignoram mensagens personalizadas em diálogos beforeunload para prevenir abuso.

A Alternativa da Page Visibility API

Em vez de eventos unload não confiáveis, use a Page Visibility API para responder quando usuários trocam de abas ou minimizam o navegador:

document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        // Página está oculta - pausar operações custosas
        pauseVideoPlayback();
        throttleWebSocket();
    } else {
        // Página está visível novamente
        resumeOperations();
    }
});

Estados do Ciclo de Vida da Página

A Page Lifecycle API estende isso com estados como frozen (aba suspensa para economizar memória) e terminated:

document.addEventListener('freeze', () => {
    // Salvar estado - aba pode ser descartada
    localStorage.setItem('appState', JSON.stringify(state));
});

document.addEventListener('resume', () => {
    // Restaurar após ser congelado
    hydrateState();
});

React useEffect vs DOMContentLoaded

No React e outros frameworks modernos, você raramente usa DOMContentLoaded diretamente. Métodos de ciclo de vida de componentes lidam com o timing de inicialização:

// Componente React
import { useEffect } from 'react';

function MyComponent() {
    useEffect(() => {
        // Executa após o componente montar e atualizações do DOM
        // Timing similar ao DOMContentLoaded para este componente
        initializeThirdPartyLibrary();
        
        return () => {
            // Limpeza na desmontagem
            cleanup();
        };
    }, []); // Array de dependências vazio = executar uma vez após montagem
    
    return <div>Conteúdo do componente</div>;
}

Para Next.js ou outros frameworks SSR, código no useEffect executa apenas no cliente após a hidratação—o framework lida com a complexidade do timing de execução servidor vs. cliente.

Escolhendo o Hook Certo

Para JavaScript vanilla:

  • Manipulação do DOM: Use DOMContentLoaded
  • Código dependente de recursos: Use evento window load
  • Persistência de estado: Use Page Visibility API
  • Verificando estado atual: Use document.readyState

Para SPAs e frameworks:

  • Inicialização de componentes: Use ciclo de vida do framework (useEffect, mounted, etc.)
  • Mudanças de rota: Use eventos do roteador
  • Background/foreground: Ainda use Page Visibility API

Evite estes padrões:

  • Não confie em beforeunload/unload para operações críticas
  • Não use DOMContentLoaded em componentes React
  • Não assuma que scripts executam em um carregamento de página fresco (considere bfcache)

Conclusão

Eventos de carregamento de página JavaScript evoluíram além de simples handlers DOMContentLoaded e load. Embora esses eventos clássicos permaneçam essenciais para JavaScript vanilla, o desenvolvimento moderno requer compreender Page Visibility, Lifecycle APIs e padrões específicos de frameworks. Escolha sua estratégia de inicialização baseada em quais recursos você precisa e se está trabalhando com uma página renderizada no servidor ou construindo uma SPA completa. Mais importante, não confie em padrões obsoletos como eventos unload—adote as APIs modernas construídas para aplicações web contemporâneas.

Perguntas Frequentes

Scripts defer executam em ordem após a análise do DOM, mas antes do DOMContentLoaded. Scripts async executam imediatamente quando baixados, potencialmente interrompendo a análise. Use defer para scripts que precisam de acesso ao DOM e async para scripts independentes como analytics.

Embora window.onload funcione, addEventListener é preferível porque permite múltiplos handlers e não sobrescreverá os existentes. O próprio evento load permanece útil quando você precisa que todos os recursos estejam totalmente carregados antes de executar o código.

Use useEffect com um array de dependências vazio para inicialização no lado do cliente. Isso executa após a hidratação ser concluída. Para código no lado do servidor, use métodos específicos do framework como getServerSideProps ou getStaticProps em vez de eventos do navegador.

Navegadores modernos fazem cache de páginas e podem não disparar beforeunload quando usuários navegam para longe. Use a Page Visibility API em vez disso para detectar quando usuários saem, e persista dados críticos continuamente em vez de na saída.

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