Archives mensuelles : janvier 2012

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.