Aller au menu - Aller au contenu

Icône L'objet XMLHttpRequest

Mise à jour : 07/12/2010
Difficulté : Intermédiaire Intermédiaire
5 614 visites depuis 7 jours, dont 1 368 sur ce chapitre, classé 34/795
Chapitre rédigé par Thunderseb

Compatibilité de la méthode
Image utilisateur
Méthode
Image utilisateur
Internet Explorer
Image utilisateur
Firefox
Image utilisateur
Opera
Image utilisateur
Google Chrome
Image utilisateur
Safari
XMLHttpRequest
Oui
avec ActiveX pour IE < 7
Oui
Oui
Oui
Oui

Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

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.

Pour la suite du tutoriel, j'abrègerai XMLHttpRequest par XHR, c'est plus simple.


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 :

Code : JavaScript
1
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 :

Code : JavaScript
1
2
3
4
5
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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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;
}


Pour la suite de ce chapitre, je placerai cette fonction dans un fichier nommé oXHR.js pour ne pas devoir la réécrire à chaque fois et ainsi alléger la lecture des codes. Vous pouvez réutiliser ce fichier pour vos tests.



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

Code : JavaScript
1
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 :

Code : JavaScript
1
2
3
4
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 n'êtes pas familier avec synchrone et asynchrone, j'explique tout ça dans le chapitre précédent !


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 :

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


Cette dernière ligne doit être placée après la ligne contenant la méthode open !


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 :

Code : JavaScript
1
2
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 :

Code : JavaScript
1
2
3
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 :

Code : JavaScript
1
2
3
4
5
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) :

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 :

Code : JavaScript
1
2
3
4
5
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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 :

Code : XML
1
2
3
4
5
6
7
8
9
<?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>


C'est un fichier tout simple, contenant une liste d'éditeurs pouvant servir à écrire du Javascript.


Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!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>


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.

Nous ferons par après un exemple avec du XML généré au moyen de PHP.


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 :

Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!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>


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

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?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";
}

?>


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.

D'ailleurs si vous voulez utiliser une image de progression, je vous conseille de vous rendre sur ajaxload.info qui permet d'en générer.


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 :

Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!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>


Il s'agit simplement d'afficher l'image de progression contenue dans le span loader. 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 :

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?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";

?>


Si votre hébergement, comme le mien est chez Free.fr, la fonction sleep est désactivée. Il faut donc utiliser une petite entourloupette pour la simuler :

Secret (cliquez pour afficher)
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php 

header("Content-Type: text/plain");

$sleep = (isset($_GET["Sleep"])) ? $_GET["Sleep"] : NULL;

echo date("h:i:s") . "\n\n";

if ($sleep) {
	fsleep($sleep);
	echo $sleep . "\n\n";
} else {
	fsleep(10);
	echo "10\n\n";
}
echo date("h:i:s") . "\n";


function fsleep($sec) { // Pour chez Free
    $sec = abs((int) $sec) ;
    $tps = time();
    while ($tps + $sec >= time()) ;
}

?>


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



Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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



Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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 :

Code : SQL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 :

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<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.

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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



Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?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 :

Code : PHP
1
2
3
<?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 :

Code : XML
1
2
3
4
5
6
<?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 :

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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



Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!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>

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



Propriétés de l'objet XMLHttpRequest
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



Méthodes de l'objet XMLHttpRequest
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
Chapitre précédent Sommaire Chapitre suivant

Partager

93 commentaires pour "L'objet XMLHttpRequest"
Note moyenne : 3.61 / 4 (137 votes)
Pseudo Commentaire
Hors ligne int main(void) # Posté le 30/05/2011 à 16:34:43

Citation : Thunderseb
utile s'il y a plusieurs requêtes AJAX sur une même page et que chaque requête nécessite un traitement différent.


Ah oui là tout de suite je vois mieux. :D Dans ce cas je suis d'accord, mais c'est vrai que je n'y avais pas pensé au départ (peut-être le préciser dans le tuto ?)
Hors ligne hooker7 # Posté le 10/07/2011 à 22:18:22
Le mieux est l'ennemi du bien.
Avatar

Bonjour,
j'ai recopier le code de l'exemple avec les listes liés.
j'ai beau regarder les commentaites ou essayer de chercher a debuguer,
mais je trouve pas l'erreur, voici mon code:

la page formulaire_ajout.php

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<!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="bibliotheque/xmlh.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", "donnees_ajouter.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("libelle"));
		oOption.value = nodes[i].getAttribute("numero");
		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			
					require("bibliotheque/connecter.php");
					require("bibliotheque/connection.php");
					$connection=connection(serveur,user,password,bd);
					$requete = mysql_query("SELECT * FROM entreprises");
					while ($donnees = mysql_fetch_assoc($requete)) {
						echo "\t\t\t\t<option value=\"" . $donnees["numero"] . "\">" . $donnees["libelle"] . "</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>


la page donnees_ajout.php

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?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) {
	require("bibliotheque/connecter.php");
	require("bibliotheque/connection.php");
	$connection= connection(serveur,user,password,bd);
	$requete = mysql_query("SELECT * FROM clients WHERE entreprise=" . mysql_real_escape_string($idEditor) . " ");
	while ($donnees = mysql_fetch_assoc($requete)) {
		echo "<item id=\"" . $donnees["numero"] . "\" name=\"" . $donnees["libelle"] . "\" />";
		alert($donnees);
	}
}

echo "</list>";
?>


et la page xmlh.js

Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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;
}


je comprend a peut pret le fonctionnement, seulement,
ma premier liste deroulante s'affiche bien,
lorsque je la selectionne change la valeur,
le chargement marche, mais la seconde liste n'ai pas mise a jour,

lorsque je fait un alert de readData (oData)
voici ce que j'ai [object XMLDocument]
et lorsque je met un alert dans mon donnees_ajout.php,
rien ne s'affiche.
comme si le fichier n'etait pas appeler,

donc il y a peut être une erreur ici ?

xhr.open("POST", "donnees_ajouter.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("IdEditor=" + value);

( d'ailleurs je ne comprend pas pourquoi on passe en parametre
le value après avec exécuter la fonction readData (oData).

si certain on deja utiliser se code qui marche,
pourriez vous m'envoyer vos exemple de code,
je pourrait mieux voire mon erreur.
Merci d'avance pour votre aide.

La connaissance s'accroît quand on la partage. ( Socrate )
 
Connecté cqu13 # Posté le 26/08/2011 à 18:11:25

Bonjour à tous
moi j'ai le même prb!!
je souhaite faire fonctionner l'exemple pour les listes liés, j'ai créé les 2 tables, copier le code complet PHp , créer le script oXHR, mais la seconde liste déroulante ne se met pas à jour.
Il n'y a aucune erreur.
qlq'un peut-il définir les fichiers à créer avec le code dedans svp (oui oui je débute et je veux faire des trucs de grand mais c'est ça qui fait avancer !)

merci d'avance
Hors ligne Super_carotte # Posté le 02/12/2011 à 17:05:53

Ville : Andernos
Pays : France métropolitaine

Salut,

Je lis ce tuto (félicitation ;) ) mais il faudrait juste faire une petite MAJ sur cette ligne

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

C'est dans la page:
http://www.siteduzero.com/tutoriel-3-1 [...] prequest.html

Passer a ie9 etc... et ajouter chrome :p

Merci encore,
Benjamin.
Hors ligne Nesquik69 # Posté le 02/12/2011 à 17:12:17
Slave of the Rythm
Avatar
Groupe : Auteurs

Ville : Tassin la demi-lune
Pays : France métropolitaine

Il est maintenant peu probable que nous mettions ce chapitre à jour car nous avons réécrit ce chapitre pour le cours de Javascript du SdZ, je t'invite à consulter la partie Ajax à cette adresse ;) : http://www.siteduzero.com/tutoriel-3-3 [...] l#part_557809

À noter que le nouveau cours est bien plus complet en ce qui concerne l'XHR !

Mes sites web : Plune | It's Friday!
Mes tutos : Javascript | Ajax | Code::Blocks
Mes projets : SpeedTest | Boxifier | zUploader
Articles : Breakpoint 2007 / 2008 / 2009
Useless : Mangas | Caramelldansen | Monde parallèle acidifié | Tydax
Des suggestions ? Oui !
 

Voir tous les commentaires