Intervenir contre document.write()

Avez-vous récemment vu un avertissement comme celui-ci dans la console de développement de Chrome et vous êtes-vous demandé de quoi il s'agissait ?

(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.

La composabilité est l'une des grandes forces du Web. Elle nous permet d'intégrer facilement des services créés par des tiers pour créer de nouveaux produits de qualité. L'un des inconvénients de la composabilité est qu'elle implique une responsabilité partagée sur l'expérience utilisateur. Si l'intégration n'est pas optimale, l'expérience utilisateur en sera affectée.

L'utilisation de document.write() dans les pages, en particulier pour injecter des scripts, est une cause connue de mauvaises performances. Aussi inoffensif que cela puisse paraître, l'exemple suivant peut poser de réels problèmes pour les utilisateurs.

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

Avant que le navigateur puisse afficher une page, il doit créer l'arborescence DOM en analysant la balise HTML. Chaque fois que l'analyseur rencontre un script, il doit s'arrêter et l'exécuter avant de pouvoir continuer à analyser le code HTML. Si le script injecte dynamiquement un autre script, l'analyseur est contraint d'attendre encore plus longtemps que la ressource soit téléchargée, ce qui peut entraîner un ou plusieurs aller-retour réseau et retarder le premier rendu de la page.

Pour les internautes qui disposent d'une connexion lente telle qu'une connexion 2G, les scripts externes insérés de façon dynamique via document.write() peuvent retarder l'affichage du contenu de la page principale pendant des dizaines de secondes, ou empêcher le chargement des pages ou nécessiter un délai de chargement tellement long que l'utilisateur abandonne. D'après l'instrumentation dans Chrome, nous avons appris que les pages contenant des scripts tiers insérés via document.write() sont généralement deux fois moins rapides à charger que les autres pages en 2G.

Nous avons collecté des données lors d'un test sur le terrain de 28 jours auprès de 1% des utilisateurs stables de Chrome, limité aux utilisateurs disposant d'une connexion 2G. Nous avons constaté que 7, 6% de tous les chargements de pages sur le réseau 2G incluaient au moins un script intersites bloquant l'analyseur qui a été inséré via document.write() dans le document de niveau supérieur. En bloquant le chargement de ces scripts, nous avons constaté les améliorations suivantes sur ces chargements:

  • 10% de chargements de pages supplémentaires atteignant le premier élément de contenu (First Contentful Paint) (confirmation visuelle pour l'utilisateur que la page se charge effectivement), 25% de chargements de pages supplémentaires atteignant l'état d'analyse complète et 10% de rechargements en moins, ce qui suggère une diminution de la frustration des utilisateurs.
  • 21% de réduction du temps moyen (plus d'une seconde plus rapide) jusqu'à la première peinture avec contenu
  • 38% de réduction du temps moyen d'analyse d'une page, ce qui représente une amélioration de près de six secondes, ce qui réduit considérablement le temps nécessaire pour afficher ce qui est important pour l'utilisateur.

Compte tenu de ces données, Chrome, à partir de la version 55, intervient au nom de tous les utilisateurs lorsque nous détectons ce modèle connu comme incorrect en modifiant la façon dont document.write() est géré dans Chrome (voir État de Chrome). Plus précisément, Chrome n'exécutera pas les éléments <script> injectés via document.write() lorsque toutes les conditions suivantes sont remplies:

  1. L'utilisateur dispose d'une connexion lente, en particulier s'il utilise la 2G. (À l'avenir, ce changement pourra être étendu à d'autres utilisateurs disposant de connexions lentes, telles que la 3G ou le Wi-Fi lents.)
  2. document.write() se trouve dans un document de premier niveau. L'intervention ne s'applique pas aux scripts document.written dans les iFrames, car ils ne bloquent pas le rendu de la page principale.
  3. Le script dans document.write() bloque l'analyseur. Les scripts avec les attributs async ou defer continueront de s'exécuter.
  4. Le script n'est pas hébergé sur le même site. En d'autres termes, Chrome n'intervient pas pour les scripts avec un eTLD+1 correspondant (par exemple, un script hébergé sur js.example.org inséré sur www.example.org).
  5. Le script ne se trouve pas déjà dans le cache HTTP du navigateur. Les scripts dans le cache ne subiront pas de retard réseau et s'exécuteront toujours.
  6. La requête de la page n'est pas un rechargement. Chrome n'intervient pas si l'utilisateur a déclenché une actualisation et exécute la page normalement.

Les extraits tiers utilisent parfois document.write() pour charger des scripts. Heureusement, la plupart des tiers proposent des alternatives de chargement asynchrone, qui permettent aux scripts tiers de se charger sans bloquer l'affichage du reste du contenu de la page.

Comment résoudre ce problème ?

La réponse simple est de ne pas injecter de scripts à l'aide de document.write(). Nous gérons un ensemble de services connus pour la prise en charge du chargeur asynchrone que nous vous encourageons à consulter régulièrement.

Si votre fournisseur ne figure pas dans la liste et qu'il prend en charge le chargement asynchrone des scripts, veuillez nous en informer afin que nous puissions mettre à jour la page pour aider tous les utilisateurs.

Si votre fournisseur ne permet pas de charger des scripts de manière asynchrone sur votre page, nous vous invitons à le contacter et à nous en informer, ainsi que lui, sur les conséquences que cela aura sur votre site.

Si votre fournisseur vous fournit un extrait qui inclut document.write(), vous pouvez peut-être ajouter un attribut async à l'élément de script ou ajouter les éléments de script avec des API DOM telles que document.appendChild() ou parentNode.insertBefore().

Détecter quand votre site est affecté

De nombreux critères déterminent si la restriction est appliquée. Comment savoir si vous êtes concerné ?

Détecter quand un utilisateur est connecté au réseau 2G

Pour comprendre l'impact potentiel de ce changement, vous devez d'abord déterminer combien de vos utilisateurs seront sur la 2G. Vous pouvez détecter le type et la vitesse de réseau actuels de l'utilisateur à l'aide de l'API Network Information disponible dans Chrome, puis envoyer un avertissement à vos systèmes d'analyse ou de métriques réelles des utilisateurs (RUM).

if(navigator.connection &&
    navigator.connection.type === 'cellular' &&
    navigator.connection.downlinkMax <= 0.115) {
    // Notify your service to indicate that you might be affected by this restriction.
}

Détecter les avertissements dans Chrome DevTools

Depuis Chrome 53, les outils pour les développeurs génèrent des avertissements pour les instructions document.write() problématiques. Plus précisément, si une requête document.write() répond aux critères 2 à 5 (Chrome ignore les critères de connexion lors de l'envoi de cet avertissement), l'avertissement se présente comme suit:

Avertissement d&#39;écriture de document.

Il est utile de voir des avertissements dans les outils pour les développeurs Chrome, mais comment les détecter à grande échelle ? Vous pouvez vérifier les en-têtes HTTP envoyés à votre serveur lors de l'intervention.

Vérifier vos en-têtes HTTP sur la ressource de script

Lorsqu'un script inséré via document.write a été bloqué, Chrome envoie l'en-tête suivant à la ressource demandée:

Intervention: <https://shorturl/relevant/spec>;

Lorsqu'un script inséré via document.write est détecté et qu'il peut être bloqué dans différentes circonstances, Chrome peut envoyer:

Intervention: <https://shorturl/relevant/spec>; level="warning"

L'en-tête d'intervention sera envoyé dans la requête GET du script (de manière asynchrone en cas d'intervention réelle).

Que nous réserve l'avenir ?

Le plan initial consiste à exécuter cette intervention lorsque nous détectons que les critères sont remplis. Nous avons commencé par afficher un avertissement dans la console de développement de Chrome 53. (la version bêta a été lancée en juillet 2016). La version stable devrait être disponible pour tous les utilisateurs en septembre 2016.)

Nous interviendrons pour bloquer les scripts injectés pour les utilisateurs 2G à partir de Chrome 54, qui devrait être disponible en version stable pour tous les utilisateurs vers la mi-octobre 2016. Pour en savoir plus, consultez la fiche sur l'état de Chrome.

À terme, nous souhaitons intervenir lorsque la connexion d'un utilisateur est lente (par exemple, 3G ou Wi-Fi lents). Suivez cette entrée sur l'état de Chrome.

Vous voulez en savoir plus ?

Pour en savoir plus, consultez les ressources supplémentaires suivantes: