Première version : les bases

Parent Previous Next


Première version : les bases



L'utilisation de l'objet XHR se fait en deux étapes bien distinctes :

  1. Préparation et envoi de la requête ;
  2. Réception des données.


Nous allons donc étudier l'utilisation de cette technologie au travers de ces deux étapes.


Vous avez sûrement pu constater que nous avons abrégé XMLHttpRequest par XHR. Il s'agit d'une abréviation courante pour tout développeur Javascript, ne soyez donc pas étonnés de la voir sur de nombreux sites.


Préparation et envoi de la requête


Pour commencer à préparer notre requête, il nous faut tout d'abord instancier un objet XHR :

var xhr = new XMLHttpRequest();


La préparation de la requête se fait par le biais de la méthode open(), qui prend en paramètres cinq arguments différents, dont trois facultatifs :


Voici une utilisation basique et courante de la méthode open() :

xhr.open('GET', 'http://mon_site_web.com/ajax.php');


Cette ligne de code prépare une requête afin que cette dernière contacte la page ajax.php sur le nom de domaine mon_site_web.com par le biais du protocole http (vous pouvez très bien utiliser d'autres protocoles, comme HTTPS ou FTP par exemple). Tout paramètre spécifié à la requête sera transmis par le biais de la méthode GET.


Après préparation de la requête, il ne reste plus qu'à l'envoyer avec la méthode send(). Cette dernière prend en paramètre un argument obligatoire que nous étudierons plus tard. Dans l'immédiat, nous lui spécifions la valeur null :

xhr.send(null);


Après exécution de cette méthode, l'envoi de la requête commence. Cependant, nous n'avons spécifié aucun paramètre ni aucune solution pour vérifier le retour des données, l'intérêt est donc quasi nul.


Si vous travaillez avec des requêtes asynchrones (ce que vous ferez dans 99% des cas), sachez qu'il existe une méthode abort() permettant de stopper toute activité. La connexion au serveur est alors interrompue et votre instance de l'objet XHR est remise à zéro. Son utilisation est très rare, mais elle peut servir si vous avez des requêtes qui prennent bien trop de temps.


Synchrone ou asynchrone ?


Vous savez très probablement ce que signifient ces termes dans la vie courante, mais que peuvent-ils donc désigner une fois transposés au sujet actuel ? Une requête synchrone va bloquer votre script tant que la réponse n'aura pas été obtenue, tandis qu'une requête asynchrone laissera continuer l'exécution de votre script et vous préviendra de l'obtention de la réponse par le biais d'un événement.


Quelle est la solution la plus intéressante ?


Il s'agit sans conteste de la requête asynchrone. Il est bien rare que vous ayez besoin que votre script reste inactif simplement parce qu'il attend une réponse à une requête. La requête asynchrone vous permet de gérer votre interface pendant que vous attendez la réponse du serveur, vous pouvez donc indiquer au client de patienter ou vous occuper d'autres tâches en attendant.


Transmettre des paramètres


Intéressons-nous à un point particulier de ce cours ! Les méthodes d'envoi GET et POST vous sont sûrement familières, mais qu'en est-il de HEAD ? En vérité, il ne s'agit tout simplement pas d'une méthode d'envoi, mais de réception : en spécifiant cette méthode, vous ne recevrez pas le contenu du fichier dont vous avez spécifié l'URL, mais juste son en-tête (son header, d'où le HEAD). Cette utilisation est pratique quand vous souhaitez simplement vérifier, par exemple, l'existence d'un fichier sur un serveur.


Revenons maintenant aux deux autres méthodes qui sont, elles, conçues pour l'envoi de données !


Comme dit précédemment, il est possible de transmettre des paramètres par le biais de la méthode GET. La transmission de ces paramètres se fait de la même manière qu'avec une URL classique, il faut les spécifier avec les caractères ? et & dans l'URL que vous passez à la méthode open() :

xhr.open('GET', 'http://mon_site_web.com/ajax.php?param1=valeur1&param2=valeur2');


Il est cependant conseillé, quelle que soit la méthode utilisée (GET ou POST), d'encoder toutes les valeurs que vous passez en paramètre grâce à la fonction encodeURIComponent(), afin d'éviter d'écrire d'éventuels caractères interdits dans une URL :

var value1 = encodeURIComponent(value1),

    value2 = encodeURIComponent(value2);

 

xhr.open('GET', 'http://mon_site_web.com/ajax.php?param1=' + value1 + '&param2=' + value2);


Votre requête est maintenant prête à envoyer des paramètres par le biais de la méthode GET !


En ce qui concerne la méthode POST, les paramètres ne sont pas à spécifier avec la méthode open() mais avec la méthode send() :

xhr.open('POST', 'http://mon_site_web.com/ajax.php');

xhr.send('param1=' + value1 + '&param2=' + value2);


Cependant, la méthode POST consiste généralement à envoyer des valeurs contenues dans un formulaire, il faut donc modifier les en-têtes d'envoi des données afin de préciser qu'il s'agit de données provenant d'un formulaire (même si, à la base, ce n'est pas le cas) :

xhr.open('POST', 'http://mon_site_web.com/ajax.php');

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

xhr.send('param1=' + value1 + '&param2=' + value2);


La méthode setRequestHeader() permet l'ajout ou la modification d'un en-tête, elle prend en paramètres deux arguments : le premier est l'en-tête concerné et le deuxième est la valeur à lui attribuer.


Réception des données


La réception des données d'une requête se fait par le biais de nombreuses propriétés. Cependant, les propriétés à utiliser diffèrent selon que la requête est synchrone ou non.


Requête asynchrone : spécifier la fonction de callback


Dans le cas d'une requête asynchrone, il nous faut spécifier une fonction de callback afin de savoir quand la requête s'est terminée. Pour cela, l'objet XHR possède un événement nommé readystatechange auquel il suffit d'attribuer une fonction :

xhr.onreadystatechange = function() {

    // Votre code…

};


Cependant, cet événement ne se déclenche pas seulement lorsque la requête est terminée, mais plutôt, comme son nom l'indique, à chaque changement d'état. Il existe cinq états différents représentés par des constantes spécifiques à l'objet XMLHttpRequest :

Constante

Valeur

Description

UNSENT

0

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

OPENED

1

La méthode open() a été appelée, mais la requête n'a pas encore été envoyée par la méthode send().

HEADERS_RECEIVED

2

La méthode send() a été appelée et toutes les informations ont été envoyées au serveur.

LOADING

3

Le serveur traite les informations et a commencé à renvoyer les données. Tous les en-têtes des fichiers ont été reçus.

DONE

4

Toutes les données ont été réceptionnées.


L'utilisation de la propriété readyState est nécessaire pour connaître l'état de la requête. L'état qui nous intéresse est le cinquième (la constante DONE), car nous voulons simplement savoir quand notre requête est terminée. Il existe deux manières pour vérifier que la propriété readyState contient bien une valeur indiquant que la requête est terminée. La première consiste à utiliser la constante elle-même :

xhr.onreadystatechange = function() {

    if (xhr.readyState == xhr.DONE) { // La constante DONE appartient à l'objet XMLHttpRequest, elle n'est pas globale

        // Votre code…

    }

};


Tandis que la deuxième manière de faire, qui est la plus courante (et que nous utiliserons), consiste à utiliser directement la valeur de la constante, soit 4 pour la constante DONE:

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4) {

        // Votre code…

    }

};


De cette manière, notre code ne s'exécutera que lorsque la requête aura terminé son travail. Toutefois, même si la requête a terminé son travail, cela ne veut pas forcément dire qu'elle l'a mené à bien, pour cela nous allons devoir consulter le statut de la requête grâce à la propriété status. Cette dernière renvoie le code correspondant à son statut, comme le fameux 404 pour les fichiers non trouvés. Le statut qui nous intéresse est le 200, qui signifie que tout s'est bien passé :

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        // Votre code…

    }

};


À noter qu'il existe aussi une propriété nommée statusText contenant une version au format texte du statut de la requête, en anglais seulement. Par exemple, un statut 404 vous donnera le texte suivant : « Not Found ».

Si vous souhaitez tester votre requête XHR sur votre ordinateur sans même utiliser de serveur de test (WampServer par exemple), alors vous n'obtiendrez jamais de statut équivalent à 200 puisque c'est normalement le rôle du serveur HTTP (Apache par exemple, fourni avec WampServer) de fournir cette valeur. Vérifiez alors si le statut équivaut à 0, cela suffira.

Nous avons ici traité le cas d'une requête asynchrone, mais sachez que pour une requête synchrone il n'y a qu'à vérifier le statut de votre requête, tout simplement.


Traitement des données


Une fois la requête terminée, il vous faut récupérer les données obtenues. Ici, deux possibilités s'offrent à vous :

  1. Les données sont au format XML, vous pouvez alors utiliser la propriété responseXML, qui permet de parcourir l'arbre DOM des données reçues.
  2. Les données sont dans un format autre que le XML, il vous faut alors utiliser la propriété responseText, qui vous fournit toutes les données sous forme d'une chaîne de caractères. C'est à vous qu'incombe la tâche de faire d'éventuelles conversions, par exemple avec un objet JSON : var response = JSON.parse(xhr.responseText);.


Les deux propriétés nécessaires à l'obtention des données sont responseText et responseXML. Cette dernière est particulière, dans le sens où elle contient un arbre DOM que vous pouvez facilement parcourir. Par exemple, si vous recevez l'arbre DOM suivant :

<?xml version="1.0" encoding="utf-8"?>

<table>

 

  <line>

    <cel>Ligne 1 - Colonne 1</cel>

    <cel>Ligne 1 - Colonne 2</cel>

    <cel>Ligne 1 - Colonne 3</cel>

  </line>

 

  <line>

    <cel>Ligne 2 - Colonne 1</cel>

    <cel>Ligne 2 - Colonne 2</cel>

    <cel>Ligne 2 - Colonne 3</cel>

  </line>

 

  <line>

    <cel>Ligne 3 - Colonne 1</cel>

    <cel>Ligne 3 - Colonne 2</cel>

    <cel>Ligne 3 - Colonne 3</cel>

  </line>

 

</table>


vous pouvez récupérer toutes les balises <cel> de la manière suivante :

var cels = xhr.responseXML.getElementsByTagName('cel');


Une petite précision est nécessaire concernant l'utilisation de la propriété responseXML. Sur de vieux navigateurs (notamment avec de vieilles versions de Firefox), celle-ci peut ne pas être utilisable si le serveur n'a pas renvoyé une réponse avec un en-tête spécifiant qu'il s'agit bel et bien d'un fichier XML. La propriété pourrait alors être inutilisable, bien que le contenu soit pourtant un fichier XML. Pensez donc bien à spécifier l'en-tête Content-type avec la valeur text/xml pour éviter les mauvaises surprises. Le Javascript reconnaîtra alors le type MIME XML. En PHP, cela se fait de la manière suivante :

<?php header('Content-type: text/xml'); ?>


Récupération des en-têtes de la réponse


Il se peut que vous ayez parfois besoin de récupérer les valeurs des en-têtes fournis avec la réponse de votre requête. Pour cela, vous pouvez utiliser deux méthodes. La première se nomme getAllResponseHeaders()et retourne tous les en-têtes de la réponse en vrac. Voici ce que cela peut donner :

Date: Sat, 17 Sep 2011 20:09:46 GMT

Server: Apache

Vary: Accept-Encoding

Content-Encoding: gzip

Content-Length: 20

Keep-Alive: timeout=2, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=utf-8


La deuxième méthode, getResponseHeader(), permet la récupération d'un seul en-tête. Il suffit d'en spécifier le nom en paramètre et la méthode retournera sa valeur :

var xhr = new XMLHttpRequest();

 

xhr.open('HEAD', 'http://mon_site_web.com/', false);

xhr.send(null);

 

alert(xhr.getResponseHeader('Content-type')); // Affiche : « text/html; charset=utf-8 »


Mise en pratique


L'étude de cet objet étant assez segmentée, nous n'avons pas encore eu l'occasion d'aborder un quelconque exemple. Pallions ce problème en créant une page qui va s'occuper de charger le contenu de deux autres fichiers selon le choix de l'utilisateur.


Commençons par le plus simple et créons notre page HTML qui va s'occuper de charger le contenu des deux fichiers :

<p>

  Veuillez choisir quel est le fichier dont vous souhaitez voir le contenu :

</p>

 

<p>

  <input type="button" value="file1.txt" />

  <input type="button" value="file2.txt" />

</p>

 

<p id="fileContent">

  <span>Aucun fichier chargé</span>

</p>


Comme vous pouvez le constater, le principe est très simple, nous allons pouvoir commencer notre code Javascript. Créons tout d'abord une fonction qui sera appelée lors d'un clic sur un des deux boutons, elle sera chargée de s'occuper du téléchargement et de l'affichage du fichier passé en paramètre :

function loadFile(file) {

   

    var xhr = new XMLHttpRequest();

 

    // On souhaite juste récupérer le contenu du fichier, la méthode GET suffit amplement :

    xhr.open('GET', file);

 

    xhr.onreadystatechange = function() { // On gère ici une requête asynchrone

 

        if (xhr.readyState == 4 && xhr.status == 200) { // Si le fichier est chargé sans erreur

 

            document.getElementById('fileContent').innerHTML = '<span>' + xhr.responseText + '</span>'; // Et on affiche !

 

        }

 

    };

 

    xhr.send(null); // La requête est prête, on envoie tout !

 

}


Il ne nous reste maintenant plus qu'à mettre en place les événements qui déclencheront tout le processus. Ça commence à être du classique pour vous, non ?

(function() { // Comme d'habitude, une fonction anonyme pour éviter les variables globales

 

    var inputs = document.getElementsByTagName('input'),

        inputsLen = inputs.length;

   

    for (var i = 0 ; i < inputsLen ; i++) {

     

        inputs[i].onclick = function() {

            loadFile(this.value); // À chaque clic, un fichier sera chargé dans la page

        };

 

    }

 

})();


Et c'est tout bon ! Il ne vous reste plus qu'à essayer le résultat de ce travail !


Cela fonctionne plutôt bien, n'est-ce pas ? Peut-être même trop bien, on ne se rend pas compte que l'on utilise ici de l'AJAX tellement le résultat est rapide. Enfin, on ne va pas s'en plaindre !


Cet exercice vous a sûrement clarifié un peu l'esprit quant à l'utilisation de cet objet, mais il reste un point qui n'a pas été abordé. Bien qu'il ne soit pas complexe, mieux vaut vous le montrer, notamment afin de ne jamais l'oublier : la gestion des erreurs !


Le code de l'exercice que nous venons de réaliser ne sait pas prévenir en cas d'erreur, ce qui est assez gênant au final, car l'utilisateur pourrait ne pas savoir si ce qui se passe est normal. Nous allons donc mettre en place un petit bout de code pour prévenir en cas de problème, et nous allons aussi faire en sorte de provoquer une erreur afin que vous n'ayez pas à faire 30 000 chargements de fichiers avant d'obtenir une erreur. 


Commençons par fournir un moyen de générer une erreur en chargeant un fichier inexistant (nous aurons donc une erreur 404) :

<p>

  <input type="button" value="file1.txt" />

  <input type="button" value="file2.txt" />

  <br /><br />

  <input type="button" value="unknown.txt" />

</p>


Maintenant, occupons-nous de la gestion de l'erreur dans notre événement readystatechange :

xhr.onreadystatechange = function() { // On gère ici une requête asynchrone

 

    if (xhr.readyState == 4 && xhr.status == 200) { // Si le fichier est chargé sans erreur

 

        document.getElementById('fileContent').innerHTML = '<span>' + xhr.responseText + '</span>'; // On l'affiche !

 

    } else if(xhr.readyState == 4 && xhr.status != 200) { // En cas d'erreur !

     

        alert('Une erreur est survenue !\n\nCode :' + xhr.status + '\nTexte : ' + xhr.statusText);

 

    }

 

};


Et voilà ! Vous pouvez d'ores et déjà commencer à vous servir de l'AJAX comme bon vous semble sans trop de problèmes !


Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub facile

Site à deux balles