Comprendre document.write() en JavaScript

S’il est bien une méthode méconnue en JavaScript, c’est la méthode write() de l’objet document.

La syntaxe est pourtant simple :
document.write(arg1, arg2, ..., argn);
Les arguments attendus sont des chaînes de caractères (ou tout du moins des arguments possédant une méthode toString())
Vous noterez au passage que le nombre de paramètres que l’on peut passer à cette méthode est indéterminé.
L’instruction précédente aura pour effet d’ajouter la valeur de chaque paramètre dans la page.
Par exemple
document.write('toto', 12, [1,2,3]);
affichera :

toto121,2,3

Notez que chaque argument est ajouté à la suite du précédent sans insérer de séparateur.
Malgré tout, certaines valeurs affichent un résultat inattendu :
document.write(false, {toto: 'tata', foo: 'bar'});
affichera :

false[object Object]

c’est-à-dire qu’un booléen ne sera pas transformé en 0 ou 1 mais en false ou true et un objet affichera (en fonction du navigateur) [object Object].

Jusque là, rien de vraiment compliqué à comprendre, d’autant que l’on passe habituellement un seul paramètre correspondant à une chaîne à afficher.

Cependant, le comportement de cette méthode dépend de l’état du document au moment de son appel.
En effet, un document HTML peut avoir deux états distincts : ouvert ou fermé.
Lorsque le navigateur reçoit un contenu HTML à afficher, il commence par ouvrir un document (le flux) puis interprète le code HTML en affichant son contenu et en construisant le DOM et une fois l’intégralité du code interprété, il ferme le document.
Pendant que le document est ouvert, si le navigateur rencontre une instruction JavaScript write(), alors ses arguments sont insérés à l’emplacement de cette instruction dans le code.
C’est le comportement attendu de cette méthode.
Mais il faut savoir que document.write() a besoin que le document soit ouvert pour pouvoir fonctionner correctement. Que se passe-t-il alors si le document est fermé ?
Dans ce cas, JavaScript va appeler en interne l’instruction document.open() puis exécuter le write().
Or document.open() correspond à l’ouverture du flux d’affichage, ce qui signifie que l’appel à cette méthode va recréer un nouveau flux, en commençant par supprimer l’existant.
Pour vous en convaincre, ouvrez une console JavaScript (par exemple Firebug pour Firefox) et dans la ligne de commande, tapez l’instruction
document.open();
et vous constaterez que la page va devenir blanche. Au passage, vous noterez aussi que l’indicateur de chargement se met en place (à la place du favicon), ce qui est le cas lorsque le flux est ouvert.
De la même manière, si vous entrez
document.write('Hello world !');
dans la console, vous constaterez que l’ensemble du contenu a été remplacé par le message « Hello world ! ». Vous pourrez aussi vérifier que l’indicateur de chargement est toujours actif : le document n’a pas été refermé.
Si ensuite vous entrez l’instruction
document.close();
dans la console, l’indicateur de chargement disparaitra au profit du favicon par défaut.

La première impression que l’on pourrait avoir en comprenant ce fonctionnement serait de se dire qu’il faut effectivement faire attention au contexte avant d’utiliser cette méthode.
En fait, il faut surtout considérer write() comme une méthode à bannir.
Déjà, parce qu’une méthode dont on ne peut garantir le comportement n’est pas à recommander.
Ensuite, parce que les bonnes pratiques en développement Web préconisent de séparer les couches : HTML pour l’affichage, CSS pour la mise en forme et JavaScript pour le comportement : utiliser la méthode write() implique d’introduire au niveau de l’affichage du JavaScript qui n’a rien à y faire, surtout que pour manipuler le document, de nombreuses méthodes du DOM sont disponibles et beaucoup plus fiables.
On pourrait aussi évoquer le fait que l’utilisation de write() interdit l’accès aux informations à ceux qui, pour une raison quelconque, ont JavaScript désactivé. Mais surtout, son utilisation (et son existence) force les navigateurs à une pratique particulièrement désagréable et dommageable qui est le chargement des scripts de façon synchrone.
En effet, l’affichage de la page doit être garantit par le navigateur, or si un script possède une instruction write(), alors il est important de pouvoir prévoir de façon certaine à quel endroit du document ses paramètres seront affichés, c’est pour cela que les navigateurs bloquent l’interprétation de la page pendant le chargement et l’évaluation des scripts (à la différence des autres éléments de la page, comme les images ou les iframes) ce qui retarde d’autant l’affichage du reste du contenu.

4 réflexions au sujet de « Comprendre document.write() en JavaScript »

  1. Avatar de palnappalnap

    Et ça marche encore mieux avec le code dans les accolades plutôt que dans les parenthèses ;)

    (function(){
    var sc = document.createElement(‘script’);
    sc.type = ‘text/javascript';
    sc.src = ‘url_du_script';
    document.getElementsByTagName(‘script’)[0].insertBefore(sc);
    })();

  2. Avatar de bovinobovino Auteur de l’article

    Salut Guillaume.
    Effectivement, pour les scripts dont tu parles, il n’y a pas forcément le choix dans la mesure où le code d’intégration (sauf erreur de ma part) est contractuel.

    Pour autant, il existe une astuce pour ajouter des scripts dans la page de façon asynchrone (donc non bloquante pour le navigateur).
    Il s’agit en fait de créer la balise script via le DOM, avec document.createElement(‘script’); (c’est d’ailleurs la méthode désormais utilisée par Google notamment pour analytics).
    Un exemple complet serait :

    (function(){&nbsp;<br />
    &nbsp; var sc = document.createElement('script');&nbsp;<br />
    &nbsp; sc.type = 'text/javascript';&nbsp;<br />
    &nbsp; sc.src = 'url_du_script';&nbsp;<br />
    &nbsp; document.getElementsByTagName('script')[0].insertBefore(sc);&nbsp;<br />
    })();

    Quelques remarques sur ce script.

    • La notation (function(){})(); permet de lancer l’exécution de la fonction immédiatement après son interprétation par le compilateur JavaScript (et accessoirement de protéger les noms de variables).
    • L’endroit où l’on intègre la balise script nouvellement créée est délicat : un document HTML ne possède pas obligatoirement de balises head ou body et si l’on veut que le code soit le plus portable possible, on ne connait pas nécessairement la structure de la page. Malgré tout, on peut être sûrs que la page possède au moins une balise script (celle qui a servi à appeler la fonction), c’est pourquoi on intègre la nouvelle par référence à celle-ci.

    Edité suite à la remarque de palnap, merci à lui !

  3. Avatar de YoguiYogui

    Je suis de ton avis sur tous les points : la méthode write est à éviter, et la majorité (si non la totalité) des traitements de la page doivent être faits par le serveur, non par le navigateur.
    Cependant, j’aimerais avoir ton avis dans le cas d’un site optimisé (j’entends par là qu’il est parvenu à réduire le temps de chargement de ses pages) mais qui utiliserait des scripts externes (analytics, marketing, retargeting, sonde, régie, etc.). Le webmestre n’a pas le contrôle sur ces scripts, et les mettre dans une iframe peut donner des résultats indésirables : comment faire lorsque l’un de ces scripts appelle document,write() ?
    Je parle bien sûr des sites ayant injecté à l’aide d’un timer les scripts externes après la fin du chargement des ressources sous leur contrôle.

Laisser un commentaire