Mantys Core

Retarder l'exécution du JavaScript a cassé mon site : comment trouver le script fautif

Le résumé en une phrase : retarder le JavaScript casse un site de cinq façons mécaniques (une dépendance absente, un événement de page déjà passé, un premier clic avalé, une chaîne bloquée sur un script, un visiteur jamais compté), et chacune laisse une signature lisible dans le navigateur, ce qui permet d'exclure un seul script au lieu de couper toute l'option.

Par · · 9 min

Vous activez « Retarder l'exécution du JavaScript », le rapport PageSpeed bondit, puis les messages arrivent. Le menu mobile ne fait rien. Le bandeau cookies refuse de se fermer. Le formulaire de contact tourne dans le vide. Le slider de la page d'accueil est vide. La bulle de chat a disparu. Ou, une semaine plus tard, les statistiques montrent une baisse de sessions que personne n'explique.

Le conseil habituel, c'est une liste d'exclusions à coller, ou couper l'option. Les deux marchent, et les deux jettent le gain sans vous dire ce qui a vraiment cassé. Ce guide vous donne plutôt la méthode : pourquoi le report casse des choses, comment lire le symptôme, et comment trouver le seul script à sortir.

Ce que le report fait à une page

Normalement, chaque script de la page se télécharge et s'exécute pendant le chargement. Avec un report du JavaScript, les scripts sont mis de côté : leurs balises sont réécrites pour que le navigateur les ignore. Un petit chargeur attend que le visiteur fasse quelque chose (bouger la souris, faire défiler, toucher l'écran, appuyer sur une touche), ou qu'un minuteur expire, et seulement alors remet les scripts en place, l'un après l'autre.

C'est pour cela que le score de labo grimpe : pendant le test, presque aucun JavaScript ne tourne. C'est aussi pour cela que des choses cassent : chaque script tourne désormais plus tard, dans une page qui a déjà fini de charger, et seulement si le chargeur va jusque-là.

En bref
  • Les scripts reportés ne tournent pas au chargement. Ils tournent après la première interaction ou un minuteur.

  • Ils tournent dans une page déjà chargée, ce pour quoi ils n'ont pas été écrits.

  • Chaque casse décrite plus bas vient de l'un de ces deux faits.

Les cinq façons dont ça casse, et leur signature

1. Une dépendance manque. Un script est exclu du report (il tourne donc au chargement) mais quelque chose dont il a besoin est toujours mis de côté. Le cas classique : un script de menu tourne alors que jQuery n'existe pas encore. Signature : une erreur rouge dans la console, par exemple jQuery is not defined, $ is not a function ou Cannot read properties of undefined.

2. L'événement de page est déjà passé. Beaucoup de scripts attendent DOMContentLoaded ou window.onload avant de démarrer. Quand un script reporté arrive, ces événements sont passés depuis longtemps, donc son code d'initialisation attend quelque chose qui n'arrivera jamais. Signature : aucune erreur, la fonctionnalité ne démarre tout simplement jamais. Sliders, galeries et lightbox sont des victimes fréquentes.

3. Le premier clic est avalé. Le premier toucher du visiteur est l'interaction qui réveille le chargeur. Les scripts ne sont pas encore là quand ce toucher arrive, donc il ne fait rien. Signature : ça marche toujours au deuxième toucher. Nous avons détaillé ce cas dans le guide du menu burger.

4. La chaîne est bloquée sur un script. Les scripts reportés sont en général remis en place dans l'ordre, l'un après l'autre, pour que les dépendances continuent de fonctionner. Si l'un d'eux est retenu (un bloqueur de pub, un filtre DNS ou un proxy d'entreprise qui ne répond ni ne refuse), tout ce qui suit attend indéfiniment. Signature : intermittent, seulement chez certains visiteurs, jamais sur votre machine. Ce sont souvent les fonctionnalités du pied de page qui meurent.

5. Le visiteur n'est jamais compté. Si votre balise analytics est reportée, un visiteur qui lit la page et repart sans interagir (et avant le minuteur) ne la charge jamais. Signature : aucun bug visuel, juste moins de sessions, et une part plus élevée de visites courtes absentes des rapports.

En bref
  • Erreur rouge dans la console : une dépendance manque (cas 1).

  • Aucune erreur, la fonctionnalité ne démarre jamais : un événement de page attendu est déjà passé (cas 2).

  • Ça marche au deuxième toucher : la première interaction a servi à réveiller le chargeur (cas 3).

  • Ça casse seulement chez certains visiteurs : la chaîne est bloquée sur un script retenu (cas 4).

  • Rien de visible, moins de sessions : la balise analytics est reportée (cas 5).

Étape 1 : confirmer que le report est la cause

Coupez le report, videz chaque couche de cache (cache de page, cache de l'hébergeur, CDN), et testez de nouveau en navigation privée. Si la fonctionnalité marche, le report est la cause. Si elle échoue encore, arrêtez-vous là : le problème est ailleurs, souvent une optimisation CSS qui a retiré les styles d'un élément caché (voir le guide sur le critical CSS).

Puis réactivez-le et reproduisez le bug correctement :

  • L'émulation mobile des DevTools, pas votre vue desktop. Beaucoup de configurations gardent une version optimisée séparée par appareil.
  • Une fenêtre privée propre, sans extension, pour voir ce que voit un premier visiteur.
  • Touchez l'élément cassé en premier, sans bouger la souris ni faire défiler avant. C'est le seul moyen de voir le cas 3.
  • Puis une fois de plus avec un bloqueur de pub actif, pour faire apparaître le cas 4.

Étape 2 : trouver le script derrière la fonctionnalité cassée

Ouvrez les DevTools avant de reproduire, puis lisez trois endroits.

La console. Toute erreur rouge après votre interaction désigne le cas 1, et le nom de fichier à droite de l'erreur est votre premier suspect. Cliquez dessus : les DevTools affichent la ligne exacte.

Les écouteurs d'événements de l'élément. Clic droit sur le bouton cassé, Inspecter, puis ouvrez le panneau Event Listeners. Il liste chaque gestionnaire attaché à l'élément et le fichier qui l'a attaché. Si la liste est vide après votre interaction, le script qui devait attacher le gestionnaire n'a jamais tourné (cas 2 ou 4).

Une recherche dans tous les fichiers chargés. Dans le panneau Sources, cherchez dans tous les fichiers (Ctrl+Maj+F, ou Cmd+Option+F sur Mac) la classe ou l'id de l'élément cassé, par exemple menu-toggle ou cky-btn-accept. Le fichier qui y fait référence est celui qui le pilote.

Extrait pour la console : lister les scripts encore mis de côté
// À coller dans la console une fois la page chargée, avant toute interaction,
// puis de nouveau après votre premier clic. Adaptez le type au balisage de votre outil.
[...document.querySelectorAll('script[type]')]
  .filter(s => !/^(text|application)\/(javascript|ld\+json|json)$|^module$/.test(s.type))
  .map(s => s.src || s.dataset.src || (s.textContent || '').slice(0, 60))

Si le script identifié est toujours dans cette liste après votre interaction, la chaîne ne l'a jamais atteint : regardez ce qui se trouve juste avant lui (cas 4).

Étape 3 : exclure toute la chaîne, pas seulement le fichier

Un script fonctionne rarement seul. Avant de l'exclure, listez ce dont il a besoin, dans l'ordre où le source de la page les montre :

  • Ses bibliothèques : jQuery, et parfois jQuery Migrate ou le script cœur du thème.
  • Sa configuration : WordPress imprime souvent un petit script inline juste avant le fichier, dont l'id se termine par -js-extra (par exemple var wpcf7 = {...} pour Contact Form 7). Le fichier ne peut pas tourner sans lui.
  • Son propre fichier, en dernier.

Excluez-les ensemble, et ciblez chacun par un fragment unique de son URL (comme /contact-form-7/ ou jquery.min.js) plutôt que par un mot générique. Un fragment comme slider peut correspondre à bien plus de fichiers que vous ne le pensez, et chaque correspondance en trop rend une partie du gain.

En bref
  • Excluez la bibliothèque, la configuration inline et le fichier, ensemble.

  • Ciblez par un fragment d'URL unique, jamais par un mot générique.

  • Un fichier exclu dont la dépendance est toujours reportée échoue de la même façon, avec une erreur cette fois.

Étape 4 : quand vous ne trouvez pas, coupez en deux

Sur une page lourde avec 60 scripts reportés, les lire un par un prend un après-midi. Couper en deux prend six tests. Excluez la première moitié des scripts reportés et testez. Si la fonctionnalité marche, le coupable est dans cette moitié ; sinon, il est dans l'autre. Recoupez en deux, encore et encore : 60, 30, 15, 8, 4, 2, 1.

Deux règles rendent la méthode fiable : purgez chaque couche de cache entre deux tests, et gardez jQuery avec la moitié qui le contient, sinon chaque test échouera pour une mauvaise raison.

Les suspects habituels, famille par famille

Menus, accordéons, onglets. Excluez la chaîne (jQuery plus le script du thème), ou donnez au menu un petit script autonome. Le compromis est expliqué dans le guide du menu burger, et pourquoi le menu partage parfois un fichier avec le reste du thème dans le guide sur les scripts de thème monolithiques.

Bandeaux cookies et outils de consentement. Ne les reportez pas. Le bandeau doit être utilisable tout de suite, et le consentement doit être enregistré avant qu'une balise décide quoi charger. Ces scripts sont petits, les exclure coûte peu. Si vos balises utilisent le mode consentement, l'état par défaut doit aussi être posé avant que les balises tournent. Pour Axeptio en particulier, voyez comment accélérer le chargement d'Axeptio.

Formulaires et captchas. Excluez la chaîne de l'extension de formulaire sur les pages qui ont un formulaire, pas sur tout le site. Un captcha qui ne se charge que lorsque le visiteur commence à écrire est un bon compromis.

Sliders et tout ce qui est au-dessus de la ligne de flottaison. Si c'est la première chose que voit le visiteur, le reporter donne un hero vide ou sans style, et souvent un Largest Contentful Paint plus mauvais. Rien de visible au chargement ne devrait être reporté, seulement ce qui se trouve plus bas.

Widgets de chat et pop-ups. C'est exactement pour eux que le report existe. Gardez-les reportés. Si le lanceur doit être visible sans interaction, donnez à ce seul script un minuteur court plutôt que de l'exclure.

Analytics et balises. Les reporter rapporte peu sur une balise bien configurée et coûte des données. Décidez-le en connaissance de cause : soit la balise charge tôt et chaque visite compte, soit elle attend et vous acceptez de ne compter que les visiteurs qui interagissent.

Exclusion ciblée

Une chaîne, identifiée par URL, sur les templates qui l'utilisent. Le reste de la page reste reporté et le gain est conservé.

Exclusion large

« Exclure jQuery partout », un mot-clé générique, ou un mode sécurisé qui recharge tout tôt. Le bug a disparu, et la plus grande partie du gain avec.

Après la correction : vérifier ce que vous ne voyez pas

  • Testez encore une fois avec un bloqueur de pub : une correction qui ne marche que dans un navigateur propre casse encore pour une partie de vos visiteurs.
  • Comparez une semaine de sessions avant et après l'activation du report. Une baisse sans raison de trafic, c'est le cas 5.
  • Retestez après chaque mise à jour de thème ou d'extension : les noms de fichiers changent, et une exclusion par URL peut cesser de correspondre.
  • Et suivez l'ordre qui garde un site sûr : les optimisations sans risque d'abord, les risquées une par une, comme dans notre méthode pour optimiser sans casser.

Avec Mantys Core

Avec Mantys Core

Mantys Core peut piloter toute votre couche de performance, cache inclus, ou cohabiter avec votre installation existante. Son report du JavaScript est construit autour des casses décrites plus haut :

  • Le code d'initialisation des outils de consentement courants, le gestionnaire de balises et la balise analytics sont exclus du report par défaut, donc l'état du consentement et les balises démarrent au chargement. Si votre bandeau charge son propre fichier depuis un script séparé, vous ajoutez celui-ci aux exceptions.

  • Les scripts reportés sont remis en place dans l'ordre de la page, donc une bibliothèque tourne toujours avant ce qui en dépend.

  • Un script retenu par un filtre ou un proxy ne peut pas figer le reste : après une courte attente, la chaîne passe au suivant.

  • Une interaction qui arrive pendant que la page charge encore attend la page entière, donc aucun script du bas n'est laissé de côté.

  • Les exceptions se déclarent par fragment d'URL et peuvent être posées par template : le script du slider est exclu sur la page d'accueil seulement et reste reporté partout ailleurs.

  • Les services tiers indépendants peuvent recevoir leur propre minuteur court au lieu d'une exclusion complète.

Vous décidez toujours de ce qui compte sur chaque page. La plateforme s'assure qu'une exclusion reste une exclusion.

En résumé

  • Un report du JavaScript fait tourner chaque script plus tard, dans une page déjà chargée, et seulement si le chargeur va jusque-là.
  • Cinq casses, cinq signatures : une erreur en console, une fonctionnalité qui ne démarre jamais, un deuxième toucher, un échec chez certains visiteurs seulement, et moins de sessions.
  • Confirmez avec le report coupé, puis trouvez le script par la console, les écouteurs d'événements de l'élément et une recherche dans les fichiers.
  • Excluez toute la chaîne (bibliothèque, configuration inline, fichier) par fragment d'URL, et seulement là où c'est nécessaire. Coupez en deux quand vous êtes perdu.
  • Ne reportez jamais l'outil de consentement ni rien de visible au chargement ; gardez les widgets de chat et les pop-ups reportés.

Commentaires

Vous butez sur un problème similaire ? Décrivez votre configuration et ce que vous observez. Nous lisons chaque commentaire et nous répondons.

Aucun commentaire pour l’instant. Partagez le premier votre cas.

Les commentaires sont relus avant publication. Votre e-mail n’est conservé que pour vous répondre et peut être supprimé sur demande.

Mon compteObtenir une licence →