Dossier ajax / Javascript-PHP

 

AJAX est un acronyme anglais signifiant Asynchronous JavaScript and XML (JavaScript et XML asynchrones). Pour simplifier, il s'agit d'employer l'objet non standard XMLHttpRequest pour communiquer avec des scripts situés sur le serveur. L'objet permet d'échanger des informations sous différents formats (dont XML, HTML ou texte), mais son principal attrait est sa nature « asynchrone » : tout cela peut se faire sans recharger la page. C'est ce qui permet de mettre à jour certaines parties d'une page sur base d'évènements déclenchés par l'utilisateur.

Les deux fonctionnalités combinées sont les possibilités de :

 

Créer une requête HTTP

Pour faire une requête HTTP vers le serveur à l'aide de JavaScript, il faut disposer d'une instance d'une classe fournissant cette fonctionnalité. Une telle classe a d'abord été introduite dans Internet Explorer sous la forme d'un objet ActiveX appelé XMLHTTP . Par la suite, Mozilla, Safari et d'autres navigateurs ont suivi en implémentant une classe XMLHttpRequest qui fournit les mêmes méthodes et propriétés que l'objet ActiveX original de Microsoft.

Par conséquent, pour créer une instance (un objet) de la classe désirée fonctionnant sur plusieurs navigateurs, vous pouvez utiliser :

if(window.XMLHttpRequest) // Firefox
xhr_object = new XMLHttpRequest();
else if(window.ActiveXObject) // Internet Explorer
xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
else { // XMLHttpRequest non supporté par le navigateur
alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
return;}

(Pour illustrer le principe, le code ci-dessus est une version un peu simplifiée de celui qui est utilisé pour créer une instance XMLHTTP. Pour un exemple plus réaliste, voir la troisième étape de cet article.)

Certaines versions de certains navigateurs Mozilla ne fonctionneront pas correctement si la réponse du serveur n'a pas un en-tête XML mime-type . Pour vous en assurer, vous pouvez utiliser un appel de fonction supplémentaire pour écraser l'en-tête envoyé par le serveur, juste au cas où il ne s'agit pas de text/xml .

xhr_object.setRequestHeader("Content-type", "charset=ISO-8859-15");//fonctionne mal avec Mozilla, firefox...

if(!document.all) xhr_object.overrideMimeType('text/html; charset=ISO-8859-15');//fonctionne mal avec IE7

La chose suivante à faire est de décider ce que vous ferez après avoir reçu la réponse du serveur. À ce stade, vous devez juste communiquer à l'objet de requête HTTP le nom de la fonction JavaScript qui effectuera le travail d'analyse de la réponse. Pour cela, assignez à la propriété onreadystatechange de l'objet le nom de la fonction JavaScript que vous envisagez d'utiliser, comme ceci :

xhr_object.onreadystatechange = nomDeLaFonction;

Notez qu'il n'y a ni parenthèses après le nom de la fonction, ni paramètres fournis, car vous ne faites qu'assigner une référence à la fonction sans l'appeler effectivement. Par ailleurs, au lieu de donner un nom de fonction, vous pouvez utiliser la technique JavaScript de définition de fonctions au vol (ce qu'on appelle une fonction anonyme), et définir à cet endroit les actions à effectuer sur la réponse, comme ceci :

xhr_object.onreadystatechange = function() { // instructions de traitement de la réponse };

Ensuite, après avoir déclaré ce qui se produit lorsque la réponse est reçue, il s'agit de lancer effectivement la requête. Il faut pour cela appeler les méthodes open() et send() de la classe de requête HTTP, comme ceci :

xhr_object.open('GET', ' http://...' , true);
xhr_object.send(null);

Le paramètre de la méthode send() peut être n'importe quelle donnée que vous voulez envoyer au serveur en cas d'utilisation de la méthode POST. Les données doivent être sous la forme d'une chaîne de requête, comme :

nom=valeur&autrenom=autrevaleur&ainsi=desuite

Notez que si vous voulez envoyer des données avec la méthode POST, vous devrez changer le type MIME de la requête à l'aide de la ligne suivante :

xhr_object.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

Autrement, le serveur ignorera les données envoyées.

 

Gestion de la réponse du serveur

Lors de l'envoi de la requête, vous avez désigné une fonction JavaScript pour traiter la réponse.

xhr_object.onreadystatechange = nomDeLaFonction;

Voyons maintenant ce que cette fonction doit faire. Tout d'abord, elle doit vérifier l'état de la requête. Si cet état a une valeur de 4, cela signifie que la réponse du serveur a été reçue dans son intégralité et qu'elle peut maintenant être traitée.

if (xhr_object.readyState == 4) {
// tout va bien, la réponse a été reçue
} else {
// pas encore prête
}

Voici la liste complète des valeurs de readyState  :

 

La seconde vérification concerne le code d'état de la réponse HTTP du serveur. Tous les codes possibles sont listés sur le site du W3C . Dans notre cas, nous sommes seulement intéressés par la réponse 200 OK .

if (xhr_object.status == 200) {
// parfait !
} else {
// il y a eu un problème avec la requête,
// par exemple la réponse peut être un code 404 (Non trouvée)
// ou 500 (Erreur interne au serveur)
}

Après avoir vérifié l'état de la requête et le code d'état HTTP de la réponse, vous pouvez traiter à votre guise les données envoyées par le serveur. Il existe deux manières d'accéder à ces données :

 

Un exemple simple

Rassemblons tous ces éléments dans un exemple : une requête HTTP simple. Notre JavaScript demande un document HTML, test.html , qui contient le texte « Je suis un test. », et nous affichons le contenu de ce fichier test.html dans un message alert() .

<script type="text/javascript" language="javascript">
function faire_ajax(url) {
var xhr_object = false;
if (window.XMLHttpRequest) { // Mozilla, Safari,...
xhr_object = new XMLHttpRequest();
if (xhr_object.overrideMimeType) {
xhr_object.overrideMimeType('text/xml'); // Voir la note ci-dessous à propos de cette ligne
} } else if (window.ActiveXObject) { // IE
try { xhr_object = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) { try { xhr_object = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } }
if (!xhr_object) { alert('Abandon :( Impossible de créer une instance XMLHTTP'); return false; }
xhr_object.onreadystatechange = function() { alertContents(xhr_object); };
xhr_object.open('GET', url, true);
xhr_object.send(null); }
function alertContents(xhr_object) {
if (xhr_object.readyState == 4) {
if (xhr_object.status == 200) { alert(xhr_object.responseText); } else { alert('Un problème est survenu avec la requête.'); } } }
</script>
<span style="cursor: pointer; text-decoration: underline" onclick="faire_ajax('votre_lien.php')"> Effectuer une requête </span>

Dans cet exemple :

Note  : La ligne httpRequest.overrideMimeType('text/xml'); ci-dessus provoquera des erreurs dans la console JavaScript de Firefox 1.5 ou supérieur si la page appelée par XMLHttpRequest n'est pas du XML valide (par exemple s'il s'agit de texte simple). Ce comportement est en réalité le comportement correct et cet article sera revu ultérieurement.

Note 2  : Si vous envoyez une requête vers un bout de code dynamique qui renvoie du XML, plutôt que vers un fichier XML statique, vous devez définir certains en-têtes de réponse pour que votre page fonctionne non seulement avec Mozilla, mais aussi avec Internet Explorer. Si vous ne spécifiez pas l'en-tête Content-Type: application/xml , IE produira une erreur JavaScript « Objet attendu » après la ligne à laquelle vous tenterez d'accéder à un élément XML. Si vous ne spécifiez pas l'en-tête Cache-Control: no-cache , le navigateur mettra la réponse en cache et n'effectuera plus jamais la requête ultérieurement, ce qui peut faire du débogage un véritable défi.

Note 3  : Si la variable httpRequest est utilisée globalement, des appels concurrents à makeRequest() peuvent s'écraser l'un l'autre, provoquant un effet de race condition . On peut s'en prémunir en rendant la variable httpRequest locale à la fonction et en la passant en paramètre à la fonction alertContent() .

Note 4  : Pour procéder à l'enregistrement de la fonction de callback onreadystatechange , aucun paramètre n'est permis. C'est pourquoi le code suivant ne fonctionnera pas :

xhr_object.onreadystatechange = alertContents(xhr_object); // (ne fonctionne pas)

Pour enregistrer la fonction correctement, vous pouvez soit passer les paramètres indirectement via la fonction anonyme, ou utiliser httpRequest comme une variable globale. Voici quelques exemples :

xhr_object.onreadystatechange = function() { alertContents(xhr_object); }; //1 (requêtes simultanées)
httpRequest.onreadystatechange = alertContents; //2 (variable globale)

La méthode 1 permet d'avoir plusieurs requêtes lancées simultanément, la méthode 2 est utilisée si httpRequest est une variable globale.

Note 5  : Si erreur de communication se produit (par exemple le serveur web qui cesse de répondre), une exception se déclenchera dans la méthode onreadystatechange lors de l'accès à la variable .status .

function alertContents(xhr_object) {
try { if (xhr_object.readyState == 4) {
if (xhr_object.status == 200) {
alert(xhr_object.responseText);
} else {
alert('Un problème est survenu au cours de la requête.');
} } }
catch( e ) { alert("Une exception s'est produite : " + e.description); } }

 

Travailler avec des réponses XML

Dans l'exemple précédent, après la réception de la réponse à la requête HTTP, nous avons utilisé la propriété responseText de l'objet de requête, qui contient le contenu du fichier test.html . Essayons maintenant la propriété responseXML .

Tout d'abord, créons un document XML valide qui sera l'objet de la requête. Le document ( test.xml ) contient ce qui suit :

<?xml version="1.0" ?> <root> Je suis un test. </root>

Dans le script, il est juste nécessaire de remplacer la ligne de requête par :

... onclick="faire_ajax('votre_lien.php')"> ...

Ensuite, dans alertContents() , il faut remplacer la ligne alert(xhr_object.responseText); par :

var xmldoc = xhr_object.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
alert(root_node.firstChild.data);

De cette façon, nous avons pris l'objet XMLDocument donné par responseXML et nous avons utilisé des méthodes DOM pour accéder à certaines données contenues dans le document XML.

 

Eviter le blocage du à une bande passante faible

Il suffit de bloquer la requette sur un setTimout:

timerAntiblocage=setTimeout(function () { xhr_object.abort();},5000);
if ( xhr_object.readyState == 4 ){
clearTimeout(timerAntiblocage);
}

 

Exemple complexe avec PHP

Voici un script fonctionnel utilisé afin de lire un flux et l'afficher dans un div

function lire_ce_lien_dans_id(action,type,flux,id,racineid){
var xhr_object = null;
var position = racineid+id;
document.getElementById(position).innerHTML='<div align="center">en cours...</div>';
if(window.XMLHttpRequest)
xhr_object = new XMLHttpRequest();
else if(window.ActiveXObject)
xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
else {
alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
return;}
var sauvurl="module.php?action="+action+"&type="+type+"&flux="+flux;
timer=setTimeout(function () { xhr_object.abort();},10000);
xhr_object.open("GET", sauvurl, true);
xhr_object.setRequestHeader("Content-type", "charset=ISO-8859-15");
if(!document.all) xhr_object.overrideMimeType('text/html; charset=ISO-8859-15');
xhr_object.onreadystatechange = function(){
if ( xhr_object.readyState == 4 )
{
clearTimeout(timer);
document.getElementById(position).innerHTML = xhr_object.responseText;
}
}
xhr_object.send(null);
}