L'objet XMLHttpRequest

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

XMLHttpRequest

Oui
avec ActiveX pour IE < 7

Oui

Oui

Oui

Oui

Introduction

Le principe de fonctionnement d'XMLHttpRequest est d'envoyer une requête HTTP vers le serveur, et une fois la requête envoyée, les données renvoyées par le serveur peuvent être récupérées. Pour ce faire, il faut disposer d'un objet disposant de cette fonctionnalité. Cet objet a été développé par Microsoft et implémenté dans Outlook puis dans Internet Explorer 5.5 en tant que contrôle ActiveX. Microsoft l'avait à l'époque nommé XMLHTTP.

Par la suite, les autres navigateurs suivirent et implémentèrent un objet appelé XMLHttpRequest. Cet objet fut implémenté avec les mêmes méthodes que celle d'XMLHTTP de Microsoft. Plus tard, XMLHttpRequest fut proposé au W3C en vue de devenir un standard.

A l'heure actuelle, les navigateurs récents (IE7, FF2, Opera 9, Safari...) implémentent tous cet objet.

Quelques liens

Instancier un objet XHR

Pour instancier (créer, déclarer) un objet XHR, on procède de la même façon que pour n'importe quel objet JavaScript à savoir avec le mot-clé new :

var xhr = new XMLHttpRequest();

Les versions d'Internet Explorer inférieures à la version 7 requièrent toujours une instanciation via un contrôle ActiveX . Il y a deux façons d'instancier un objet XHR avec un contrôle ActiveX et elles dépendent de la version d'XMLHTTP utilisée. Pour faire simple, on va utiliser un try...catch , l'instanciation indiquée dans le try étant la plus récente :

try {
var xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
var xhr = new ActiveXObject("Microsoft.XMLHTTP");
}

Pour faire un script homogène, rassemblons ce code en un seul, en prenant soin de tester la prise en charge des différentes méthodes d'instanciation :

var xhr = null;
if (window.XMLHttpRequest || window.ActiveXObject) {
if (window.ActiveXObject) {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
} else {
xhr = new XMLHttpRequest();
}
} else {
alert("Votre navigateur ne supporte pas l'objet XMLHTTPRequest...");
return;
}

Par la suite, j'utiliserai une fonction qui retournera l'objet XMLHttpRequest instancié, ce sera plus simple :

function getXMLHttpRequest() {
var xhr = null;
if (window.XMLHttpRequest || window.ActiveXObject) {
if (window.ActiveXObject) {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
} else {
xhr = new XMLHttpRequest();
}
} else {
alert("Votre navigateur ne supporte pas l'objet XMLHTTPRequest...");
return null;
}
return xhr;
}

Par la suite, pour instancier un objet XHR, il suffira de faire :

var xhr = getXMLHttpRequest();

Envoi d'une requête HTTP

Définir et envoyer

Dans un premier temps, il faut définir les modalités d'envoi avec la méthode open , et on l'enverra ensuite avec la méthode send :

var xhr = getXMLHttpRequest(); // Voyez la fonction getXMLHttpRequest() définie dans la partie précédente
xhr.open("GET", "handlingData.php", true);
xhr.send(null);

open s'utilise de cette façon : open(sMethod, sUrl, bAsync)

  • sMethod : la méthode de transfert : GET ou POST;

  • sUrl : la page qui donnera suite à la requête. Ça peut être une page dynamique (PHP, CFM, ASP) ou une page statique (TXT, XML...);

  • bAsync : définit si le mode de transfert est asynchrone ou non (synchrone). Dans ce cas, mettez true . Ce paramètre est optionnel et vaut true par défaut, mais il est courant de le définir quand même (je le fais par habitude).

Si vous utilisez la méthode POST, vous devez absolument changer le type MIME de la requête avec la méthode setRequestHeader , sinon le serveur ignorera la requête :

xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

Passer des variables

Vous avez la possibilité de passer des variables au serveur. Suivant la méthode d'envoi utilisée une petite différence fait son apparition.

Dans le cas de GET les variables sont transmises directement dans l'URL :

xhr.open("GET", "handlingData.php?variable1=truc&variable2=bidule", true);
xhr.send(null);

Pour POST, il faut spécifier les variables dans l'argument de send :

xhr.open("POST", "handlingData.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("variable1=truc&variable2=bidule");
Protéger les caractères

Avant de passer des variables, il est important de les protéger pour conserver les caractères spéciaux et les espaces. Pour cela, utilisez la fonction globale encodeURIComponent , comme ceci :

var sVar1 = encodeURIComponent("contenu avec des espaces");
var sVar2 = encodeURIComponent("je vois que vous êtes un bon élève... oopa !");
xhr.open("GET", "handlingData.php?variable1=" + sVar1 + "&variable2= " + sVar2, true);
xhr.send(null);

Traitement côté serveur

Dans mon exemple, la page handlingData.php reçoit la requête. Cette page est une page PHP, elle est donc dynamique, mais ce n'est pas obligatoire, il peut s'agir d'un fichier TXT ou même XML !

Une page PHP

Cette page se comporte comme une page PHP normale, il n'y a rien de spécifique. La récupération des variables se fait d'une façon classique (via $_GET ou $_POST, suivant la méthode d'envoi) et il ne faut pas perdre de vue de sécuriser la page, car un petit malin pourrait l'appeler directement sans passer par l'appel via XMLHttpRequest (il peut donc mettre directement l'URL de la page) :

<?php
header("Content-Type: text/plain"); // Utilisation d'un header pour spécifier le type de contenu de la page. Ici, il s'agit juste de texte brut (text/plain).
$variable1 = (isset($_GET["variable1"])) ? $_GET["variable1"] : NULL;
$variable2 = (isset($_GET["variable2"])) ? $_GET["variable2"] : NULL;
if ($variable1 && $variable2) {
// Faire quelque chose...
echo "OK";
} else {
echo "FAIL";
}
?>

Ce script ne fait évidemment rien d'intéressant, il vérifie juste si les deux variables sont renseignées. Si elles le sont, il écrit OK, et si elles ne le sont pas il écrit FAIL. C'est très important d'écrire quelque chose, car XMLHttpRequest va être capable de récupérer ce qui a été écrit, sous forme de texte brut (comme dans cet exemple) ou sous forme d'arbre XML. En écrivant OK ou FAIL, on pourra alors vérifier si le script a été correctement exécuté.

Une page statique

Comme je l'ai laissé entendre, il est possible d'utiliser des fichiers statiques. Dans le cas d'un fichier de texte (TXT), l'entièreté du texte sera récupéré, et s'il s'agit d'un fichier XML, on pourra récupérer la structure XML et l'utiliser directement.

Dans tous les cas, il faudra préciser dans quel format les données devront être récupérées. C'est ce que nous allons faire maintenant.

Récupération des données

Récapitulons : en 1, on a envoyé la requête, et en 2 la requête a trouvé des données à récupérer (les données fournies par le fichier PHP, TXT ou XML).

Le changement d'état

Il faut savoir que quand on envoie une requête HTTP via XMLHttpRequest, celle-ci passe par plusieurs états différents :

  • 0 : L'objet XHR a été créé, mais pas encore initialisé (la méthode open n'a pas encore été appelée)

  • 1 : L'objet XHR a été créé, mais pas encore envoyé (avec la méthode send )

  • 2 : La méthode send vient d'être appelée

  • 3 : Le serveur traite les informations et a commencé à renvoyer des données

  • 4 : Le serveur a fini son travail, et toutes les données sont réceptionnées

En vue de cela, il va donc nous falloir détecter les changements d'état pour savoir où en est la requête. Pour cela, on va utiliser la propriété onreadystatechange, et à chaque changement d'état (state en anglais), on regardera lequel il s'agit :

var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
alert("OK"); // C'est bon \o/
}
};
xhr.open("GET", "handlingData.php", true);
xhr.send(null);

On utilise readyState pour connaître l'état de la requête. En addition, nous devons aussi vérifier le code d'état (comme le fameux code 404 pour les pages non trouvées ou le code 500 pour l'erreur de serveur) de la requête, pour vérifier si tout s'est bien passé. Pour cela, on utilise la propriété status. Si elle vaut 200 ou 0 (aucune réponse, pratique pour les tests en local, sans serveur d'évaluation), tout est OK.

Récupérer les données

Rien de plus simple, il suffit d'utiliser une des deux propriétés disponibles :

  • responseText : pour récupérer les données sous forme de texte brut

  • responseXML : pour récupérer les données sous forme d'arbre XML

Un alert simple

L'utilisation est assez simple, il suffit de procéder comme ceci :

xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
alert(xhr.responseText); // Données textuelles récupérées
}
};
Une fonction de callback

Le alert c'est bien beau, mais à part vérifier si la réception est bonne, ça ne sert à rien. Si on s'amuse à récupérer des données, c'est pour les traiter ! Le traitement peut se faire directement dans la fonction anonyme définie dans onreadystatechange. Mais c'est très laid et ça n'a rien à faire là, il vaut mieux séparer les codes pour avoir deux fonctions concises plutôt qu'une grosse.

On va donc définir ce qu'on appelle une fonction dite "de callback". Une fonction de callback est une fonction de rappel, exécutée généralement après qu'un script a été exécuté. C'est pas très clair, mais c'est tout con en fait : il s'agit de passer à une fonction A le nom d'une fonction B qui sera lancée une fois que la fonction A aura été exécutée. Comme ceci en gros :

function funcA(callback) {
// instruction...
// instruction...
// instruction...
callback();
}
function funcB() {
alert("Plop");
}
funcA(funcB);

En me basant sur cet exemple de callback, il est donc facile de le transposer pour utiliser XMLHttpRequest :

function request(callback) {
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
callback(xhr.responseText);
}
};
xhr.open("GET", "handlingData.php", true);
xhr.send(null);
}
function readData(sData) {
// On peut maintenant traiter les données sans encombrer l'objet XHR.
if (sData == "OK") {
alert("C'est bon");
} else {
alert("Y'a eu un problème");
}
}
request(readData);

Premiers exemples

Illustrons ceci par deux exemples simples.

Exemple 1 : récupération d'un fichier XML

On va simplement lire le contenu d'un fichier XML, fichier que voici :

<?xml version="1.0" encoding="utf-8"?>
<!-- XMLHttpRequest_getXML.xml -->
<root>
<soft name="Adobe Dreamweaver" />
<soft name="Microsoft Expression Web" />
<soft name="Notepad++" />
<soft name="gedit" />
<soft name="Emacs" />
</root>
<!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 - XMLHttpRequest</title>
<script type="text/javascript" src="oXHR.js"></script>
<script type="text/javascript">
<!--
function request(callback) {
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
callback(xhr.responseXML);
}
};
xhr.open("GET", "XMLHttpRequest_getXML.xml", true);
xhr.send(null);
}
function readData(oData) {
var nodes = oData.getElementsByTagName("soft");
var ol = document.createElement("ol"), li, cn;
for (var i=0, c=nodes.length; i<c; i++) {
li = document.createElement("li");
cn = document.createTextNode(nodes[i].getAttribute("name"));
li.appendChild(cn);
ol.appendChild(li);
}
document.getElementById("output").appendChild(ol);
}
//-->
</script>
</head>
<body>
<p>
<button onclick="request(readData);">Afficher le fichier XML</button>
<div id="output"></div>
</p>
</body>
</html>

Test du code

En cliquant sur le bouton, on envoie la requête qui va se charger de récupérer les données, au format XML (avec responseXML donc). Ensuite, dans la fonction readData, on lit ces données et on crée une liste à puces que l'on va ajouter à l'élément appelé output.

Exemple 2 : envoi - traitement - réception

Ici, on va demander à l'utilisateur d'entrer son pseudonyme ainsi que son prénom. Ces deux données seront transmises à une page PHP qui renverra un texte avec les données envoyées. Ça n'a aucun intérêt comme ça, mais c'est pour vous donner une base simple.

Voici la page HTML :

<!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 - XMLHttpRequest</title>
<script type="text/javascript" src="oXHR.js"></script>
<script type="text/javascript">
<!--
function request(callback) {
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
callback(xhr.responseText);
}
};
var nick = encodeURIComponent(document.getElementById("nick").value);
var name = encodeURIComponent(document.getElementById("name").value);
xhr.open("GET", "XMLHttpRequest_getString.php?Nick=" + nick + "&Name=" + name, true);
xhr.send(null);
}
function readData(sData) {
alert(sData);
}
//-->
</script>
</head>
<body>
<form>
<p>
<label for="nick">Pseudo :</label>
<input type="text" id="nick" /><br />
<label for="name">Prénom :</label>
<input type="text" id="name" />
</p>
<p>
<input type="button" onclick="request(readData);" value="Exécuter" />
</p>
</form>
</body>
</html>

Test du code

Et la page de script PHP qui va traiter la requête et renvoyer la petite phrase :

<?php
header("Content-Type: text/plain");
$nick = (isset($_GET["Nick"])) ? $_GET["Nick"] : NULL;
$name = (isset($_GET["Name"])) ? $_GET["Name"] : NULL;
if ($nick && $name) {
echo "Bonjour " . $name . " ! Je vois que votre pseudo est " . $nick;
} else {
echo "FAIL";
}
?>

Test du code

Notez bien l'utilisation d'encodeURIComponent pour protéger les données transmises. Si tout se passe bien, un alert s'affichera avec la petite phrase renvoyée par le script PHP.

Contrôle d'état

Requête en cours, ne pas déranger

Grâce à readyState, on sait connaitre l'état de la requête. Il peut donc être intéressant de dire à l'internaute que la requête est en train d'être traitée et affichant un petit texte "Chargement en cours" ou bien une petite image de progression.

Si quand readyState vaut 4 tout est terminé, ça veut donc dire que si ça ne vaut pas 4, c'est en cours de traitement. De là, il n'y a qu'un pas pour afficher une image de progression comme on va le voir dans cet 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 - XMLHttpRequest</title>
<script type="text/javascript" src="oXHR.js"></script>
<script type="text/javascript">
<!--
function request(callback) {
var sleep = document.getElementById("sleep").value;
if (isNaN(parseInt(sleep))) {
alert(sleep + " n'est pas un nombre valide !");
return;
}
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
callback(xhr.responseText);
document.getElementById("loader").style.display = "none";
} else if (xhr.readyState < 4) {
document.getElementById("loader").style.display = "inline";
}
};
xhr.open("GET", "XMLHttpRequest_getSleep.php?Sleep=" + sleep, true);
xhr.send(null);
}
function readData(sData) {
alert(sData);
}
//-->
</script>
</head>
<body>
<form>
<p>
<label for="sleep">Temps de sommeil :</label>
<input type="text" id="sleep" />
</p>
<p>
<input type="button" onclick="request(readData);" value="Exécuter" />
<span id="loader" style="display: none;"><img src="loader.gif" alt="loading" /></span>
</p>
</form>
</body>
</html>

Test du code

Il s'agit simplement d'afficher l'image de progression contenue dans le spanloader. Pour être sur de bien le voir (car une simple requête comme ça prend moins d'une seconde), j'utilise une fonction sleep dans la page XMLHttpRequest_getSleep.php, page que voici :

<?php
header("Content-Type: text/plain");
$sleep = (isset($_GET["Sleep"])) ? $_GET["Sleep"] : NULL;
echo date("h:i:s") . "\n\n";
if ($sleep) {
sleep($sleep);
echo $sleep . "\n\n";
} else {
sleep(10);
echo "10\n\n";
}
echo date("h:i:s") . "\n";
?>

Une seule requête à la fois !

En considérant l'exemple précédent, que se passe-t-il si l'utilisateur clique plusieurs fois sur le bouton d'envoi ? Et bien la réponse est simple, la requête est envoyée plusieurs fois. Ce qu'il faudrait c'est trouver une astuce pour annuler la requête en cours si une nouvelle est envoyée.

Pour ce fait, il va falloir stocker l'objet XMLHttpRequest dans un objet global, xhr, et il n'y aura qu'à tester l'état de cet objet avant d'envoyer une nouvelle requête. Si l'état est différent de 0 (la requête est en cours), il suffit de l'annuler pour envoyer la nouvelle. On peut aussi décider de ne pas envoyer la nouvelle, au choix ça.

Annulation de la requête en cours
var xhr = null;
function request(callback) {
if (xhr && xhr.readyState != 0) {
xhr.abort(); // On annule la requête en cours !
}
xhr = getXMLHttpRequest(); // plus de mot clé 'var'
xhr.onreadystatechange = function() {
/* ... */
};
xhr.open("GET", "XMLHttpRequest_getSleep.php?Sleep=" + sleep, true);
xhr.send(null);
}
Annulation de l'envoi de la nouvelle requête
var xhr = null;
function request(callback) {
if (xhr && xhr.readyState != 0) {
alert("Attendez que la requête ait abouti avant de faire joujou");
return;
}
xhr = getXMLHttpRequest();
/* ... */
}

Exemple des listes liées

Les listes liées c'est quelque chose d'assez habituel : une première liste déroulante permet de choisir une option, et en fonction de ce choix, on affiche le contenu d'une deuxième liste. C'est assez simple et c'est un bon exercice.

Principe

Une première liste déroulante est affichée, proposant à l'utilisateur de choisir un éditeur de logiciels (c'est pour l'exemple). Quand l'utilisateur choisit un éditeur, on récupère, via XHR, la liste des logiciels qui correspondent à cet éditeur, et on l'affiche dans une deuxième liste déroulante.

La liste des éditeurs et des logiciels est enregistrée dans une base de données. Dans mon exemple, j'utilise une base mySQL. Voici d'ailleurs les requêtes SQL pour créer les deux tables que je vais utiliser :

CREATE TABLE IF NOT EXISTS `ajax_example_editors` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`name` varchar(50) collate latin1_general_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=7 ;
INSERT INTO `ajax_example_editors` (`id`, `name`) VALUES
(1, 'Adobe'),
(2, 'Macromedia'),
(3, 'Microsoft'),
(4, 'Mozilla'),
(5, 'ACDSystem'),
(6, 'Apple');
CREATE TABLE IF NOT EXISTS `ajax_example_softwares` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`name` varchar(50) collate latin1_general_ci NOT NULL,
`idEditor` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=21 ;
INSERT INTO `ajax_example_softwares` (`id`, `name`, `idEditor`) VALUES
(1, 'Photoshop', 1),
(2, 'GoLive', 1),
(3, 'InDesign', 1),
(4, 'Acrobat', 1),
(5, 'Dreamweaver', 2),
(6, 'Flash', 2),
(7, 'ColdFusion', 2),
(8, 'FlashPaper', 2),
(9, 'Silverlight', 3),
(10, 'Internet Explorer', 3),
(11, 'Office', 3),
(12, 'Visual Studio', 3),
(13, 'Camino', 4),
(14, 'Firefox', 4),
(15, 'Rhino', 4),
(16, 'Thunderbird', 4),
(17, 'ACDSee Photo Editor', 5),
(18, 'ACDSee Photo Manager', 5),
(19, 'iTunes', 6),
(20, 'Safari for Windows', 6);

La page HTML

En réalité, ce sera une page PHP, car je vais utiliser un script PHP pour afficher la liste des éditeurs dans la liste déroulante :

<div id="programBox">
<p id="editors">
<select id="editorsSelect" onchange="request(this);">
<option value="none">Selection</option>
<?php
mysql_connect($hote, $login, $m_d_p);
mysql_select_db($bdd);
$query = mysql_query("SELECT * FROM ajax_example_editors ORDER BY name");
while ($back = mysql_fetch_assoc($query)) {
echo "\t\t\t\t<option value=\"" . $back["id"] . "\">" . $back["name"] . "</option>\n";
}
?>
</select>
<span id="loader" style="display: none;"><img src="images/loader.gif" alt="loading" /></span>
</p>
<p id="softwares">
<select id="softwaresSelect"></select>
</p>
</div>

Du côté du code JavaScript pour envoyer la requête, c'est assez simple. Voici la fonction request, appelée via l'évènement onchange de l'élément <select>. Cette fonction se charge d'envoyer la requête.

function request(oSelect) {
var value = oSelect.options[oSelect.selectedIndex].value;
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
readData(xhr.responseXML);
document.getElementById("loader").style.display = "none";
} else if (xhr.readyState < 4) {
document.getElementById("loader").style.display = "inline";
}
};
xhr.open("POST", "XMLHttpRequest_getListData.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("IdEditor=" + value);
}

La fonction readData sera appelée et se chargera de remplir le <select> avec la liste des logiciels. J'en reparlerai après le code PHP.

La page de traitement PHP

<?php
header("Content-Type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
echo "<list>";
$idEditor = (isset($_POST["IdEditor"])) ? htmlentities($_POST["IdEditor"]) : NULL;
if ($idEditor) {
mysql_connect($hote, $login, $m_d_p);
mysql_select_db($bdd);
$query = mysql_query("SELECT * FROM ajax_example_softwares WHERE idEditor=" . mysql_real_escape_string($idEditor) . " ORDER BY name");
while ($back = mysql_fetch_assoc($query)) {
echo "<item id=\"" . $back["id"] . "\" name=\"" . $back["name"] . "\" />";
}
}
echo "</list>";
?>

C'est une simple récupération de base de données avec PHP. Il faut juste bien penser à formater comme étant du XML :

<?php
header("Content-Type: text/xml");
?>

Et s'arranger pour produire une structure XML valide !

Le code XML généré sera sous cette forme :

<?xml version="1.0" encoding="utf-8"?>
<list>
<item id="0" name="Programme A" />
<item id="1" name="programme B" />
...
</list>

Récupération du XML

Voici donc la fonction readData qui analyse les données XML et recrée les éléments <option> de la liste déroulante :

function readData(oData) {
var nodes = oData.getElementsByTagName("item");
var oSelect = document.getElementById("softwaresSelect");
var oOption, oInner;
oSelect.innerHTML = "";
for (var i=0, c=nodes.length; i<c; i++) {
oOption = document.createElement("option");
oInner = document.createTextNode(nodes[i].getAttribute("name"));
oOption.value = nodes[i].getAttribute("id");
oOption.appendChild(oInner);
oSelect.appendChild(oOption);
}
}

Le code complet

<!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 - XMLHttpRequest</title>
<script type="text/javascript" src="oXHR.js"></script>
<script type="text/javascript">
<!--
function request(oSelect) {
var value = oSelect.options[oSelect.selectedIndex].value;
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
readData(xhr.responseXML);
document.getElementById("loader").style.display = "none";
} else if (xhr.readyState < 4) {
document.getElementById("loader").style.display = "inline";
}
};
xhr.open("POST", "XMLHttpRequest_getListData.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("IdEditor=" + value);
}
function readData(oData) {
var nodes = oData.getElementsByTagName("item");
var oSelect = document.getElementById("softwaresSelect");
var oOption, oInner;
oSelect.innerHTML = "";
for (var i=0, c=nodes.length; i<c; i++) {
oOption = document.createElement("option");
oInner = document.createTextNode(nodes[i].getAttribute("name"));
oOption.value = nodes[i].getAttribute("id");
oOption.appendChild(oInner);
oSelect.appendChild(oOption);
}
}
//-->
</script>
</head>
<body>
<fieldset>
<legend>Programmes</legend>
<div id="programBox">
<p id="editors">
<select id="editorsSelect" onchange="request(this);">
<option value="none">Selection</option>
<?php
mysql_connect($hote, $login, $m_d_p);
mysql_select_db($bdd);
$query = mysql_query("SELECT * FROM ajax_example_editors ORDER BY name");
while ($back = mysql_fetch_assoc($query)) {
echo "\t\t\t\t<option value=\"" . $back["id"] . "\">" . $back["name"] . "</option>\n";
}
?>
</select>
<span id="loader" style="display: none;"><img src="images/loader.gif" alt="loading" /></span>
</p>
<p id="softwares">
<select id="softwaresSelect"></select>
</p>
</div>
</fieldset>
</body>
</html>

Test du code

Propriétés et méthodes

Pour terminer, voici une vue globale des propriétés et méthodes qui s'appliquent à l'objet XMLHttpRequest.

Les propriétés

Disponibilité

Description

onreadystatechange

Tous

Propriété exécutée à chaque changement d'état de la requête

readyState

Tous

Etat de la requête, à vérifier au sein du onreadystatechange pour savoir où en est le traitement de la requête

responseText

Tous

Réponse du serveur, au format texte

responseXML

Tous

Réponse du serveur, au format XML

status

Tous

Code de réponse du serveur (200, 404, 500...)

statusText

Tous

Le message associé à status

timeout

IE8, non-W3C

Permet de définir un time-out

Les méthodes

Disponibilité

Description

abort

Tous

Annule la requête en cours d'exécution

getAllResponseHeaders

Tous - IE7

Récupère la liste complète des en-têtes de la requête

getResponseHeader

Tous - IE7

Récupère l'en-tête de la requête

open

Tous

Définit les modalités d'envoi de la requête

overrideMimeType

Tous sauf IE, non-W3C

Permet de forcer le type MIME de la requête

send

Tous

Envoie la requête

setRequestHeader

Tous

Ajoute un en-tête HTTP manuellement

Conclusion

XMLHttpRequest est sans conteste le système AJAX qui offre le plus de marge de manœuvre, mais il se révèle lourd à déployer.

Points forts

  • Adapté pour envoyer une requête qui n'attend pas nécessairement de résultat

  • Adapté pour récupérer des données textuelles et XML

  • Supporté par tous les navigateurs

  • Possibilité de requêtes POST

Points faibles

  • Assez lourd à mettre en place et à manipuler

  • L'importation de données au format JSON requiert une compilation avec eval