Depuis le début de ce tutorial, l'ensemble des approches et des exercices ne porte que sur la manipulation des données reçues par le navigateur lors de la première requête.
Nous allons voir maintenant, comment alimenter à nouveau le document par des données fraichement cueillies sur le serveur.
Il existe une simple complexité qui est qu'en fonction du navigateur, l'objet XMLHttpRequest n'est pas identique. Mais une fois ce filtrage passé, le reste des opérations est identique quelques soit le navigateur, de même que les méthodes et propriétés..
Méthodes et propriétés de l'objet
L'objet XMLHttpRequest, comme l'activeX, possèdent une série de méthodes et propriétés nous permettant de dialoguer avec le serveur et d'analyser l'ensemble de ces échanges.
| Méthodes | Propriétés |
|---|---|
| open() | status |
| send() | statusText |
| abort() | readyState |
| setRequestHeader() | onreadystatechange |
| getResponseHeader() | responseText |
| getAllResponseHeader() | respondeXML |
Nous allons explorer au cours de ce tutorial la majeure partie de ces méthodes et propriétés.
Instantiation de l'objet
Comme nous venons de le préciser juste précédemment, en fonction du navigateur, l'objet à instantier n'est pas le même. Seul Internet Explorer n'utilise pas le même objet, il utilise un ActiveX. Et encore, Internet Explorer lui même se crée des différences, en fonction de la version du navigateur. Il peut utiliser deux versions différentes de l'activeX : avant IE 7 il utilise Microsoft.XMLHTTP et à partir de IE 7 Msxml2.XMLHTTP.
Il existe également plusieurs versions de Msxml2.XMLHTTP. Jeremy Keith dans son ouvrage Bulletproof AJAX, propose une approche basée sur une série de try catch imbriqués qui apporte une solution à cette problématique. Vous trouverez d'autres pistes et approches sur les liens suivants :
Le principe de base du filtrage consiste à utiliser une variable témoin initialisée sur false au départ. En fonction du filtre, cette variable prendra la valeur de l'instance de l'objet XMLHttpRequest, ou restera à sa valeur false.
On commence donc par tester si l'objet XMLHttpRequest est disponible, s'il ne l'est pas, on teste alors la présence et l'activation des activeX. En fonction de ce résultat, on entre dans une série de try / catch, afin d'utiliser l'activeX la mieux appropriée.
Enfin, une fois l'ensemble des tests effectués, on retourne la valeur de la variable contenant alors soit un pointeur sur l'instance de l'objet / activeX XMLHttpRequest, soit sa valeur de départ false.
function creeObjetHTTP(){
var xhr = false
if (window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject){
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
xhr = false;
}
}
}
return xhr
}
Certains scripts que vous pourriez trouver sur le Web utilisent une syntaxe particulière qui englobe certaines parties du code par /*@ et @*/. En fait, il s'agit d'une compilation conditionnelle introduite par Internet Explorer 5, et donc seules les versions égales ou supérieures à Internet Explorer 5 l'interprètent.
L'implémentation de Javascript dans Internet Explorer version 4, ne prenant pas encore en compte la structure Try / Catch, il faut éviter une erreur de script en utilisant le principe du code placé entre commentaires et non pris en compte par Internet Explorer 4.
Un script plus complet peut prendre en charge les diverses versions des navigateurs en utilisant les divers objets XMLHTTP correspondants à ces versions. L'idée est alors d'utiliser un tableau contenant chacun de ces objets et au travers d'une boucle, de les essayer de manière descendantes un par un, jusqu'à trouver la version adéquate.
Attention lors de tests de versions antérieures à la version 5 il faut penser au hack évoqué précédemment :
var xhr = false
if (window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
var success = false;
var ieXHR = new Array(
"Msxml2.XMLHTTP.7.0","Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP");
for (var I=0; I<ieXHR.length && !success; I++) {
try{
xhr = new ActiveXObject(ieXHR[I]);
success = true;
//break;
}catch(e){
}
} }
}
return xhr;
Une fois la requête appellée, il ne reste plus qu'à s'assurer qu'une instance d'objet ou d'activeX XMLHttpRequest à bien été renvoyée. Un simple test sur true ou false suffit amplement.
requete = creeObjetHTTP()
if (requete != false) {
// il existe bien une instance
}
Envoyer une requête
L'envoi d'une requête se fait en deux temps, d'une part la préparation et l'ouverture de la requête au travers de la méthodes open(), et d'autre part, l'envoi même de la requête par la méthode send().
Préparation et ouverture de la requête, méthode open()
La méthode open(a,b,c,d,e) possède 5 paramètres, dont deux facultatifs (d et e), et s'applique à l'instance de l'objet XMLHttpRequest.
requete.open("GET","fichierACharger.extension",true,login,password)
a
Le premier paramètre représente la méthode utilisée pour envoyer les paramètres GET ou POST. Généralement, la méthode GET est utilisée lorsqu'aucun paramètre n'est envoyé et que seulement une réponse est attendue.
Sinon la méthode GET envoie les données vers le serveur à la suite de l'URL, de la manière page.php?param=lasuite... : de ce fait, les données peuvent facilement être lues lors de l'appel du lien, et elles sont limitées à 256 caractères.
b
Ce deuxième paramètre contient l'adresse ciblée par la requête. Il peut s'agir d'une adresse absolue, relative au document ou relative à la racine du site.
c
Le troisième paramètre permet de gérer l'aspect asynchrone de la requête. Soit on décide de bloquer la suite du programme en attendant la réception des données (on affecte alors true au paramètre), soit on continue la suite du processus, en attendant que l'ensemble des données soit parvenues (on affecte false au pramètre). Généralement on initie ce paramètre sur true.
d et e
Bien que peu utilisées, car n'importe qui peut accéder très facilement à ces paramètres, il est possible de passer deux valeurs pour login et password. Cela peut être utile pour authentifier la provenance d'un formulaire ou d'une requête particulière.
Envoi de la requête, méthode send()
La nature de l'unique paramètre de cette méthode dépend du fait qu'il y ait ou pas des paramètres à envoyer. Si aucun paramètre n'est envoyé, il faut passer null.
requete.send(null)
Si des paramètres sont envoyés, il va falloir au préalable définir l'ensemble des headers nécessaires au protocole d'envoi. La méthode setRequestHeader() est là pour remplir cette tâche. Vous trouverez une liste assez complète, ainsi que la description des divers entête HTTP disponibles sur le site du w3c.
Toutefois, seule la définition d'encodage est au minimum requise, l'encodage par défaut en encodage URL peut être définie. Vous pouvez en savoir plus en vous rendant encore une fois sur le site du w3c ou sur le site de yoyo design.
requete.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Attention, ce n'est pas le tout que de définir le type d'encodage, il faut également encoder les données transmises. C'est à dire remplacer les caractères spéciaux par des caractères de type URL, voici dans le tableaux ci-dessous les principaux carcatères. Vous trouverez de plus amples informations sur le site de permadi.com. Vous trouverez également un large tableau de correspondance sur le site de lab.artlung.com.
| caractères | caractères encodés |
|---|---|
| espace | + |
| & | %26 |
| " | %27 |
| ' | %22 |
| % | %25 |
| ( | %28 |
| ) | %29 |
| ~ | %7E |
Affectation de la fonction de récupération.
A chaque changement d'état de la requête, une information est renvoyée au client. Il faut dès le départ initialiser une fonction qui va se charger de récupérer cette information et l'analyser. Il suffit simplement d'affecter à la propriété onreadystatechange de l'objet requête, une fonction de réception. Attention à ne pas utiliser de parenthèse à ce moment-là.
requete.onreadystatechange = fonction_reception
Récupération d'une requête
Vérification de l'état de récupération des données
Il existe une propriété readyState qui reflète à chaque changement l'état de la requête AJAX en cours. La valeur est numérique et correspond généralement, en fonction des navigateurs, aux valeurs énumérées dans le tableau ci-dessous :
| valeur de readyState | état | interprétation |
|---|---|---|
| 0 | Uninitialized | La méthode open() n'a pas encore été interpellée |
| 1 | Loading | La méthode open() a été interpellée mais pas la méthode send() |
| 2 | Loaded | La méthode send() a été interpellée |
| 3 | Interactive | Le server a commencé à envoyer une réponse |
| 4 | Complete | Le serveur a terminé l'envoi de la réponse |
Bien que certains navigateurs diffèrent sur la manière d'interpréter les états de la requête AJAX, tous s'accordent à ce que la propriété readyState vale 4, lorsque le serveur a terminé d'envoyer sa réponse. De ce fait, il suffit de vérifier si cette propriété équivaut 4, pour s'assurer de la bonne réception des informations attendues.
function fonction_reception(){
if (requete.readyState == 4){
// les informations ont été renvoyées par le serveur
}
}
Status de réception
Bien que le serveur atteste de l'envoi des données, rien ne nous garantit que les données signifient quelque chose pour le contenu de notre page web. Il faut donc s'assurer du statut contenu dans les entêtes envoyées par le serveur.
Vous connaissez les classiques messages 404 ou 500 du protocole HTTP... et après tout AJAX n'utilise qu'une partie de ce protocole. Nous devons donc nous assurer que le statut du retour de notre requête soit bien le bon, c'est à dire 200.
Attention, certains navigateurs peuvent renvoyer un statut 304, qui correspond à une version de document qui n'a pas été modifié depuis la dernière utilisation et donc, qui peut être récupéré dans le cache.
Voici ci-dessous un tableau reflètant les principaux statuts HTTP. Vous pourrez également en consulter la liste complète sur le site du w3c.
| status | interprétation |
|---|---|
| 200 | ok |
| 304 | Not Modified |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Internal Server Error |
Nous pouvons donc compléter notre fonction avec un filtre suplémentaire qui s'assurera que le statut renvoyé soit bien 200 ou éventuellement 304, avant de poursuivre :
function fonction_reception(){
if (requete.readyState == 4){
if (requete.status == 200 || requete.status == 304){
// les informations ont été renvoyées par le serveur et le status est correct
}
}
}
Exploitation des données reçues
Il existe deux formats de réception des données, et donc deux méthodes de réception qui vont de pair. Ne pas confondre les formats de réception qui sont définis en fonction du type MIME utilisé par le serveur avec la structure des données renvoyées qui peuvent, quelque soit le format, structurer les données. En clair il est possible de recevoir un document de type XML envoyé comme du format TXT...
Pour vous en assurer, vous pouvez utiliser la méthode getResponseHeader("Content-Type") appliquée à l'objet de requête.
Vous obtiendrez alors : soit text/plain auquel cas il faut utiliser la propriété objet.responseText, soit un autre type de contenu comme text/xml application/xml, application/javascript, text/html ... et là il est également possible de faire appel à la propriété objet.responseXML. Pour notre exemple, ajoutez simplement la ligne de code suivante à notre filtre précédent :
if (requete.getResponseHeader("Content-Type") == "text/plain"){
alert(requete.responseText)
}
responseText vs responseXML
Lequel utiliser ? et bien tout dépend du traitement des informations. Si vous avez besoin du texte brut (TXT, HTML, JSON) vous pouvez avoir recours à responseText. Attention pour JSON, il faudra quand même utiliser eval() afin que Javascript puisse évaluer le contenu de la chaine.
Dans le cas d'un traitement d'un document structuré (xHTML, XML) utilisant un type MIME application/xml, si vous souhaitez accéder à la structure de l'arborescence, il faut user d'un responseXML.
Formatage des données
Bien souvent, les données sont reçues sous forme de chaine de caractères et donc accessibles via la propriété objet.responseText. Il est cependant possible de structurer ces données suivant diverses solutions parmis lesquelles : TXT, HTML, XML ou JSON. Nous allons explorer ces diverses possibilités.
TXT
Les données sont transmises à l'état brut et peuvent tel l'exemple précédent, se composer d'une simple phrase. Il est vrai que cela n'est pas bien complet ni structuré, mais cela à le mérite d'exister et d'être utilisable dans certains cas.
Cependant nous avons en général besoin de diverses informations qui puissent être facilement identifiables. Dans ce cas, nous aurons de préférence recours à une structuration plus élaborée, comme HTML, XML ou JSON.
element.firstChild.nodeValue = requete.responseText;
HTML
Il est possible de recevoir les données directement au format HTML. Dans ce cas, au moment de l'intégration dans le document, l'utilisation de la propriété objet.responseText peut être simplifiée au maximum.
Il existe la propriété innerHTML qui ne fait pas partie du DOM, ni d'aucun autre standard. C'est un ajout de Microsoft que l'ensemble des éditeurs ont repris, et maintenant tous les navigateurs interprètent correctement cette propriété. Elle s'utilise simplement de la manière suivante :
element.innerHTML = requete.responseText;
Ceci dit, il faut faire attention, le contenu actuel de l'élément est intégralement remplacé par le contenu chargé. Il ne s'agit pas d'une méthode similaire à appendChild().
Bien que l'utilisation de la structure HTML semble intéressante dans bien des cas, s'il est très simple de mettre à jour une partie contigüe du document, il n'est pas facile de modifier diverses parties du document en simultané. Il est préférable pour cela d'avoir recours à une structure XML ou JSON.
XML
Si l'on souhaite parser le document XML en utilisant le DOM, il faut que les données soient renvoyées sous application/xml. De ce fait, tout ce que nous avons vu au préalable sur l'utilisation du DOM reste pleinement exploitable. Vous allez pouvoir le constater sur l'exemple utilisé.
var root = requete.responseXML;
JSON
JSON, prononcer Jason, est en fait une utilisation d'un objet Javascript pour contenir et parser les données (JavaScript Object Notation). De ce fait, l'ensemble des données peut être écrit de manière litérale, et manipulé comme une simple chaine de caractères.
Une fois chargées, les données seront alors évaluées par Javascript et deviendront du coup complètement manipulables par le code.
Simple non ? L'ensemble des acteurs du web et les concepteurs de web services, commencent à fournir des données JSON en parallèle de données XML. Voir à ce sujet la proposition de Yahoo.
var data = eval('('+requete.responseText+')');
Récapitulations
Afin d'obtenir le service minimum pour une requête AJAX, voici donc la fonction complète :
function requeteAJAX(){
// invoquer l'instanciation traitée précédemment
var xhr = creeObjetHTTP()
// si l'instance existe alors on peut envoyer une requête
if (xhr != false){
xhr.open("GET","fichierACharger.extension",true,login,password)
xhr.send(null)
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status == 200 || xhr.status == 304){
// si TXT
//traite_datas(xhr.responseText)
// si XML
//traite_datas(xhr.responseXML)
}
}
}
}
function traite_datas(arg){
// traitement des données reçues
}





