Back

Quand exécuter votre code : les événements de chargement de page expliqués

Quand exécuter votre code : les événements de chargement de page expliqués

Quand votre JavaScript doit-il réellement s’exécuter ? C’est une question que tout développeur frontend se pose, que vous manipuliez le DOM en JavaScript vanilla ou que vous gériez les cycles de vie des composants dans React. La réponse dépend de la compréhension des événements de chargement de page du navigateur et du choix du bon point d’accroche pour vos besoins spécifiques.

Points clés à retenir

  • DOMContentLoaded se déclenche lorsque l’analyse HTML est terminée, tandis que load attend toutes les ressources
  • Les navigateurs modernes utilisent les API Page Visibility et Lifecycle au lieu des événements unload peu fiables
  • React et autres frameworks gèrent le timing via les méthodes de cycle de vie des composants
  • Vérifiez document.readyState pour éviter de manquer des événements déjà déclenchés

Comprendre le cycle de vie classique du navigateur

Le navigateur déclenche des événements à des moments précis pendant le chargement de la page. Savoir quand chacun se déclenche — et ce qui est disponible à ce moment-là — détermine où votre code doit se situer.

DOMContentLoaded vs load : la distinction critique

DOMContentLoaded se déclenche lorsque le HTML est entièrement analysé et que l’arbre DOM est construit. Les ressources externes comme les images, les feuilles de style et les iframes sont encore en cours de chargement. C’est votre première opportunité pour interroger et manipuler les éléments DOM en toute sécurité :

document.addEventListener('DOMContentLoaded', () => {
    // DOM is ready, but images might still be loading
    const button = document.querySelector('#submit');
    button.addEventListener('click', handleSubmit);
});

L’événement load attend tout — images, feuilles de style, iframes et autres ressources externes. Utilisez-le lorsque vous avez besoin d’informations complètes sur les ressources :

window.addEventListener('load', () => {
    // All resources loaded - image dimensions are available
    const img = document.querySelector('#hero');
    console.log(`Image size: ${img.naturalWidth}x${img.naturalHeight}`);
});

Comment les scripts affectent le timing

Les balises <script> classiques bloquent DOMContentLoaded — le navigateur doit les exécuter avant de continuer. Cependant, les scripts avec defer s’exécutent après l’analyse du DOM mais avant le déclenchement de DOMContentLoaded. Les scripts avec async se chargent en parallèle et s’exécutent immédiatement une fois téléchargés, potentiellement avant ou après DOMContentLoaded.

Pour les ressources spécifiques à un élément, utilisez des événements load individuels :

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

Approches modernes : document.readyState et au-delà

Utiliser document.readyState

Au lieu d’espérer que votre écouteur d’événement soit attaché à temps, vérifiez l’état actuel :

function initialize() {
    // Your initialization code
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initialize);
} else {
    // DOMContentLoaded already fired
    initialize();
}

Les trois états sont :

  • 'loading' - Le document est en cours d’analyse
  • 'interactive' - L’analyse est terminée, DOMContentLoaded sur le point de se déclencher
  • 'complete' - Toutes les ressources sont chargées

Le $(document).ready() de jQuery était essentiellement un wrapper multi-navigateurs pour ce pattern. Aujourd’hui, les API natives gèrent cela de manière fiable sur tous les navigateurs.

API Page Visibility et Lifecycle : la nouvelle réalité

Pourquoi beforeunload et unload sont problématiques

Les navigateurs modernes optimisent agressivement les performances grâce à des fonctionnalités comme le cache avant-arrière (bfcache). Les pages entrant dans le bfcache ne sont pas déchargées — elles sont suspendues. Cela signifie que les événements beforeunload et unload ne se déclenchent pas de manière fiable lorsque les utilisateurs naviguent ailleurs. De plus, les navigateurs limitent ou ignorent désormais les messages personnalisés dans les dialogues beforeunload pour prévenir les abus.

L’alternative de l’API Page Visibility

Au lieu d’événements unload peu fiables, utilisez l’API Page Visibility pour réagir lorsque les utilisateurs changent d’onglet ou minimisent le navigateur :

document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        // Page is hidden - pause expensive operations
        pauseVideoPlayback();
        throttleWebSocket();
    } else {
        // Page is visible again
        resumeOperations();
    }
});

États du cycle de vie de la page

L’API Page Lifecycle étend cela avec des états comme frozen (onglet suspendu pour économiser la mémoire) et terminated :

document.addEventListener('freeze', () => {
    // Save state - tab might be discarded
    localStorage.setItem('appState', JSON.stringify(state));
});

document.addEventListener('resume', () => {
    // Restore after being frozen
    hydrateState();
});

React useEffect vs DOMContentLoaded

Dans React et autres frameworks modernes, vous utilisez rarement DOMContentLoaded directement. Les méthodes de cycle de vie des composants gèrent le timing d’initialisation :

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

function MyComponent() {
    useEffect(() => {
        // Runs after component mounts and DOM updates
        // Similar timing to DOMContentLoaded for this component
        initializeThirdPartyLibrary();
        
        return () => {
            // Cleanup on unmount
            cleanup();
        };
    }, []); // Empty dependency array = run once after mount
    
    return <div>Component content</div>;
}

Pour Next.js ou d’autres frameworks SSR, le code dans useEffect s’exécute uniquement côté client après l’hydratation — le framework gère la complexité du timing d’exécution serveur vs client.

Choisir le bon point d’accroche

Pour JavaScript vanilla :

  • Manipulation du DOM : utilisez DOMContentLoaded
  • Code dépendant des ressources : utilisez l’événement window load
  • Persistance d’état : utilisez l’API Page Visibility
  • Vérification de l’état actuel : utilisez document.readyState

Pour les SPA et frameworks :

  • Initialisation de composant : utilisez le cycle de vie du framework (useEffect, mounted, etc.)
  • Changements de route : utilisez les événements du routeur
  • Arrière-plan/premier plan : utilisez toujours l’API Page Visibility

Évitez ces patterns :

  • Ne comptez pas sur beforeunload/unload pour les opérations critiques
  • N’utilisez pas DOMContentLoaded dans les composants React
  • Ne supposez pas que les scripts s’exécutent lors d’un chargement de page frais (considérez le bfcache)

Conclusion

Les événements de chargement de page JavaScript ont évolué au-delà des simples gestionnaires DOMContentLoaded et load. Bien que ces événements classiques restent essentiels pour le JavaScript vanilla, le développement moderne nécessite de comprendre les API Page Visibility, Lifecycle et les patterns spécifiques aux frameworks. Choisissez votre stratégie d’initialisation en fonction des ressources dont vous avez besoin et selon que vous travaillez avec une page rendue côté serveur ou que vous construisez une SPA complète. Plus important encore, ne vous fiez pas aux patterns obsolètes comme les événements unload — adoptez les API modernes conçues pour les applications web contemporaines.

FAQ

Les scripts defer s'exécutent dans l'ordre après l'analyse du DOM mais avant DOMContentLoaded. Les scripts async s'exécutent immédiatement une fois téléchargés, interrompant potentiellement l'analyse. Utilisez defer pour les scripts nécessitant un accès au DOM et async pour les scripts indépendants comme les analytics.

Bien que window.onload fonctionne, addEventListener est préféré car il permet plusieurs gestionnaires et n'écrasera pas ceux existants. L'événement load lui-même reste utile lorsque vous avez besoin que toutes les ressources soient entièrement chargées avant d'exécuter du code.

Utilisez useEffect avec un tableau de dépendances vide pour l'initialisation côté client. Cela s'exécute après la fin de l'hydratation. Pour le code côté serveur, utilisez des méthodes spécifiques au framework comme getServerSideProps ou getStaticProps au lieu d'événements navigateur.

Les navigateurs modernes mettent en cache les pages et peuvent ne pas déclencher beforeunload lorsque les utilisateurs naviguent ailleurs. Utilisez plutôt l'API Page Visibility pour détecter quand les utilisateurs partent, et persistez les données critiques de manière continue plutôt qu'à la sortie.

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