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 :
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 :
-
invoquer le script PHP
exemple-1.php et récupérer le prochain proverbe
-
faire disparaitre l'élément contenant le proverbe courant actuellement visible
-
remplacer le contenu de cet élément par le nouveau proverbe
-
faire apparaitre l'élément contenant le nouveau proverbe
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 :
-
l'URL (relative) du script PHP invoqué
exemple-1.php
-
une fonction callback (ici, anonyme) qui est appelée automatiquement lorsque la requête est achevée,
c'est à dire lorsque le script a terminé et envoyé son résultat au client
Lorsque la fonction callback est appelée, ses paramètres effectifs sont les suivants :
-
data : c'est une chaîne de caractères qui est la valeur produite par le script PHP invoqué
(ici le nouveau proverbe retourné par exemple-1.php)
-
status : c'est un code (un entier) qui indique le status de la requête
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 :
-
associer la fonction
definition à l'évènement click sur tous les éléments de classe
definition (les termes dont la définition peut être visualisée)
-
ajouter le style CSS nécessaire à ces mêmes éléments afin qu'ils puissent réagir au survol du pointeur
-
associer une fonction anonyme à l'évènement
click sur l'élément HTML récepteur qui va contenir
les définitions
$(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 :
-
une valeur litérale :
null, true, false, une chaîne de caractères, un nombre
-
un tableau : délimité par des crochets
[ ], i peut contenir des éléments JSON quelconques (et donc,
récursivement des tableaux)
-
un objet : un objet est une liste de paire attribut : valeur délimitée par des accolades
{ }
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 :
-
convertit la chaîne de caractères
j qui représente un tableau JSON en tableau d'objets JavaScript
-
affiche le contenu de chacun des objets contenus dans ce tableau, en accédant aux attributs
nom
et prenom
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 l'invoque sans paramètre, il affiche la page HTML montrant le galerie des personnages
-
si on l'invoque avec le paramètre
GET id, il retourne les données relatives au personnage
correspondant à cet id au format JSON
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