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
apparut bien plus tard.
Ajax n'a donc été, en aucune façon, une révolution dans le monde du Web.
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 :
onreadystatechangeprend en compte le changement d'état de
readyState, pour une requête asynchrone.
readyState == 4indique que la requête est terminée.
statut == 200signifie que tout s'est bien passé.
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 n'est plus 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.
function import(fichier) { document.write("<script src='"+fichier+"'></script>" ); } ... import("fichier.js");
De nos jours, on préfère généralement une solution de ce genre :
function import(fichier) { script_complementaire=document.createElement("script"); script_complementaire.src=fichier; document.body.appendChild(script_complementaire); } ... import("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.
setTimeOut()
Cette fonction 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>
setInterval()
Cette fonction relativement récente, propose une autre solution. 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>
requestAnimationFrame()
Cette fonction, encore plus récente, demande au navigateur d'exécuter
dès que possible une fonction. Elle est adaptée aux animations en temps réel.
La fonction cancelAnimationFrame()
stoppe l'exécution.
refreshde la balise
meta
Cette solution s'utilise 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">
Des systèmes de cache, sur les navigateurs et éventuellement sur les passerelles, sauvegardent des données dans des fichiers temporaires. Ils améliorent la fluidité de la navigation mais gênent le rechargement des données évoluant rapidement.
Dans l'exemple ci-dessous, on souhaite recharger l'image dessin.png
,
laquelle change toutes les secondes.
Un paramètre fictif et modifié à chaque rechargement est ajouté
pour éviter que l'image ne provienne du cache.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> var nb=0; recharge=function() { nb++; document.images[0].src="dessin.png?"+nb; setTimeout(recharge,1000) }; window.onload=recharge; </script> </head> <body><img src="dessin.png"></body> </html>