Eviter l’utilisation de document.write, surtout pour l’injection de scripts externes

Déjà pointée du doigt par les outils historiques d’audit de la performance front-end comme Google Page Speed (et évidemment Dareboost plus récemment), l’utilisation de l’instruction document.write pour injecter un script pose des problèmes importants pour le temps de chargement des sites web. A l’occasion d’une mise à jour de Chrome, qui fait revenir ce sujet dans l’actualité du moment, penchons nous sur les problèmes posés et les alternatives à envisager.

Chrome n’exécute plus le code document.write

La nouvelle est tombée au début du mois de septembre sur le site developers.google.com par l’intermédiaire de Paul Kinlan.
Chrome va prochainement intervenir à l’encontre de l’instruction document.write et ainsi interdire l’injection de scripts par son biais. Pour être plus précis, ce changement interviendra mi-octobre, avec la sortie de Chrome 54. Des avertissements sont déjà visibles dans la console développeur de Chrome 53 si jamais votre site est concerné.

Quelle est la nature de ce blocage exactement ?
Heureusement pour beaucoup de sites, ce blocage sera en fait très contextualisé :

  • l’utilisateur est sur une connexion bas débit,
  • le script qui est injecté l’est de manière synchrone (absence d’attribut async et defer) et n’est pas en cache sur la navigateur,
  • l’instruction est appelée sur la page principale (e.g. les iframes ne sont pas concernées)

Si ces conditions sont réunies, le script appelé ne se chargera tout simplement pas. Attention donc si votre site utilise des services externes, car même si vous n’utilisez pas cette instruction vous même, votre site pourrait avoir une telle dépendance, qui très bientôt ne fonctionnera plus pour une partie de vos visiteurs.

Même si cette évolution peut occasionner des dysfonctionnements, l’approche de Chrome reste intéressante, en visant à contraindre les sites web à l’abandon de (très) mauvaises pratiques. Comme nous allons le voir par la suite, l’instruction document.write occasionne en effet des problèmes de performance significatifs.

Les problèmes posés par document.write

Lorsque vous utilisez l’instruction Javascript suivante pour injecter un script :

document.write('<script src="https://example.com/script.js"></script>');

vous empêchez le parser du navigateur de continuer son exécution sur le code HTML qui pourrait suivre. Le navigateur web va rester bloqué sur cette instruction jusqu’à ce que le script soit téléchargé, mais aussi exécuté.
Et la situation peut être encore plus défavorable, puisque le navigateur va également rester bloqué si ce même script en injecte d’autres ! Un cas qui n’est pas si rare parmi les services externes que l’on peut être amené à utiliser sur nos pages web.
Chrome a d’ailleurs publié les résultats de ses tests dans son annonce : 7.6% des pages seraient concernées.

Pour savoir à quel point l’instruction document.write peut nuire à la performance, là encore on peut s’intéresser aux résultats obtenus par les tests de Chrome sur 1% de ses utilisateurs reposant sur une connexion 2G : un gain de 38% sur le temps moyen nécessaire pour interpréter une page, ce qui représente près de 6 secondes gagnées !

Un autre problème qui peut se poser par l’utilisation de cette instruction document.write, et pas uniquement pour injecter un script : si la construction de l’arbre DOM a été achevée, l’utiliser impliquera de le reconstruire complètement, ce qui est une opération coûteuse ! (document.write écrit dans le flux du document, une fois ce flux fermé tout appel a donc pour conséquence une réinitialisation).

Comment éviter d’utiliser document.write

De manière générale, vous devez éviter d’utiliser du Javascript de manière bloquante. Les attributs defer et async vous permettront d’appeler des scripts externes de manière asynchrone.
Attention cependant à l’ordre d’exécution de vos scripts dans le cas d‘un chargement asynchrone, il n’est pas garanti !

Si c’est un service externe qui est responsable d’un tel appel à document.write, vérifiez que le fournisseur ne propose pas une autre intégration. Envisagez sinon une alternative.

Enfin, pour insérer du contenus sur vos pages, préférez une manipulation du DOM plutôt que l’utilisation de document.write. Voici un exemple d’insertion de script :

var sNew = document.createElement("script");
sNew.async = true;
sNew.src = "https://example.com/script.min.js";
var s0 = document.getElementsByTagName('script')[0];
s0.parentNode.insertBefore(sNew, s0);

En conclusion, notez que dans ce contexte, notre outil d’analyse de site web va prochainement pénaliser davantage l’utilisation de document.write, notamment lorsque vous utilisez un contexte mobile pour vos analyses .