Introduction brève à Javascript

Najib Tounsi

☰ Contenu

  1. Avant Propos
  2. Background
  3. Langage de Base
    1. Le programme "bonjour".
    2. Une deuxième version du même programme
    3. Usage de fonction
    4. Les variables
    5. Les fonctions comme objets
    6. Trucs de base du langage
    7. En savoir plus
  4. Les Objets en JS
    1. Objets prédéfinis
    2. Objets utilisateurs
    3. Notation JSON
    4. JSON comme format d'échange
    5. Objets HTML / DOM
  5. Les événements Javascript
    1. Exemple simple
    2. Un Exemple plus complet
    3. Résumé
  6. Objet XMLHttpRequest
    1. Un premier exemple
    2. Caractéristiques de l'objet XMLHTTPRequest
    3. Autres exemples
    4. Requête asynchrone et méthode POST.
      1. Open asynchrone avec true
      2. Open avec Méthode POST

Avant Propos

Javascript (JS) est un langage de programmation de scripts utilisé pour les pages interactives dans les applications Web. C'est un langage " côté client" (client-side) qui est incorporé au code HTML et exécuté localement par le navigateur. Javascript permet entre autre :

Javascript est fortement événementiel, interprété et souvent compilé à la volée (just-in-time).

Javascript est une implémentation de ECMAScript une spécification de langage de script standard.

Cette petite introduction illustre les caractéristiques générales du langage à travers des exemples simples : les éléments de base (instructions et variables), les objets dans le langage, les événements, le lien avec les éléments HTML (exemple d'objets DOM) et la communication (a)synchrone avec un serveur Web. Des références pour approfondir les concepts présentés complètent les exemples.

N.B.: Ne sont pas abordées ici (hors de propos) les bibliothèques de haut niveau tels que JQuery, ou les APIs spécifiques à des domaines d'applications particuliers comme Node.js pour des applications réseaux, la programmation côté serveur etc.

Background

HTML, programmation cgi, formulaires.

Langage de Base

JavaScript est donc un langage incorporé à HTML dans une page Web. Il est donc exécuté par le navigateur.

Le programme "bonjour".

Exemple 1.1: Programme "Bonjour"

  <html>
...
<body>
<p>Une ligne html </p>
<script type="text/javascript" language="javascript">
document.write("<p> Bonjour de <em>Javascript</em></p>")
;
</script>
</p>Une autre ligne html</p>
</body>
</html>

Le source Javascript est entouré des balises <script>  </script>. Les attributs type et language ne sont pas obligatoires. 

L'instruction document.write("<p> Bonjour de <em>Javascript</em></p>") insère le texte donné entre " " dans la page HTML source. On constatera donc la présence de balises (<p> <em> ici) dans ce contenu ajouté puisque c'est HTML.

En fait, la page affichée par un navigateur est accessible à travers l'objet document.  La méthode write() est appliquée à l'instance actuelle de la page pour y écrire un contenu.

Résultat:

Bonjour de Javascript

(Source)

Une deuxième version du même programme

Exemple 1.1-bis: Insertion de contenu par élément ciblé.

<html>
<body>
<p>Une ligne html </p>
<p id="ici">... paragraphe vide ...</p>
</p>Une autre ligne html</p>
<script>
document.getElementById("ici").innerHTML = "Bonjour de <em>Javascript</em>";
</script>
</body>
</html>

Ici, on cible un élément <p> identifié par le nom "ici" pour lui affecter un contenu. On accède à cet élément avec la méthode getElementById() du même objet document, ensuite on lui affecte un contenu en utilisant la propriété (le champ) innerHTML.

Noter que le script a été mis à la fin de <body>. En tout cas après l'élément ciblé. C'est pour que cet élément soit déjà connu (i.e. déjà analysé par le navigateur).

Remarque : Un script peut aussi se trouver dans la partie <head> d'un source HTML.

Résultat :

Bonjour bis

(Source)

Noter au passage que le texte "... paragraphe vide ..." de la page ne sera jamais affiché visiblement (on n' a pas le temps de le lire), puisqu'il est remplacé.

Exercice : Pour visualiser ce texte, on peut interrompre le script par une instruction alert("Cliquer OK"); avant le remplacement, ce qui a pour effet de bloquer le script jusqu'à appuyer sur un bouton.

Usage de fonction

Exemple 2 : Fonction de conversion des températures F° en C°

<html>
<head>
<title>javascript - exemple2 fonction js</title>
<meta charset="utf-8">
<script>
function fahr2Cel(f){
return 5/9 * (f-32);
}

</script>
</head>

<body>
<p id="ici"> paragraphe vide </p>
<script>
var f = 78;
var c = fahr2Cel(f);
document.getElementById("ici").innerHTML = f
+ "° Fahrenait correspond à: "
+ Math.round (c) + "° Celcius";
</script>
</body>
</html>

Une fonction (ligne 6) de nom fahr2Cel de paramètre formel f est définie dans la partie <head> ici. Elle sera appelée dans un script qui se trouve dans <body>.

Les variables sont introduites par le mot clé var. Il n'y a pas de déclaration de type. En fait, le type diffère  selon la valeur actuelle d'une variable, voir plus bas.

De même que le profile des fonctions : ni les paramètres formels ni le résultat ne sont typés.

Si une fonction est appelée avec moins de paramètres effectif, e.g. fahr2Cel() ici, c'est une erreur à l'exécution. Par contre, si elle est appelée avec plus de paramètres, e.g. fahr2Cel(78, 89),  seul les paramètres utiles à partir de la gauche sont utilisée. 78 ici.

Les variables qui n'ont pas encore reçu de valeur ont le type undefined.

Noter aussi l'usage de la fonction d'arrondi round de la classe Math fournie avec JS.

Résultat :

Fonction JS

(Source)

Les variables

Les variables sont introduites par les mots clés var, let ou const, optionnels. Leur type dépend de leur valeur actuelle

Exemple : On affecte à une même variable différentes valeurs et on affiche sont type avec typeof().

var a;		console.log(typeof(a));
a = 23;		console.log(typeof(a));
a = 2.3; 	console.log(typeof(a));
a = 'toto';	console.log(typeof(a));
a = [1,3]; 	console.log(typeof(a));
a = {};		console.log(typeof(a));
a = function(){}; console.log(typeof(a));

Les résultat est affiché ici dans la console du navigateur avec console.log().

types JS
les différents types de données de l'exemple

Remarque : La console du navigateur est utile pour voir les messages d'erreurs ou pour afficher des messages de mise au points. On la visualise dans le menu Developer sous Tools avec Firefox ou sous View avec Chrome.

La portée des variables est toute la fonction, et non le bloc de déclaration. Par exemple:

function f(){
  var a = 1;
  if (true) {
     var a = 2;
    	//...
    }
   // ...    Ici a vaut 2 et non 1 
}

A la ligne 7 a veut 2 et non 1 comme en C ou Java. Sauf si on met let a = 2; au lieu de var (ligne 4) auquel cas la variable a pour portée le bloc de déclaration. Cette caractéristique (inhabituelle) a été introduite avec la norme ECMAscript-6.

Il y a aussi le déclarateur const pour les constantes immuables.

Les fonctions comme objets

Les fonctions peuvent être considérées comme des objets (de type Function) et affectées à des variables ou passées en paramètres.

Exemple 3 :

<body>
  <script type="text/javascript">
     var action = function (x){
            document.write ("Appel avec: "+x);
            return x+1;
      }
                // ...
      action(5);            // Appel avec: 5
      var y = action (1);
      document.write(y);    // donne 2
  </script>
</body>

Exemple 4 : Fonctions en paramètre

<body>
<script type="text/javascript">
    function action (fct, x, y) {
       var r = fct(x, y);   // paramètre formel fct utilisé comme fonction
       document.write (r);
    }
    function add(x, y) {
        return x + y;
    }
    function mult(x, y) {
        return x * y;
    }
    action(add, 1, 2);       // donne 3
    action(mult, 3, 4);      // donne 12
</script>
</body>

Une fonction peut aussi être retournée en résultat d'une (autre) fonction.

Exemple 5: fonction résultat de return

<body>
<script>
  function bonjour() {
    return function() {
              return "Hello World!";
           };
  }

  hello = bonjour();  // hello est la fonction retournée 
  console.log (hello());  // affichage de la chaîne résultat de hello()

</script>
</body>

Cet exemple là est intéressant pour avoir des valeurs qui persistent d'un appel à l'autre d'une fonction.

Exemple 5-bis: fonction compteur qui incrémente une valeur à chaque appel

<body>
<script>
  function compter() { var cpt = 0; return function() { return ++cpt; }; } transition = compter(); c = transition(); console.log(c) ; // 1
c = transition(); console.log(c); // 2 c = transition(); console.log(c); // 3
</script>
</body>

Entre deux appels, lignes 11, 13 ou 15, la fonction retournée (et qui calcule ++cpt ) a gardé son environnement.

Remarque : Les fonctions en paramètres sont parfois appelées callback function.

Trucs de base du langage

La syntaxe est semblable à celle du langage Java.

Exemple d'instructions for et if avec usage d'un tableau.

Exemple 6 : Programme qui imprime la valeur absolue (calcul manuel) des nombres d'un tableau

<body>
<script>
var t = new Array (2, -3.5, 4, 0);
for (var i=0; i<t.length; i++) {
if (t[i] <0)
t[i] *= -1;
document.write(t[i] + "<br />");
}
</script>
</body>

(Source)

Un tableau t est initialisé par new et énumération des éléments. L'accès est indexé t[i]. Le tableau a la propriété length qui contient sa taille.

On peut aussi parcourir le tableau simplement avec for (i in t)...   sans mettre les trois expressions habituelles. La variable de contrôle prend successivement les valeurs d'indices des éléments initialisés (i.e. sans ceux les éléments encore indéfinis).

La version while de ce programme serait :

var i = 0; 
while (i < t.length) {
if (t[i] <0) t[i] *= -1;
document.write(t[i] + "<br />");
i++;
}

 Exercice :  Dans l'exemple 6, constater qu'on peut mettre un élément chaîne de caractères dans le tableau (var t = new Array(2, "Coucou", 0);).

En savoir plus

Voir http://www.w3schools.com/js/js_loop_while.asp et le reste pour plus de détails.
Syntaxe détallée https://en.wikipedia.org/wiki/JavaScript_syntax

Les Objets en JS

Tout est objet en JavaScript. Les objets sont dynamiques et peuvent recevoir de nouvelles caractéristiques, champs et méthodes. Un objet est vu aussi comme un tableau associatif dont les éléments sont accédés avec la notation objet.champ équivalente à la notation objet['champ']. Voir plus bas objets structurés.

A part les numériques, il y a déjà des objets prédéfinis.

Objets prédéfinis

Objets String 

var mot = new String("Hello World!");
document.write(mot.length, mot.toUpperCase(), mot.indexOf("W"));

// var mot = "Hello World!" // marche aussi, affectation directe  

Objets Date 

var d = new Date();             // Date du Jour
document.write("j = ", d.getDay(), ", m = ",d.getMonth()+1);
Date.now();    // 1576800000000 
// nombre de millisecondes écoulées depuis le 1 janv. 1970 Date.parse("January 2, 1970"); // 86400000, nombre de ms jusqu'à 2 janv. 1970

Objets Array 

var a = new Array ();
a[0] = 1;
a[1] = "Momo";
// idem que
var a = new Array (1, "Momo");
// ou bien
var a = [1, "Momo"]; // Constructeur de type [...]

document.write (a.length, a.indexOf("Momo"));
// donne 2 et 1

Les objets structurés {}

var personne = {nom:"Benali", prénom:"Ali"};
document.write (personne.nom);          // donne Benali
document.write (personne["prénom"]);    // donne Ali
var tarif = {pomme:"20", poire:"25", tomate:"5"};
document.write (tarif["pomme"]);     // donne 20
document.write (tarif["tomate"]);    // donne 5

La classe Math

var x = Math.PI;                // Donne PI
var y = Math.sqrt(16); // Donne 4, racine carré de 16

Objets utilisateurs

Un objet JavaScript est une donnée dynamique avec des propriétés et des méthodes.

Les propriétés contiennent les valeurs des différents champs de l'objet et les méthodes les opérations de manipulation l'objet.

Un objet personne 

var p = new Object();
p.nom = "Benali";
p.prenom="Ali";
p.age = 34;
document.write(p.prenom, " ", p.age);

Méthode vieillir de un an 

  ...    // Ajout de la méthode plusUn à l'objet p
p.plusUn = function() {
this.age += 1; // augmente l'âge d'un an};

p.plusUn();
document.write(p.prenom, " ", p.age); // donne: Ali 35

Un constructeur d'objets 

...
function personne (n, p, t) {
this.nom = n;
this.prenom = p;
this.taille = t;
}

var p = new personne ("Benali", "Aline", 1.80);
document.write(p.prenom, " ", p["taille"]); // donne: Aline 1.8
...
var p = {prenom:"Ali", nom:"Benali", adresse:"Rabat"};
  ...
var p = { prenom:"Ali",
nom:"Benali",
adresse: { rue:"Atlas", no:5 }
};

document.write(p.nom, " Habite à: ", p.adresse.no, " rue ", p.adresse.rue);
// donne: Benali Habite à: 5 rue Atlas

Héritage avec le mot clé prototype 

  ...
var p = new personne ("Benali", "Ali", 1.80); document.write (p.prenom, p.taille); // donne Benali Ali 1.8 personne.prototype.pays = "Maroc"; var q = new personne ("Benali", "Aline", 1.70); document.write (q.prenom, q.taille, q.pays);
// donne Benali Aline 1.7 Maroc
Un sucre syntaxique est la boucle for pour parcourir les propriétés d'un objet

Itérateur abstrait

var p = {prenom:"Ali", nom:"Benali", adresse:"Rabat"}; 
for (x in p) { // Itérateur abstrait
document.write (p[x]+" ");
// donne Ali Benali Rabat, dans cet ordre
}

Tous les objets JavaScript possèdent les propriétés et les méthodes de l'objet spécial Object (qui est en fait un wrapper). En particulier, object.prototype est au sommet de l'arbre d'héritage.

Notation JSON

JSON (pour JavaScript Object Notation) est une notation texte pour décrire les objets Javascript. Son intérêt réside dans son usage pour échanger des données JS entre programmes, par exemple entre clients et serveurs.

C'est en fait un format général de mémorisation et de transmission de données sous forme attribut:valeur, comme en Javascript

Grossièrement parlant la notation JSON définit des objets (récursivement) comme suit  :

  1. Objet simple, valeur ou  clé : valeur. Exemples :
  2. Objet composé de type {liste d'objets séparés par virgule}, lesquels objets sont des objets simples ou non. Exemples :
  3. Objet composé de type [Tableau d'objets séparés par virgule], lesquels objets sont des valeurs  ou des objets. Exemples :

Notation d'accès. Soit les déclarations :

var c = {prenom:"Ali", nom:"Benali", adresse: { rue:"Atlas", no:5 }};  et

var t = ["Ali", "23", 45, {age : 23} ];

Exemple d'objets JSON :

  {
"prenom" : "Ali",
"nom" : "Benali",
"adresse" : {
"rue" :"Atlas",
"no" : 5
},
marié : true,
"enfants" : [
"Fatima",
"Amine"
]
}

présenté en plusieurs lignes avec des espaces autour du caractère   et des guillemets "" pour les noms de champs.

JSON comme format d'échange

Un objet JSON peut être donné sous forme de chaîne de caractères, par exemple donnée à l'exécution. Soit

var q = ' {"prenom":"Ali", "nom":"Benali"} ';  // q chaîne de caractère entre ' '

Il faut convertir cette chaîne vers un objet JavaScript. Avec la méthode parse() de la classe JSON.

var p = JSON.parse(q);

 Dans ce cas, p.nom donnera Benali

Noter que dans la chaîne q, les clés "prenom", "nom", sont aussi entre " ".

L'inverse de cette fonction parse() est stringify(), qui convertit un objet JavaScript en une chaîne de caractère format JSON.

var p = {prenom:"Ali", nom:"Benali", taille:180};

var q = JSON.stringify (p);

la chaîne q peut  maintenant être envoyée à un serveur (e.g. requête POST) pour traitement de l'objet qui y est décrit.

Les données échangées entre un navigateur et un serveur sont sous forme texte.  Comme JSON est un texte, il est donc facile de convertir n'importe quel objet JavaScript en JSON, et envoyer JSON au serveur.

On peut également convertir tout format JSON reçu du serveur en objets JavaScript.

Objets HTML / DOM

Une page HTML est représentée dans la mémoire du navigateur sous forme d'un arbre dont les nœuds sont les éléments et la racine l'élément <html> (c'est à dire le document même).

La figure suivante montre l'arbre correspondant à une page HTML tel qu'il est vu par l'outil de développement (developer tool).

DOM général
Structure générale d'une page HTML :
La racine
html et les deux nœuds  fils head et body

La figure suivante montre un exemple de page simple avec l'expansion de son DOM.

HTML document DOM complet
Page HTML (en vert)  et le DOM détaillé :
Tous les nœuds et leurs descendants (en bleu)

HTML DOM ( Document Object Model) est un standard W3C pour accéder dynamiquement à ces éléments, et les manipuler comme des objets ayant des méthodes pour agir dessus et des propriétés qu'on peut lire ou changer.

HTML DOM est une interface de programmation (API) pour accéder, changer, ajouter ou supprimer des éléments HTML. Cette interface est accessible aussi pour Javascript.

On en a déjà vu un exemple avec l'objet document et les méthodes write() et getElementById(), et l'objet element avec la propriété innerHTML.

La méthode getElementById() et la propriété innerHTML

 <p id="ici">Mon paragraphe</p>
<script>
var par = document.getElementById("ici");
document.write(par.innerHTML + "se poursuit ici ...");
</script>
<p> Fin </p>

Changement du style d'un paragraphe, pour colorer en bleu

 <p id="ici">Mon paragraphe</p>
<script>
var par = document.getElementById("ici");
par.setAttribute("style", "color:blue");
</script>
<p> Fin </p>

Autres méthodes et propriété d'éléments

 <p>Mon paragraphe</p>
<p> Le tien </p>
<p> Dernier </p>
<script>
var pars = document.getElementsByTagName( 'p' );
document.write("Il y a eu:<blockquote>")
for (i=0; i< pars.length; i++)
document.write(pars[i].innerHTML + "<br>");

</script>

Certaines propriétés d'un objet HTML DOM acceptent comme valeur une fonction. C'est intéressant par exemple pour prendre en charge un événement. La propriété onclick permet celà. (voir les événements Javascript plus bas).

Ajouter dynamiquement un gestionnaire d'événement à un élément 

<p id="ici">Paragraphe à cliquer</p>
<script>
document.getElementById("ici").onclick = function(){alert("on a cliqué");}
</script>

Voir (http://www.w3schools.com/jsref/, section HTML DOM Reference) pour une liste complète avec liens vers la spécification de chaque méthode. Ci-après un tableau de quelques objets HTML DOM et quelques méthodes et attributs (http://www.w3schools.com/js/js_htmldom_document.asp)

Table-1 Quelques objets et méthodes HTML DOM

Méthode Description
document.getElementById() Trouver un élément par son identifiant
document.getElementsByTagName() Trouver un élément par son nom
document.getElementsByClassName() Trouver un élément par sa classe
document.forms[] Trouver par son indice l'un des formulaires d'une page
document.createElement() Créer un élément HTML
document.removeChild() Supprimer un élément
document.appendChild() Ajouter un  élément
document.replaceChild() remplacer un  élément
document.write(text) Ecrire dans le flux HTML
element.onclick=function(){code} Ajouter un gestionnaire d'événement à un élément
element.innerHTML Accéder ou changer (par affectation) le contenu HTML d'un élément
element.setAttribute(attribute,value) Changer un attribut d'un élément HTML
element.style
element
.style.property
Accéder ou changer (par affectation)  le style ou la propriété de style d'un élément HTML

Remarque :

Javascript fournit aussi une API HTML Objects (http://www.w3schools.com/jsref/default.asp), qui est propre à JavaScript et qui décrit les propriétés et méthodes de chaque objet HTML. Cette API ne diffère pas beaucoup de DOM HTML.

Les événements Javascript 

Exemple simple

Les actions qu'un utilisateur effectue sur une page constituent des événements auxquels il faut répondre. Par exemple :

Les événement sont décrit par des attributs comme: onclick, onblur, onload, onmouseover, onsubmit, onkeydown,  etc. (attention, tout en minuscule). 

Exemple de trois événements simples 

<body>
<p onmouseover="this.innerHTML +='…<em>Merci</em>!'"> Passer la souris ici</p>
<p onclick="alert('On a cliqué sur l\'élément')"> Cliquer avec la souris ici</p>
<p>Votre nom <input type="text" onblur="this.value=this.value.toUpperCase()"></p>
</body>

Quand la souris passe sur le premier paragraphe, on lui concatène le texte "...Merci!", et quand on quitte le champs de saisie, le texte qu'on a tapé est mis en majuscule. Quand on clique sur le deuxième paragraphe, une fenêtre d'alerte est activée avec le message dedans.

evenement JS    evenement JS2
Illustration de l'exemple précédent:
la page avec les réponses et l'alerte JS.

En réalité, les événements entraînent une action qu'il faut programmer. Ce qui est alors affecté à l'attribut événement correspondant est une action JS. Généralement, cette action est un appel à une fonction qu'on surnomme gestionnaire d'événement (event handler). L'élément auquel est attaché l'événement est accessible à JS par la variable this.

Voir la liste des événements HTML DOM ici (http://www.w3schools.com/js/js_events_examples.asp).

En voici quelques un des plus courants:

onfocus - Quand on pointe sur un champ
onblur - Quand on quitte un champ
onselect - Quand un choix input  est fait
onsubmit - Quand on clique sur un bouton de soumission de formulaire
onreset - Quand on clique sur le bouton d' annulation de saisie de formulaire
onkeypress - Quand on appuie sur une touche
onkeyup - Quand on lâche une touche
onmouseover/onmouseout - Quand la souris passe/quitte un élément
onmousedown/onmouseup - Quand la souris est appuyée/lâchée sur un élément
onmouseover/onmouseout - Quand la souris passe/quitte une image
onclick - Quand un élément est cliqué (généralement un bouton)
ondblclick - Quand un élément est double-cliqué
onload - Quand un page (ou image) est chargée

Un Exemple plus complet

L'exemple suivant est un formulaire où on saisit le montant d'un prêt, la durée en mois et le taux d'intérêt annuel. Le formulaire affiche alors le montant mensuel à rembourser pour amortir le prêt.

Page de simulation des mensualités d'un prêt 

  <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Javascrpit. Amortissement de prêts</title>

<script type="text/javascript">
function calcul(maForm){
tm = maForm.Taux.value/100/12; // Taux mensuel
d = maForm.Duree.value; // La durée
p = Math.pow ((1+tm), -d);
m = tm * maForm.Montant.value / (1 - p);

m = (Math.round(m * 100) / 100).toFixed(2); // formatage 2 decimales

// On ecrit la mensualité calculée dans le champ correpondant du formulaire
maForm.Mensualite.value = m;
}
</script>
</head>

<body>
<form>
Montant du prêt:
<input type="text" name="Montant" value="10000" size="10"><br/>
Durée du prêt en mois:
<input type="text" name="Duree" value="12" size="3"><br/>
Taux :
<input type="text" name="Taux" value="9.25" size="5">%<br/><br/>
<input type="button" value="calculer" onclick="calcul(this.form)"><br/>
<hr>
Mensualités:
<input type="text" name="Mensualite" size="15"> Dh.
</form>
</body>
</html>

(Source)

Ici on a l'objet HTML form qui correspond au formulaire qui contient trois champs données (Montant, Duree, Taux) et un champ résultat (Mensualite). Le formulaire est transmis  par la règle onclick = "calcul(this.form)" à la fonction calcul,  qui fait le calcul nécessaire. Dans cette fonction, on accède aux différents champs du formulaire par  maForm.Taux etc. dont on prend la valeur avec la propriété value, maForm.Taux.value. Une fois le calcul terminé, on inscrit dans le champ maForm.Mensualite le résultat m.

Résultat :

Amortissement de prêt

Exercice : Améliorer ce programme pour afficher aussi le coût totale du crédit et sa répartition en capitale et intérêt. (Source)

Remarque : On peut se servir de ce programme pour simuler chacune des données en fonction des autres. Par exemple, si on a la durée (12 mois), le montant du prêt demandé (12000 Dh) et la mensualité accordée (1060 Dh), on peut varier le taux d'intérêt (entre 10 et 11) jusqu'à tomber sur cette valeur 1060.00. On aura alors 10.896 % comme taux d'intérêt TTC appliqué (en négligeant les frais d'assurance).

Résumé

On a déjà dit que Javascript est fait pour pour la programmation côté client. Il est surtout utilisé avec HTML dans le  cadre des navigateurs Web pour interagir avec l'utilisateur, contrôler les événements navigateur et aussi modifier le contenu du document qui s'affiche.

Cela dit, Javascript permet aussi de communiquer avec des programmes côté-serveur en envoyant des requêtes HTTP. En plus, et grâce à l'objet particulier XMLHttpRequest, la communication peut se faire de façon asynchrone. D'où toute la puissance de JS.

Cette technique est appelée AJAX (Asynchronous JavaScript and XML).

Objet XMLHttpRequest

XMLHttpRequest est un objet qui offre au navigateur Web une API bien adaptée pour envoyer depuis un programme script des requêtes HTTP à un serveur Web.

Avec XMLHttpRequest, un programme n'a pas besoin d'attendre la réponse du serveur, mais peut à la place :

Le schéma de principe est le suivant :

(1) Evénement (formulaire)
(2) Requête GET/POST au serveur -------> traitement serveur (3)

(5) Affichage nouvelle <------- réponse serveur (4)
page HTML retournée
(1) Evénement (formulaire)
(2) Création objet XMLHttpRequest
(3) Objet envoie requête GET/POST
(a)synchrone au serveur -------> traitement serveur
(4)
(6) Récupération réponse, <------- réponse serveur (5)
(7) Traitement et insertion résultat
dans la page déjà affichée

La réponse reçue du serveur est de type texte. Cela peut être  XML, JSON, HTML ou simple texte non formaté.

Un premier exemple

Un programme JS va faire une requête GET et attendre la réponse du serveur. La réponse est un simple texte "bonjour", produit par un programme Python (voir aussi ici).

Exemple 6.1 : Requête GET et insertion dans la page de la réponse du serveur

<body>
<p>Réponse du serveur : <span id ="ici">Pas encore de réponse ... </span> </p> <script type="text/javascript"> /* Je crée mon objet XMLHttpRequest */ var myXMLHttp=new XMLHttpRequest(); var uri="/~ntounsi/cgi-bin/Ajax/hello.py"; /* J'envoie ma requête GET synchrone (false) */ myXMLHttp.open("GET", uri, false); /* envoi synchrone */ myXMLHttp.send(null); /* Après attente et au retour du serveur, on exécute: */ document.getElementById("ici").innerHTML = myXMLHttp.responseText; </script>
</body>
  1. Une instance XMLHttpRequest est créée (ligne 7).
  2. On positionne ensuite une URI vers la ressource demandée (ligne 8). Ici un programme cgi-bin en langage Python.
  3. On prépare ensuite la requête client GET avec open() paramétrée par l'URI demandée (ligne 11).  false indique que la programme se bloquera pour attendre le retour du serveur (cas synchrone).
  4. Enfin on envoie effectivement la requête avec send() sans paramètre donnée (ligne 12).
  5. La réponse responseText obtenue du serveur est ensuite insérée dans la page à l'endroit souhaité (ligne 15).

Résultat:

Exemple 1 Ajax

Programme serveur ayant produit la réponse:

#!/usr/bin/python3

print ("Content-type:text/html; charset=UTF-8\n"); 
print ("Bonjour...");
print ("Contenu texte produit par Python");

Exercice-6.1: Tester cet exemple.

Caractéristiques de l'objet XMLHTTPRequest

On va illustrer cela par quelques exemples simples.

Autres exemples

On va rajouter au programme 6.1 précédent un test au retour du serveur. Si l'état readyState de la requête est 4 (on a la réponse du serveur) et le statut status de celle-ci est  200, alors OK on effectue le traitement.

Exempe 6.2 : Même que Exemple 6.1 avec test au retour du serveur 

<script type="text/javascript">
 /* Je crée mon objet XMLHttpRequest */
  var myXMLHttp = new XMLHttpRequest();
  var uri = "/~ntounsi/cgi-bin/Ajax/hello.py";

 /* J'envoie ma requête GET synchrone (false) */
  myXMLHttp.open("GET", uri, false); /* envoi synchrone */
  myXMLHttp.send(null);

 /* Après attente, on teste le retour du serveur */
  if (myXMLHttp.readyState == 4 && myXMLHttp.status == 200) {
      document.getElementById("ici").innerHTML = myXMLHttp.responseText;
  } else {
      console.log("Erreur: " + myXMLHttp.readyState +
          " " + myXMLHttp.status);
  }
</script>  

NB. Il faut toujours faire ce test ( ligne 11) pour savoir si le retour du serveur est OK.

Exercice-6.2: Vérifier les cas d'erreur 404. Dans l'URI, mettre fichier qui n'existe pas.

Dans les exemples précédents on a mis l'adresse de la ressource (URI) littéralement dans la requête open() uri="/~ntounsi/cgi-bin/Ajax/hello.py". Cette adresse peut être calculée dynamiquement, par exemple pour y mettre des valeurs saisies dans un formulaire.

L'exemple suivant fait usage d'un formulaire pour saisir un nom, et l'envoyer dans la requête GET ainsi uri="/~ntounsi/cgi-bin/Ajax/helloAli.py"?nom=Alia".

Exemple 6.3 Données saisies dans un formulaire et envoyées au serveur pour traitement 

  <body>
<p style="float:right; width:65%;">Réponse du serveur... :
<span id ="ici">Pas encore de réponse</span
</p>

<form>
Rentrez votre nom <input name="nom" value="BenAli"> <br />
<input value="Soumettre" type="submit"
onclick = "traiter(this.form); return false;" >
</form>

</body>

correspondant à :

Ajax exemple2

Quand on clique sur soumettre on obtient :

Ajax Exemple 3

où le traitement serveur a consisté à retourner un texte disant bonjour au nom qui a été saisi, texte affiché ensuite dans la page à l'endroit indiqué.

Dans le source ci-dessus, on a l'événement onclick="traiter(this.form); return false;"  qui fait appel à la fonction traiter () suivante placée dans le <head> de la page : 

Exemple 6.3-bis : Fonction qui traite la forme précédente 

<script type="text/javascript">
function traiter(maForm) {
    /* On récupère la donnée du formulaire */
    var str = maForm.nom.value;

    /* on envoie au serveur pour calcul... */
    var myXMLHttp = new XMLHttpRequest();
    var uri = "/~ntounsi/cgi-bin/Ajax/helloNom.py?nom=" + str; // noter + str

    myXMLHttp.open("GET", uri, false);
    myXMLHttp.send(null);

    /* On traite la réponse */
    if (myXMLHttp.readyState == 4 && myXMLHttp.status == 200) {
        document.getElementById("ici").innerHTML = myXMLHttp.responseText;
    } else {
        console.log("Erreur : " + myXMLHttp.readyState +
            " " + myXMLHttp.status);
    }
}
</script> 

Remarque: return false; dans l'événement onclick précédent (Exemple 6.3) est là pour empêcher que le comportement par défaut du gestionnaire d'événement du navigateur ait lieu aussi après l'appel à traiter(). Cela se produit avec un formulaire avec le type de bouton "submit". Ce n'est pas le cas du type  "button" par exemple. (Exercice : le vérifier sur l'exemple.)

Programme serveur ayant produit la réponse :

#!/usr/bin/python3

import cgi, cgitb 
form = cgi.FieldStorage() 
nom = form.getvalue('nom')
print ("Content-type: text/html; charset=UTF-8")
print()
print ("Bonjour <em> %s </em>" % (nom))  

Exercice-6.3 :
1- Changer le programme Python pour décorer le nom avec une couleur différente, color:blue; par exempel.
2- Vérifier qu'avec "button", on peut retirer return false; dans l'événement onclick. (question juste ci-dessus.)

Requête asynchrone et méthode POST.

Open asynchrone avec true

Dans les exemples précédents le programme se bloque après send() et ...  attend la réponse du serveur. Tout de suite après dans le code on test si readyState est 4 (il y a une réponse prête) et si status est 200 (c'est "OK").  Cela convient bien pour de petites requêtes.

Au lieu de se bloquer, le browser (le script en fait) peut continuer à faire autre chose ou à interagir avec l'utilisateur. On envoie alors une requête asynchrone, avec la valeur true pour le dernier paramètre de open(). Dans ce cas, on a  besoin de savoir durant ce temps où en est sa requête pour récupérer la réponse quand elle arrive. Pour cela, il y a la propriété readyState qui prend différentes valeurs (0, 1, 2, 3, 4) selon la progresssion de la requête. Chaque changement de valeur est un événement qu'on peut gérer avec la règle onreadystatechange, en lui  associant un handler (fonction JS) qui fera l'action nécessaire ou souhaitée à ce stade.

Dans le programme suivant, on a armé la règle onreadystatechange avec une fonction qui contient le même code que précédemment, à savoir tester si la valeur l'avancement est 4 (réponse prête) et si le status est 200 ("OK"),  et récupérer la réponse dans la valeur de reponseTexte

Exemple 6.4 : envoie asynchrone avec attente sur onreadystatechange 

<script type="text/javascript">
function traiter(maForm) {
    /* On récupère la donnée du formulaire */
    var str = maForm.nom.value;

    var myXMLHttp = new XMLHttpRequest();
    var uri = "/~ntounsi/cgi-bin/Ajax/helloNom.py?nom=" + str

    /* On définit l'événement quand la réponse arrive */
    myXMLHttp.onreadystatechange = function() {
        if (myXMLHttp.readyState == 4 && myXMLHttp.status == 200) {
            document.getElementById("ici").innerHTML = myXMLHttp.responseText;
        } else {
            console.log("Attente... : " + myXMLHttp.readyState +
                " " + myXMLHttp.status);
        }
    };

    /* On envoie an asynchrone (true) ...*/
    myXMLHttp.open("GET", uri, true);
    myXMLHttp.send(null);
    /* 
      Suite du programme en attendant
      ... 
    */
}
</script> 

Un gestionnaire de l'événement onreadystatechange est défini par la fonction entre les lignes 10 et 18. Son code est semblable à celui de l'exemple précédent, où on teste si tout s'est bien passé bien quand la réponse est prête, pour ensuite insérer cette réponse dans l'endroit prévu dans la page Web. Le résultat est :

Exemple 6.4-bis : Résultat de la page précédente

OnreadyStateChange

Noter la sortie sur console (partie else ligne 14), des valeurs 1, 2, 3 successives de readyState ainsi que les valeurs status 200. (A visualiser dans "console" du menu "developer" de votre navigateur)

Exercice : &&détailler onready... avec swith pour afficher un méssage différent pour cas 1, 2, 3 (solution exemple suivant avec POST)

Open avec Méthode POST

Se rappeler que la méthode HTTP POST est utilisée en général quand on n'attend pas de réponse du serveur (sinon un message du genre accusé de réception), et quand on envoi des données servant à être traitées par des applications serveur.

Dans l'exemple suivant justement, ces données seront celles d'un formulaire sous la forme "nom=...&prenom=..." (normalement envoyés avec l'url de GET), qui sera envoyée en paramètre de la méthode send(). On reprend le même formulaire que précédemment auquel on a rajouté une zone libellée prénom.

Exemple 6.5 Données saisies dans un formulaire pour envoie par POST au serveur 

<body>
    <p style="float:right; width:65%;">Réponse du serveur... :
        <span id ="ici">Pas encore de réponse</span>
    </p>

    <form>
      <p>Rentrez votre nom  <input name="nom" value="BenAli"> </p>
      <p>Rentrez votre prénom  <input name="prenom" value="Alia"> </p>
       <input value="Soumettre" type="submit" 
               onclick = "traiter(this.form); return false;" >
    </form> 

Voici le code Javascript associé où

Exemple 6.5-bis code AJAX avec méthode POST 

    <script type="text/javascript">
        function traiter(maForm){
            /* On récupère les données du formulaire */
            var str1 = maForm.nom.value; 
            var str2 = maForm.prenom.value;  

            /* on envoie au serveur pour calcul... */
            var myXMLHttp=new XMLHttpRequest();            
            var uri="/~ntounsi/cgi-bin/Ajax/helloNomPrenom.py";

            myXMLHttp.open("POST", uri, true);

            myXMLHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

            myXMLHttp.onreadystatechange = function(){ 
               /* On suit les réponses */
            switch(myXMLHttp.readyState) { 
              case 2:
               console.log("2 : Requête envoyée..." );
               break;          
              case 3:
               console.log("3 : Traitement serveur..." );
               break; 
              case 4: 
               console.log("4 : réponse reçue");
               if (myXMLHttp.status==200){
                 document.getElementById("ici").innerHTML =  myXMLHttp.responseText;
                } else {
                  console.log("Erreur-1 : "+myXMLHttp.readyState +
                               " " +myXMLHttp.status);
                }
                break;
              default:
                console.log("Erreur serveur!");
              }               
            };

            var queryString = "nom="+str1+"&"+"prenom="+str2;
            myXMLHttp.send(queryString);
       }
     </script>  

A noter :

Résultat :

Méthode Post

où on voit en bas le suivi de l'état de la requête.


That's all Folks 😉