Dynamic Script Loading

Image utilisateur Image utilisateur

Méthode

Image utilisateur Image utilisateur

Internet Explorer

Image utilisateur Image utilisateur

Firefox

Image utilisateur Image utilisateur

Opera

Image utilisateur Image utilisateur

Google Chrome

Image utilisateur Image utilisateur

Safari

Dynamic Script Loading

Oui

Oui

En partie
Obligation de charger un fichier .js

Oui

Oui

Introduction

Javascript possède une propriété intéressante qui est celle de pouvoir charger un fichier .js à partir d'un autre et ce à n'importe quel moment. Ainsi il est possible de charger une autre page de script à partir d'une première en insérant un élément <script />, comme le montre cet exemple :

<script type="text/javascript">
<!--
document.write("<script type=\"text\/javascript\" src=\"external.js\"><\/script>");
//-->
</script>

Javascript permet donc nativement, et depuis toujours, d'utiliser le principe de l'AJAX en appelant un fichier .js après que la page HTML a été chargée. Il est aussi possible d'appeler une page PHP, identifiée comme étant un fichier JS, ce qui permettra une bonne interaction avec le serveur. C'est ce que nous allons voir plus en détail ici.

Première approche

On va faire quelque chose de très simple pour commencer : on va créer un élément <script /> qui appellera un fichier JS et celui-ci qui répondra avec un alert. C'est basique, mais il faut que vous compreniez la base avant d'aller plus loin.

Le script donné ci-dessus marche bien, mais utilise la vieille méthode write(), ce qui est plutôt déconseillée et qui de plus ne nous est pas d'un grand intérêt. Pour ajouter un élément <script />, nous allons utiliser le DOM :

var DSLScript = document.createElement("script");
DSLScript.src = "DynamicScriptLoading_1.js";
DSLScript.type = "text/javascript";
document.body.appendChild(DSLScript);

Ce script crée et insère un élément <script /> avec un attribut src pointant vers la page JS (DynamicScriptLoading_1.js dans mon exemple), dans le body de la page HTML.

Ce petit bout de code, une fois placé appelle le fichier JS exactement comme si l'élément <script /> avait été écrit en HTML "brut". Le script contenu dans ce fichier JS est alors exécuté.

On va ajouter une fonction, appelée callback qui sera appelée par le fichier JS dès que ce dernier sera chargé. C'est en quelque sorte la réponse que le script de départ attend.

function callback(sMessage) {
alert(sMessage);
}

Le fichier JS (DynamicScriptLoading_1.js) contient juste l'appel de cette fonction :

callback("Hello world !");

En clair, on obtient quelque chose comme ça :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Dynamic Script Loading</title>
<script type="text/javascript">
<!--
function send() {
var DSLScript = document.createElement("script");
DSLScript.src = "DynamicScriptLoading_1.js";
DSLScript.type = "text/javascript";
document.body.appendChild(DSLScript);
document.body.removeChild(DSLScript);
}
function callback(sMessage) {
alert(sMessage);
}
//-->
</script>
</head>
<body>
<input type="button" value="Envoyer la requ&ecirc;te" onclick="send()" />
</body>
</html>

Test du code

Dès qu'on clique sur le bouton, la fonction send() est appelée. Cette dernière ajoute un élément <script /> lequel va charger un fichier JavaScript. Et pour finir, ce fichier JavaScript va appeler la fonction callback.

Pourquoi y a-t'il un document.body.removeChild() ?

En réalité ce type de chargement n'est pas asynchrone. Lorsque l'élément <script /> est inséré via appendChild(), le fichier JS est chargé. Quand il est chargé, removeChild() est exécutée pour supprimer l'élément <script /> qui ne nous est plus d'aucune utilité.

Je pense que vous avez compris le principe de base. On va pouvoir ajouter des variables et du PHP :) .

Avec des variables et du PHP

Maintenant, au lieu d'appeler un fichier JS, nous allons appeler une page PHP. PHP, comme vous le savez, permet de récupérer des variables transmises via l'URL (méthode GET). C'est ce que nous allons faire !

La transmission des variables se fait comme à l'accoutumée :

DynamicScriptLoading_2.php?variable1=valeur1&variable2=valeur2

Le script JS ne change presque pas. Ajouter simplement deux variables dans l'URL à placer dans l'élément <script /> :

DSLScript.src = "DynamicScriptLoading_2.php?Pseudo=Thunderseb&Prenom=Sebastien";

Pour ce qui est de la page PHP, c'est un peu plus « spécial », la voici :

<?php header("Content-type: text/javascript"); ?>
var sMessage = "Bonjour <?php echo $_GET['Prenom'] ?>, alias <?php echo $_GET['Pseudo'] ?> !";
callback(sMessage);

Gardez à l'esprit que c'est « comme » un fichier JavaScript, donc, tout ce qui n'est pas dans des balises <?php ?> est du code JavaScript !
Le code PHP peut être placé n'importe où, exactement comme si vous le placiez dans une page HTML, ce qui permet d'incorporer dans le code JavaScript.

Un code plus propre

Le code que je vous ai donné n'est pas super pratique à utiliser. Le mieux serait d'en faire des fonctions facilement déployables, acceptant un certain nombre de variables, une fonction de callback différente (qu'on ne soit pas obligé de l'appeler « callback »)...

Voici une fonction send générique :

function send(sUrl, oParams) {
for (sName in oParams) {
if (sUrl.indexOf("?") != -1) {
sUrl += "&";
} else {
sUrl += "?";
}
sUrl += encodeURIComponent(sName) + "=" + encodeURIComponent(oParams[sName]);
}
var DSLScript = document.createElement("script");
DSLScript.src = sUrl;
DSLScript.type = "text/javascript";
document.body.appendChild(DSLScript);
document.body.removeChild(DSLScript);
}

La fonction send reçoit, en plus de l'URL du fichier à charger, les variables à transmettre (c'est-à-dire à concaténer dans l'URL). L'argument oParams est un objet contenant une liste de clés/valeurs utilisant la notation JSON, comme ceci :

var oParams = {
"Pseudo": "Thunderseb",
"Prenom": "Sebastien",
"callback": "messageFromServer"
};

La valeur de la clé callback est tout simplement le nom de la fonction de callback à déclencher une fois la page PHP chargée. La notation JSON vous paraît peut-être étrange, mais c'est pratique pour transmettre une liste du type clé/valeur en une seule fois.

Et pour parcourir une telle structure, on utilise une boucle for associée au mot clé in, comme pour lister un tableau associatif. La fonction globale encodeURIComponent permet de coder les caractères spéciaux, pour ne pas avoir de problèmes lors de la transmission.

Le script de la page PHP peut alors être transformé de cette façon, de manière à appeler la fonction de callback (dont le nom est transmis dans la variable $_GET['callback']);

<?php header("Content-type: text/javascript"); ?>
var sMessage = "Bonjour <?php echo $_GET['Prenom'] ?>s alias <?php echo $_GET['Pseudo'] ?> !";
<?php echo $_GET['callback'] ?>(sMessage);

Et voilà, on a fait le tour de cette méthode. Elle est pratique, rapide et facile à déployer, mais comporte cependant un certain nombre de limitations. Par exemple, on ne sait utiliser que des requêtes GET, et non POST. Il n'y a non plus aucun moyen de savoir si la transmission s'est bien déroulée : si la page a renvoyé une erreur 404, on ne le sait pas.

Cela dit, Dynamic Script Loading est certainement le moyen le plus intéressant pour récupérer des données au format JSON. Voyons ça plus en détail !

DSL et le format JSON

JSON, ou JavaScript Object Notation est un format de structuration de données, un peu comme le XML. Mais la syntaxe est identique à la syntaxe objet de JavaScript. Ce tutoriel n'était pas un tutoriel de JSON, je ne reviendrai pas sur la structure du format.

Comme l'illustre l'exemple ci-dessus, il est très facile de récupérer une variable, et cette variable peut très bien être un objet JSON. Deux avantages de l'importation d'un objet JSON via Dynamic Script Loading peuvent être les suivants :

  • Facilité de mise en place

  • L'objet récupéré est de type object, et non de type string. Il n'y a donc pas besoin d'utiliser eval ou l'objet natif JSON pour parser la chaîne de caractères, ce qui est plutôt bénéfique. Eval is evil .

Charger un objet JSON statique

Tout d'abord, voici un exemple de code JSON, que je vais placer dans un fichier .js (ou .json, ça revient au même) :

var oSoftwares = {
"Adobe": [
"Acrobat",
"InDesign",
"Photoshop"
],
"Macromedia": [
"Dreamweaver",
"Flash",
"FreeHand"
],
"Microsoft": [
"Office",
"Visual C++",
"Windows Media Player"
]
};
callback(oSoftwares);

Test du code

La fonction callback va donc servir à récupérer l'objet JSON oSoftwares. Il suffira ensuite de traiter ces données et de les afficher. Voici donc ma fonction callback :

function callback(oJson) {
var tree = "", nbItems;
for (sItem in oJson) {
tree += sItem + "\n";
nbItems = oJson[sItem].length;
for (var i=0; i<nbItems; i++) {
tree += "\t" + oSoftwares[sItem][i] + "\n";
}
}
alert(tree);
}

Bien évidemment, il n'y a pas besoin de faire appel à la fonction send compliquée vue ci-dessus, puisque je ne souhaite pas envoyer de variables au fichier JavaScript.

Voici donc le code complet de l'exemple :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Techniques AJAX - Dynamic Script Loading - JSON Statique</title>
<script type="text/javascript">
<!--
function send(sUrl) {
var DSLScript = document.createElement("script");
DSLScript.src = sUrl;
DSLScript.type = "text/javascript";
document.body.appendChild(DSLScript);
document.body.removeChild(DSLScript);
}
function callback(oJson) {
var tree = "", nbItems;
for (sItem in oJson) {
tree += sItem + "\n";
nbItems = oJson[sItem].length;
for (var i=0; i<nbItems; i++) {
tree += "\t" + oSoftwares[sItem][i] + "\n";
}
}
alert(tree);
}
function getInfo() {
send("DynamicScriptLoading_JSON_1.js");
}
//-->
</script>
</head>
<body>
<p id="main">
<input type="button" value="Récupérer les données" onclick="getInfo();" />
</p>
</body>
</html>

Test du code

Charger un objet JSON dynamique

Il n'y a rien de bien compliqué non plus. La différence avec le code précédent est que la page appelée est une page PHP qui renvoie du code JavaScript, dont voici le code :

<?php
header("Content-type: text/javascript");
echo 'var oSoftwares = {
"Adobe": [
"Acrobat",
"InDesign",
"Photoshop"
],
"Macromedia": [
"Dreamweaver",
"Flash",
"FreeHand"
],
"Microsoft": [
"Office",
"Visual C++",
"WMedia Player"
]
};';
echo $_GET['callback'] ?>(oSoftwares);

Et voici le fichier HTML. Pour différer un tantinet de l'exemple précédent, j'ai incorporé le script de gestion avec les variables, c'est plus distrayant :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Techniques AJAX - Dynamic Script Loading - JSON Dynamique</title>
<script type="text/javascript">
<!--
function sendDSL(sUrl, oParams) {
for (sName in oParams) {
if (sUrl.indexOf("?") != -1) {
sUrl += "&";
} else {
sUrl += "?";
}
sUrl += encodeURIComponent(sName) + "=" + encodeURIComponent(oParams[sName]);
}
var DSLScript = document.createElement("script");
DSLScript.src = sUrl;
DSLScript.type = "text/javascript";
document.body.appendChild(DSLScript);
document.body.removeChild(DSLScript);
}
function callback(oJson) {
var tree = "", nbItems;
for(sItem in oJson) {
tree += sItem + "\n";
nbItems = oJson[sItem].length;
for(var i=0; i<nbItems; i++) {
tree += "\t" + oSoftwares[sItem][i] + "\n";
}
}
alert(tree);
}
function getInfo() {
var oParams = { "callback": "callback" };
sendDSL("DynamicScriptLoading_JSON_2.php", oParams);
}
//-->
</script>
</head>
<body>
<p id="main">
<input type="button" value="Récupérer les données" onclick="getInfo();" />
</p>
</body>
</html>

Test du code

Conclusion

Le Dynamic Script Loading est donc un moyen simple et efficace de récupérer des données sous forme de variables (données textuelles) et d'objets JavaScript (données formatées en JSON).

Points forts

  • Facilité de mise en place

  • Intégration parfaite avec les données JSON

Points faibles

  • Un peu tordu à utiliser

  • Limité à du texte et à du JSON. On peut récupérer aussi des données XML, mais ça implique des méthodes de création avec DOMImplementation (ce qui est assez inutile)

  • Seulement des requêtes GET