Ajax, acronyme d'Asynchronous JavaScript and XML, est un concept de programmation qui consiste à effectuer, en arrière-plan et depuis une page chargée dans la fenêtre d'un navigateur, des demandes à un serveur web. La page dans la fenêtre du navigateur reste chargée lors de l'exécution de ces tâches. Les solutions de mise en œuvre sont nombreuses, mais d'une manière générale, le JavaScript reste toujours présent.
Le concept existe depuis fort longtemps, même si le terme "Ajax", relativement récent, date de 2005. Ajax n'a donc été, en aucune façon, une révolution dans le monde d'internet. Cependant, la communication qui a été faite autour de ce concept en a généralisé son utilisation.
Le concept consiste donc, la page demeurant chargée :
L'envoi des données au serveur peut se faire avec plusieurs méthodes :
La réception des données venant du serveur utilise divers formats :
Deux modes de fonctionnement sont possibles :
Plusieurs solutions permettent de mettre en œuvre Ajax :
XMLHttpRequest est un outil conçu pour effectuer des requêtes sur le serveur sans recharger la page. Le mode asynchrone est, de loin, le plus utilisé. A l'époque où XMLHttpRequest n'existait pas encore, il fallait recourir à des astuces, telles qu'un iframe invisible, pour obtenir un résultat similaire. En dépit de son nom :
L'exemple basique proposé ci-dessous utilise :
Fichier "index.html" :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> function requete(nb1,nb2) { var xhr=new XMLHttpRequest(); xhr.open("GET", "calcul.php?nb1="+nb1+"&nb2="+nb2, true); xhr.send(null); xhr.onreadystatechange=function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } }; } </script> </head> <body> <button type="button" onclick="requete(3,6);">Calculer</button> </body> </html>
Fichier "calcul.php" :
<?php $nb1=$_GET['nb1']; $nb2=$_GET['nb2']; $retour=2*$nb1+3*$nb2; echo $retour; ?>
var xhr=new XMLHttpRequest();
On crée l'objet "xhr" issu de la classe "XMLHttpRequest".
xhr.open("GET", "calcul.php?nb1="+nb1+"&nb2="+nb2, true);
On prépare la requête avec la méthode "open()" prennant trois arguments :
Le dernier argument, facultatif, peut ne pas être précisé. Le mode de fonctionnement par défaut est le mode asynchrone.
xhr.send(null);
On envoie la requête.
xhr.onreadystatechange=function() { if (xhr.readyState == 4 && xhr.status == 200) { ... } };
Les attributs "readyState" et "statut" peuvent prendre plusieurs valeurs. Pour faire simple :
Ce deuxième exemple a pour objectif de montrer :
Fichier "index.html" :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> function requete() { var nb1=document.getElementById("nb1").value; var nb2=document.getElementById("nb2").value; var xhr=new XMLHttpRequest(); xhr.open("POST", "calcul.php", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send('nb1='+nb1+'&nb2='+nb2); xhr.onreadystatechange=function() { if (xhr.readyState == 4 && xhr.status == 200) { resultat=JSON.parse(xhr.responseText); alert(resultat.calcul2); } }; } </script> </head> <body> Premier nombre = <input type="text" id="nb1"><br> Deuxième nombre = <input type="text" id="nb2"><br> <button type="button" onclick="requete();">Calculer</button> </body> </html>
Fichier "calcul.php" :
<?php $nb1=$_POST['nb1']; $nb2=$_POST['nb2']; $cal1=$nb1*2+$nb2*3+1; $cal2=$nb2*3+$nb2*4+2; $retour='{ "calcul1":'.$cal1.', "calcul2":'.$cal2.' }'; echo $retour; ?>
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
La méthode POST consiste généralement à envoyer des valeurs contenues dans un formulaire. Il est nécessaire de modifier les en-têtes d'envoi des données.
resultat=JSON.parse(xhr.responseText);
La chaîne de caractères reçue, au format JSON, est convertie en un objet "resultat". Il comporte ici les deux attributs "calcul1" et "calcul2".
La mise en place de XMLhttpRequest dans les navigateurs s'est faite progressivement. De plus au début, la syntaxe variait selon les navigateurs. L'utilisation d'une bibliothèque telle que jQuery présente donc un avantage et un inconvénient.
La bibliothèque jQuery contient la fonction "$.Ajax()", prenant divers arguments :
Argument | Valeur |
---|---|
url | Le fichier ciblé |
type | 'POST' ou 'GET', selon le type de requête |
data | Les données envoyées au serveur |
dataType | 'json', 'xml', 'text' ou 'html' selon le type de données venant du serveur |
success | La fonction à appeler si la requête a réussi |
error | La fonction à appeler si la requête a échoué |
complete | La fonction à appeler une fois la requête effectuée. |
L'exemple basique proposé ci-dessous utilise :
Par rapport à l'exemple précédent, seul le fichier "index.html" est modifié :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="jQuery.js"></script> <script> $(document).ready(function() { $('#f').submit(function(event) { event.preventDefault(); $.Ajax({ type: 'POST', url: 'calcul.php', data: $(this).serialize(), dataType: 'json', success: function(resultat) { alert(resultat.calcul2); } }); }); }); </script> </head> <body> <form id="f" method="post"> Premier nombre = <input type="text" name="nb1"><br> Deuxième nombre = <input type="text" name="nb2"><br> <button type="submit">Calculer</button> </form> </body> </html>
$(document).ready(function() {
Le document doit être chargé au préalable...
$('#f').submit(function(event) {
La fonction est appelée lorsque l'utilisateur clique sur le bouton "submit" du formulaire.
event.preventDefault();
La méthode "preventDefault()" empêche le navigateur de déclencher son comportement par défaut.
data: $(this).serialize(),
La méthode "serialize()" de jQuery simplifie l'écriture des données à envoyer au serveur. Il serait possible d'écrire le code ci-dessous à la place.
data: 'nb1='+nb1+'&nb2='+nb2,
Cette solution était mise en œuvre bien avant l'apparition de la classe XMLhttpRequest de JavaScript. Elle présente donc une excellente compatibilité avec l'ensemble des navigateurs, récents ou anciens. Naturellement, cette solution est rarement retenue dans les projets modernes.
Le tout premier exemple de cet exposé est repris. Le fichier "index.html" est modifié comme il suit.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> function requete(nb1,nb2) { document.getElementById('cache').src='calcul.php?nb1='+nb1+'&nb2='+nb2; document.getElementById('cache').onload=function() { resultat=document.getElementById('cache').contentDocument; alert(resultat.body.textContent); }; } </script> </head> <body> <button type="button" onclick="requete(3,6);">Calculer</button> <iframe id="cache" style="display:none;"></iframe> </body> </html>
Fichier "calcul.php" retourne une page au format HTML :
<html> <head></head> <body> <?php $nb1=$_GET['nb1']; $nb2=$_GET['nb2']; $retour=2*$nb1+3*$nb2; echo $retour; ?> </body> </html>
<iframe id="cache" style="display:none;"></iframe>
L'élément iframe permet d'intégrer une page HTML, pour l'instant inexistante et cachée avec le style "display:none;", dans la page principale.
document.getElementById('cache').src='calcul.php?nb1='+nb1+'&nb2='+nb2;
La page "calcul.php" est chargée dans l'iframe avec ses paramètres.
resultat=document.getElementById('cache').contentDocument; alert(resultat.body.textContent);
Le résultat contenu entre les balises <body> et</body> est affiché dans une boîte de dialogue.
Il existe plusieurs variantes à la solution présentée ci-dessus :
document.getElementById("monimage").src="image.php?param1=1¶m2=2"
Une application web comporte, en général, un nombre important de fonctions. Parfois certaines fonctions, secondaires, ne sont appelées qu'en de rares occasions. Pour éviter de charger sur le client du code qui ne sera probablement pas interprété, il est possible de ne le charger qu'en cas de besoin.
Il s'agit toujours d'Ajax, mais aucune donnée n'est transmise au serveur. Sa mission est simplement de fournir, à la demande, du code en JavaScript. Pour se faire, une solution consiste à faire évoluer le DOM en y ajoutant les noeuds nécessaires.
Fichier "index.html" :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> function tester() { script_complementaire=document.createElement("script"); script_complementaire.src="complement.js"; document.body.appendChild(script_complementaire); } </script> </head> <body> <input type="button" value="TESTER" onclick="tester();"> </body> </html>
Fichier "complement.js" :
alert("Le script complémentaire est appelé!");
Dans l'exemple de base ci-dessus, le fichier "complement.js" est chargé dans la page lorsque l'utilisateur clique sur le bouton "TESTER".
En JavaScript, il n'existe pas de fonction import(), include() ou require(). Il est toutefois possible d'en définir une. On l'a longtemps créée avec l'instruction document.write(), et cette solution reste compatible avec les navigateurs récents. De nos jours, on préfère généralement une solution de ce genre :
function require(fichier) { script_complementaire=document.createElement("script"); script_complementaire.src=fichier; document.body.appendChild(script_complementaire); } ... require("http://chemin/fichier.js");
Il est parfois nécessaire qu'une requête de type Ajax se fasse de manière cyclique et automatique. Il s'agit, par exemple, d'afficher des informations en temps réel.
Les solutions proposées ci-dessous, bien que fonctionnelles, sont de moins en moins utilisées car elles sont gourmandes en ressources. Elles ont tendance à être remplacées par des solutions basées sur les WebSockets.
La fonction "setTimeOut()" est disponible depuis les premières versions de JavaScript. Comme elle n'appelle qu'une seule fois la fonction souhaitée, il est nécessaire de créer un rappel. L'exemple ci-dessous est celui d'une fonction "requete()", appelée une fois la page complètement chargée, et ce toutes les cinq secondes.
<script> function requete() { ... setTimeout(requete,5000); } window.onload=requete(); </script>
A l'aide d'un identifiant, on peut annuler l'exécution de "setTimeout()" avec la fonction "clearTimeout()".
<script> var identifiant=setTimeout(requete, 5000); ... clearTimeout(identifiant); </script>
La fonction "setInterval()", relativement récente, présente l'avantage d'être plus simple à mettre en œuvre. L'exemple ci-dessous est similaire à celui vu précédemment.
<script> function requete() { ... } window.onload=function(){ setInterval(requete,5000); }; </script>
A l'aide d'un identifiant, on peut annuler l'exécution de "setInterval()" avec la fonction "clearInterval()".
<script> var identifiant=setInterval(requete, 5000); ... clearInterval(identifiant); </script>
Cette solution est utilisée dans le cas d'une page web chargée dans un iframe. Cette page web comporte une balise "meta" avec l'attribut "refresh", de sorte qu'elle se réactualise de façon automatique après un certain délai. Dans l'exemple ci-dessous, ce délai est défini à cinq secondes.
<meta http-equiv="refresh" content="5">