LE CONCEPT AJAX

Généralités

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 :

Envoi de données

L'envoi des données au serveur peut se faire avec plusieurs méthodes :

Réception de données

La réception des données venant du serveur utilise divers formats :

Modes de fonctionnement

Deux modes de fonctionnement sont possibles :

Mise en œuvre

Plusieurs solutions permettent de mettre en œuvre Ajax :

Ajax avec XMLHttpRequest

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 :

Premier exemple

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;
?>

Commentaire sur le premier exemple

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 :

Deuxième exemple

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;
?>

Commentaire sur le deuxième exemple

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.

Ajax avec jQuery

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 :

ArgumentValeur
urlLe fichier ciblé
type'POST' ou 'GET', selon le type de requête
dataLes données envoyées au serveur
dataType'json', 'xml', 'text' ou 'html' selon le type de données venant du serveur
successLa fonction à appeler si la requête a réussi
errorLa fonction à appeler si la requête a échoué
completeLa fonction à appeler une fois la requête effectuée.

Exemple

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>

Commentaire

$(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,

Ajax avec un IFRAME

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.

Exemple

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>

Commentaire

<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.

Variantes

Il existe plusieurs variantes à la solution présentée ci-dessus :

document.getElementById("monimage").src="image.php?param1=1&param2=2"

Ajax en complétant le DOM

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.

Exemple

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é!");

Commentaire

Dans l'exemple de base ci-dessus, le fichier complement.js est chargé dans la page lorsque l'utilisateur clique sur le bouton TESTER.

Remarque

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");

Acquisition périodique

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()

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>

La fonction 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>

La fonction 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.

L'attribut refresh de 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">

Tromper le cache du navigateur

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>