LP
IMApp

Scripts
Clients

Leçon 11

Leçon 11 : AJAX par l'exemple

Contenu :

Cette leçon est une introduction à la technologie AJAX. Les principales fonctions JQuery relatives à AJAX sont.présentées et expérimentées sur des exemples simples et caractéristiques. La leçon présente également le format d'échange de données JSON. La leçon met l'accent sur la pratique en proposant plusieurs exercices typiques dans l'utilisation d'AJAX.

Objectifs :

Etre capable d'utiliser les fonctions de base JQuery et de JSON pour implémenter des solutions asynchrones simples.

Principe

La technologie AJAX (pour Asynchronous JavaScript And XML) permet d'effectuer des requêtes vers le serveur de façon asynchrone et sans provoquer de rechargement de page. Asynchrone veut dire pendant que la requête s'effectue la page courante reste active et il est possible de déclencher et de traîter d'autres évènements de façon concurrente. Sans rechargement de page signifie que le résultat de la requête (la réponse du serveur à la requête) est reçue et traîtée par le JavaScript et non par le navigateur. On peut résumer ce comportement avec la figure suivante :

Schéma AJAX

AJAX repose sur l'objet DOM XMLHttpRequest qui permet la communication asynchrone avec le serveur. Cet objet de base est la plupart du temps caché par les librairies comme JQuery qui offrent des fonctions avec un haut niveau d'abstraction, fonctions qui permettent de s'affranchir des détails techniques. Ce sont quelques unes de ces fonctions que nous allons étudier dans la suite.

La fonction $.get

Dans cette section, nous étudions l'exemple suivant : on dispose d'une page sur les proverbes chinois et on désire afficher en boucle dans un élément de la page des proverbes chinois choisis au hasard. On dispose pour celà d'un script PHP qui, lorsqu'on l'invoque, délivre un proverbe chinois tiré au hasard dans une liste, sous la forme d'un simple texte (sans balise HTML). Les proverbes sont simplement stockés dans un fichier de texte qui contient les proverbes à raison d'un proverbe par ligne. Voilà un extrait du contenu possible pour ce fichier qu'on nomme exemple-1.txt :

Tout bonheur commence par un petit-déjeuner tranquille.
Rejeter l'essentiel pour s'attacher aux détails insignifiants.
Celui qui sait s'arrêter ne périclite jamais.
Sourire trois fois par jours rend inutile tout médicament.
Il n'y a que les fous et les Européens qui voyagent !
Ce que tu sais, apprends-le à ton voisin.
La fleur s'est fanée mais son parfum demeure.
Ne danse pas tout ce qu'on te chante.
.....
Dans la page HTML exemple-1.html, on dispose d'un élément particulier qui va recevoir les proverbes délivrés par le serveur via la script PHP exemple-1.php qui se résume au code suivant :
<?php
	$proverbes = file( "exemple-1.txt", FILE_IGNORE_NEW_LINES );
	echo $proverbes[ rand(0,count($proverbes) - 1) ];
?>
L'élément HTML récepteur est un span emboité dans un div. L'élément div est toujours visible et l'élément span a son contenu modifié et reçoit les différents proverbes :
<div id="proverbe">
	<span>La joie est une saine nourriture.</span>
</div>
Le code JQuery suivant va : et ce, à intervalles réguliers de 5 secondes :
$(document).ready(function(){
	
	function nouveau() {
		$.get( "exemple-1.php", function( data,status ) {
			$("#proverbe span").fadeOut( function(){
				$("#proverbe span").text( data );
			});
			$("#proverbe span").fadeIn();
		});
	}
	
	setInterval( nouveau, 5000 );
});
La fonction JQuery $.get met en oeuvre une requête AJAX pour récupérer un nouveau proverbe. Ici, cette fonction est appelée avec deux paramètres, mais come on le veraa dans la suite, elle peut prendre plus de paramètres : Lorsque la fonction callback est appelée, ses paramètres effectifs sont les suivants :

Les fichiers de l'exemple précédent sont disponibles dans l'archive exemple-1.zip

La fonction $.post

Dans cette section, nous étudions le nouvel exemple suivant : on dispose d'une page HTML à caractère pédagogique dédiée à un sujet très technique (ici, la définition et l'étude de l'anaphase). La page contenant un grand nombre de termes techniques, on désire pouvoir donner à l'utilisateur la possibilité de visualiser la définition de certains de ces termes. Le code HTML correspondant contient des éléments spécifiques pour les termes dont la définition peut être visualisée. Voici un extrait du fichier HTML exemple-2.html :

L'anaphase est une phase très rapide de la
<span class="definition" data-def="def1">méiose</span>
et de la
<span class="definition" data-def="def2">mitose</span>
où les ....
Dans cet extrait, on remarque que les termes méiose et mitose sont en fait contenus dans des éléments span de classe definition (les termes dont la définition peut être visualisée). Ces éléments possèdent également l'attribut propriétaire data-def dont la valeur est une référence permettant d'accéder à la définition du terme côté serveur. Pour simplifier, la définition est simplement stockée dans un fichier HTML dont le nom est justement la valeur de l'attribut propriétaire data-def correspondant. Par exemple, côté serveur, on dispose du fichier HTML def1.htm qui contient la définition du terme méiose :
<strong>méiose</strong> : c'est le processus de double division cellulaire permettant la formation de gamètes, ou cellules sexuelles chez les organismes eucaryotes.
On dispose du script PHP exemple-2.php, qui, étant donné le paramètre POST def, retourne le contenu du fichier adéquat contenant la définition du terme correspondant (tous les fichiers de définitions se trouvent dans le répertoire definitions/) :
<?php
	$fichier = $_POST[ "def" ] . ".htm";
	$definition = fopen("definitions/$fichier","r");
	fpassthru($definition);
?>
A la manière de l'exemple précédent, l'élément HTML récepteur qui va contenir les définitions est un div qui va apparaître lors de la visualisation d'une définition :
<div id="definition">
</div>
Le code JQuery suivant va :
$(document).ready(function(){
	
    function definition() {
        $.post( "exemple-2.php",
                { def: $(this).attr( "data-def" ) },
                function(data, status) {
                    $( "#definition" ).html( data );
                    $("body").css( "background-color", "#D0D0D0" );
                    $( "#definition" ).fadeIn();
				} );
	}
	
    $( ".definition" ).click(definition);
	
    $( ".definition" ).hover(
		function(){
			$(this).css( "border-bottom", "dotted 2px red");
		},
		function(){
			$(this).css( "border-bottom", "");
    });
		
    $( "#definition" ).click(function(){
		$(this).fadeOut();
		$("body").css( "background-color", bgcolor );
	});
	
    var bgcolor = $("body").css( "background-color" );
});
La fonction definition, lorsqu'elle est invoquée sur l'évènement click sur un élément span de classe definition (les termes dont la définition peut être visualisée), réalise la requête AJAX pour récupérer la définition du terme correspondant, place la chaïne de caractères résultat (produite par le script PHP exemple-2.php) dans l'élément HTML récepteur et le fait apparaître. Cette fonction invoque le script PHP en utilisant la méthode POST et passe au script le paramètre def et sa valeur (la valeur de l'attribut data-def). Ce passage de paramètres, également pour la fonction $.get, suit une syntaxe typique JQuery :
{ paramètre1 : valeur1, paramètre2 : valeur2, .... }
Remarquez la fonction hover qui associe respectivement les deux fonctions anonymes aux évènements mouseenter et mouseleave sur tous les éléments HTML de classe definition (les termes dont la définition peut être visualisée) de façon à ce que l'utilisateur puisse facilement les identifier.

Les fichiers de l'exemple précédent sont disponibles dans l'archive exemple-2.zip

Exercice 1

Complétez le fichier js/exo1JQ.js de façon que la page HTML index.html permette à l'utilisateur de faire apparaître la traduction d'un mot simplement en double-cliquant dessus. Pour réaliser cette fonctionnalité, votre code JQuery doit :
  • fabriquer un nouvel élément qui va recevoir le texte des traductions (un div) et l'insérer à la fin du body. Cet élément aura l'identifiant tooltip et sera stylé par le fichier CSS tooltip.css (fourni)
  • associer une fonction à l'évènement click sur cet élément qui, lorsqu'on l'exécute, fait disparaître l'élément
  • associer à l'évènement double-clic sur l'élément body une fonction qui va :
    • récupérer le mot sélectionné à la suite du double-clic
    • invoquer le script PHP edit.php (fourni) avec comme valeur du paramètre word le mot sélectionné
    • récupérer le résultat de ce script et l'afficher dans l'élément HTML prévu à cet effet
Pour récupérer le mot sélectionné lors du double-clic, on utilise le plug-in JQuery (fourni) jquery.selection.js écrit par Koji Iwasaki. Ce plug-in offre plusieurs fonctions pour gérer les sélections, dont la fonction $.selection qui retourne simplement la sélection courante.

Fichier(s)

exo1.zip

Solution(s)

exo1JQ.js

Exercice 2

Complétez le fichier js/exo2JQ.js de façon que le script PHP index.php affiche une page web contenant des zones éditables. Une zone éditable est un élément HTML particulier dont le contenu peut :
  • dynamiquement être édité/modifié simplement en cliquant sur l'élément
  • être sauvegarder côté serveur, sans rechargement de la page
Le contenu d'une zone éditable est stocké dans un fichier côté serveur. Examinez le script PHP index.php et repérez les zones éditables du document HTML généré. Une zone éditable peut être éditée localement (en JavaScript) puis son contenu peut être sauvegardé côté serveur via une requête AJAX. Un grand nombre de fichiers sont fournis : on se place dans le contexte de pages web existantes (avec déjà plusieurs fichiers CSS et JavaScript) qu'on veut rendre éditables en ajoutant du CSS et bien sûr du JQuery. Votre code JQuery doit :
  • ajouter un nouvel élément (l'éditeur) au document HTML. Cet élément est invisible par défaut. Cet élément contient (entre autre) deux boutons, le bouton save et le bouton cancel
  • associer les fonctions adéquates à l'évènement click sur les deux boutons précédents : la fonction associée au bouton save devra effectuer une requête AJAX pour mettre à jour le fichier concerné côté serveur, en utilisant le script PHP edit.php (fourni)
L'éditeur (le nouvel élément à créer et à ajouter au document HTML) doit avoir exactement la structure suivante :
<div id="editor">
  <p>
    <textarea />
    <input type="button" value="Save" />
    <input type="button" value="Cancel" />
  </p>
</div>

Fichier(s)

exo2.zip

Solution(s)

exo2JQ.js

Exercice 3

Complétez le fichier exo3JQ.js de façon que la page HTML index.html affiche un formulaire de contact AJAX. Ce formulaire n'est pas implémenté à l'aide de l'élément HTML FORM car l'envoi des données est assuré par une requête AJAX. Le script envoie les données (nom, prénom, email et message) via une requête AJAX POST au script PHP savecontact.php. La partie JQuery assure la validation syntaxique des données saisies par l'utilisateur et l'envoi des données via la requête AJAX. Si les données sont erronées, le script affiche le message d'erreur adéquat dans une fenêtre modale (fournie). Pour vérifier les données, vous pouvez utiliser des expressions régulières (fournies) avec la méthode JavaScript test. Le script PHP savecontact.php prend en charge la vérification en ligne du domaine, c'est à dire la partie qui suit le caractère @ dans l'adresse mail (cette partie est fournie) et ajoute le contact dans la base de donnée (précisément dans la table exo3). Ce script retourne la chaîne de carctères OK si l'adresse mail est correcte, la chaîne de carctères KO sinon. Après complétion de la requête, le script JQuery affiche un message dans une fenêtre modale (fournie). L'exercice sera largement commenté durant le cours.

Fichier(s)

exo3.zip

Solution(s)

exo3JQ.js

Le format JSON

Dans les exemples précédents, les valeurs fournies par les scripts PHP lors des requêtes AJAX sont toujours des chaînes de caractères. Cependant, il est parfois nécessaire de récupérer lors de ces requêtes des valeurs structurées, c'est à dire non atomiques, comme par exemple des tableaux (de valeurs). Dans cet type de situation, on peut utiliser JSON. JSON est un format d'échange de données qui permet de convertir un objet structuré en chaîne de caractères ou bien, à l'inverse, convertir une chaîne en objet structuré. Il existe des fonctions assurant ces conversions dans beaucoup de langages de programmation, dont JavaScript/JQuery bien sûr, mais aussi PHP. Le format JSON est très simple à produire. Un élément JSON peut être :

Voici par exemple une chaîne de caractères représentant un objet JSON qui encode un tableau de personnes :
var j = '[{ "nom": "Zorro", "prenom": "Alfred" }, { "nom": "Batman", "prenom": "Alphonse" }]';
JQuery offre la fonction $.parseJSON pour convertir une chaîne de caractères représentant un objet JSON en objet JavaScript (et pas en objet JQuery !) :
var obj = $.parseJSON( j );
$( obj ).each(function() {
	alert( this.prenom + " " + this.nom );
});
Le code précédent : Remarquez l'absence de $ autour du mot-clef this dans la fonction d'itération : le résultat de la fonction $.parseJSON est un objet JavaScript et non pas un objet JQuery !

Etude d'un exemple

Nous allons étudier un petit exemple qui met en jeu la fonction $.parseJSON. On désire concevoir une page HTML qui affiche une galerie d'images des personnages de la série télévisée Les Simpsons. En cliquant sur une photo, on veut invoquer un script PHP via AJAX pour récupérer et afficher les informations relatives au personnage dont on a cliqué l'image. Le script PHP exemple-3.php a deux fonctions distinctes :

Si on invoque le script sans paramètre, il génère une page HTML contenant la galerie des personnages, page dont voici un extrait :
<div>
  <header>
    <h1>Exemple AJAX 3 : <img src="images/logo.png" /></h1>
  </header>
  <div id="info">
    		<img src="" alt="Image du personnage" />
    		<div>
    			Nom : <span id="nom"></span>
    		</div>
     		<div>
    			Prénom : <span id="prenom"></span>
    		</div>   		
     		<div>
    			Sexe : <span id="sexe"></span>
    		</div>
     		<div>
    			Age : <span id="age"></span>
    		</div>
     		<div>
    			Activité : <span id="activite"></span>
    		</div>    				   	    		
    </div>  
    <div id="gallery">
      <img id="1" src="images/homer.gif" alt="Personnage" />
      <img id="2" src="images/marge.gif" alt="Personnage" />
      .....
    </div>
L'élément div d'identifiant info est l'élément qui va contenir les informations d'un personnage. Dans l'élément d'identifiant gallery, chaque élément img possède un attribut id qui identifie de façon unique le personnage correspondant. Si maintenant on invoque script avec un paramètre id, par exemple valant 2, il retourne la chaîne de caractères suivante :
{"nom":"Simpson", "prenom":"Marge", "sexe":"féminin", "age":"47", "activite":"Femme au foyer"}
Cet objet JSON décrit le personnage de Marge Simpson à l'aide des attributs nom, prenom, sexe, age et activite, personnage identifié par l'attribut id de valeur simpson2. Tous les personnages et leurs caractéristiques sont stockés dans une base de données (précisement dans la table exemple3). Voici la partie du script exemple-3.php qui génère un objet JSON correspondant aux informations décrivant un personnage :
<?php
	function info( $id, $pdo ) {
		$personnage = $pdo->query("SELECT * FROM `exemple3` WHERE `ID` = $id")->fetch();
		$info = [];
		$info["nom"] = $personnage['lastname'];
		$info["prenom"] = $personnage['firstname'];
		$info["sexe"] = $personnage['gender'];
		$info["age"] = $personnage['age'];
		$info["activite"] = $personnage['occupation'];
		return json_encode($info);
	}
	
	if ( isset( $_GET[ "id" ] ) ) {
		echo info( $_GET[ "id" ], $pdo );
	}
	else {
        ......
?>
Voici enfin le code JQuery qui permet de récupérer les informations et de les afficher dans un élément HTML créé pour la circonstance :
$(document).ready(function(){
	
	function update( data, src ) {
		var info = $.parseJSON( data );
		$( "#nom" ).text( info.nom );
		$( "#prenom" ).text( info.prenom );
		$( "#sexe" ).text( info.sexe );
		$( "#age" ).text( info.age );
		$( "#activite" ).text( info.activite );
		$( "#info img" ).attr( "src", src );
	}
	
	function showinfo() {
		var src = $(this).attr( "src" );
		$.get( "exemple-3.php",
			   { id: $(this).attr( "id" ) },
			   function( data, status ) {
			   		update( data, src );
			   		$( "#info" ).fadeIn();
			   }
			 );
	}
	
	$( "#gallery img" ).click( showinfo );
	
	$( "#info" ).click(function() {
		$(this).fadeOut();
	});
		
});
Ce code sera largement commenté en cours.

Les fichiers de l'exemple précédent ainsi que la base données nécessaire pour les exemples et les exercices sont disponibles dans l'archive exemple-3.zip

Exercice 4

Complétez le fichier exo4JQ.js de façon que le script PHP index.php affiche une page web permettant de sélectionner un véhicule de location. Cette sélection se fait succéssivement en sélectionnant d'abord le type de véhicule dans un premier menu déroulant. La sélection du type de véhicule fait apparaître un second menu contenant les marques des véhicules correspondants au type sélectionné. La sélection de la marque du véhicule fait apparaître un troisième menu contenant les modèles de véhicules correspondant au type et à la marque sélectionnés. Enfin, la sélection du modèle correspondant fait apparaître l'image et le nom du véhicule sélectionné. L'exercice sera largement commenté durant le cours.

Fichier(s)

exo4.zip

Solution(s)

exo4JQ.js

Exercice 5

Reprenez l'exercice 4 de la leçon 10, cette fois en rendant la liste persistente côté serveur. Pour ce faire, le serveur stocke maintenant la liste courante des participants dans une base de données (précisément dans la table exo5). Lors de l'ajout d'un nouveau participant dans la table, on stocke l'heure et la date à laquelle ce participant à été ajouté. La date est calculée et ajoutée par le PHP côté serveur. Regardez le script PHP index.php pour voir comment on affiche simplement dynamiquement la liste des participants. Chaque fois que la liste est modifiée à la suite d'un ajout ou d'une suppression (côté client), la base de données est mise à jour via une requête AJAX qui transmet :
  • les nom, prénom et genre en cas d'ajout : dans ce cas, le script invoqué lors de la requête AJAX renvoie au format JSON un objet contenant l'ID et la date du participant ajouté
  • l'ID du particpant à supprimer
Pour réaliser cet exercice, vous devez utiliser la fonction $.parseJSON qui, étant donnée une chaîne de caractères JSON (envoyée par un script PHP) retourne un objet JavaScript. Vous devez également fabriquer des objets JavaScript, ce qui se fait très simplement en utilisant les accolades (les caractères '{' et '}'). Le code JavaScript suivant :
var obj = { name: "Hulk Albert", sex: "homme" };
alert( "Je m'appelle " + obj.name + " et je suis " + obj.sex );
affiche une alerte avec le message Je m'appelle Hulk Albert et je suis homme. L'exercice sera largement commenté durant le cours.

Fichier(s)

exo5.zip

Solution(s)

exo5JQ.js

Exercice 6

Complétez le fichier autocomplete.js de façon que le script index.php affiche une page HTML présentant une zone de saisie (un élément INPUT) avec auto-complétion. Lorsque l'utilisateur ajoute (ou supprime) un caractère dans la zone de saisie, une requête AJAX invoque le script readCountry.php avec comme paramètre la chaîne de caractères déjà saisie par l'utilisateur (c'est à dire un préfixe d'un nom de pays). Le script readCountry.php retourne la liste des noms des pays commençant par la chaîne qui est son paramètre.

Fichier(s)

exo6.zip

Solution(s)

exo6JQ.js