Introduction brève à Javascript
Najib Tounsi
Lien permanent : http://www.mescours.ma/TechWeb/JS/JavaScript.html
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.
JavaScript est donc un langage incorporé à HTML dans une page Web. Il est donc exécuté par le navigateur.
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:
(Source)
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 :
(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.
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 :
(Source)
Les variables sont introduites par les mots clés var
, let
ou const
, optionnels. Leur type dépend de leur valeur
actuelle
undefined
si non initialisée,number
si c'est un nombre,string
si c'est une chaîneobject
si c'est un tableau ou une structure (voir plus
bas)function
si c'est une fonction (voir ci-après)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()
.
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 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>
action
.
On utilise ce nom pour l'appeler (ligne 8).action = function f (x) {...}
, est aussi
possible où f
est le nom déclaré de la fonction. action2 = f ;
(f sans
parenthèses). action2 est encore un autre alias pour la même
fonction . 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>
action
(ligne 3) est déclarée avec un
paramètre formel fct
qui est utilisé comme une fonction,
laquelle est appelée ensuite (ligne 4). Les lignes 13 et 14 montrent les
appels à action
avec comme paramètres effectifs les
fonctions add
et mult
définies auparavant.fct
: pour rappel, l'appel fct (x)
serait une erreur et
fct (x,y,z,..)
donnera le résultat attendu en utilisant x
et y.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>
bonjour
retourne une fonction qui, elle,
retourne la chaîne donnée. C'est équivalent à :var f = function(){ return "Hello World!";}
suivi de return
f;
."Hello World!"
.console.log
( bonjour()() );
?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.
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);
).
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
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 String
var mot = new String("Hello World!");
document.write(mot.length, mot.toUpperCase(), mot.indexOf("W"));
// var mot = "Hello World!" // marche aussi, affectation directe
12
, HELLO WORLD!
, 6
.'Hello World!'
ou une
double quotte "Hello World!"
+
sert à la concaténation : 'Hello'
+ ' World!'
https://www.w3schools.com/jsref/jsref_obj_string.asp
)
pour plus de détails. Objets Date
var d = new Date(); // Date du Jour
document.write("j = ", d.getDay(), ", m = ",d.getMonth()+1);
j = 3
et m = 4
pour
le 3 Avril.now()
et parse()
,
appelée comme suit :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
http://www.w3schools.com/jsref/jsref_obj_date.asp
)
pour en savoir plus sur l'objet Date
.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
typeof a[0]
est number
et typeof a[1]
est string
.a[3]=3;
(en sautant a[2]
) la taille deviendrait 4. Avec a[2]
valant undefined
de même que typeof a[2]
.http://www.w3schools.com/jsref/jsref_obj_array.asp
.Les objets structurés {}
{}
.
Un tel objet se comporte en tableau à accès associatif .var personne = {nom:"Benali", prénom:"Ali"}; document.write (personne.nom); // donne Benali document.write (personne["prénom"]); // donne Ali
personne.nom
que par
personne["nom"]
. La dernière notation, accès associatif,
sied bien à l'exemple suivant :var tarif = {pomme:"20", poire:"25", tomate:"5"}; document.write (tarif["pomme"]); // donne 20 document.write (tarif["tomate"]); // donne 5
La classe Math
Math
est un objet qui ne possède pas de constructeur, on
ne crée pas d'instances. C'est en fait une classe qui détient des
constantes et les méthodes mathématiques.var x = Math.PI; // Donne PI
var y = Math.sqrt(16); // Donne 4, racine carré de 16
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);
Ali 34
.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
plusUn
définie et attachée à p
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
personne()
est utilisée pour initialiser un
objet avec les champs qui y sont définies....
var p = {prenom:"Ali", nom:"Benali", adresse:"Rabat"};
http://fr.wikipedia.org/wiki/JSON
).
Elle est intéressante car facile, concise et permet l'imbrication. ...
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
p.adresse.rue
.var p = {prenom:"Ali",
nom:"Benali", adresse:"Rabat"};
,var p = new
personne ("Benali", "Aline", 1.80);
. La première définit (en
direct) et crée un objet avec ses champs.prototype
permet de rajouter plus tard la
propriété pays
aux nouveaux objets personne
. 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
pays
est ainsi hérité par le nouvel objet q
.q = {nom:"Benali",...}
,
le prototype ne marchera évidemment pas.for
pour parcourir les
propriétés d'un objetIté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
}
for (variable de contrôle in objet)
{...}
pour accéder un à un à les tous les champs.document.write
(x);
. (Réponse : les noms de champs. Si c'était
un tableau ce serait les indices des éléments définis
(différence par rapport à for (i = ...). )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.
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 :
"Amina"
nom : "Brahim"
age : 23
{prenom:"Ali", nom:"Benali", adresse:"Rabat"};
{prenom:"Ali", nom:"Benali", adresse: {rue:"Atlas", no:5} }
["Casa", "Rabat", "Kenitra" ]
["Ali", "23", 45, {age : 23} ]
noter les différents
types dans un même tableau.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} ];
c.nom
, pour accéder au nomc.adresse.no
, pour accéder au numéro de
l'adresse.c["nom"]
, accès associatif, la valeur de la
structure indexée par la champ clé "nom"
. Très
utile si la clé n'est connue qu'à l'exécution; e.g. provenant d'un
formulaire ou d'une BD.s = "nom"; // supposons connu à l'exécution
document.write("le champ demandé est ", c[s]);
donnera Benalit[0]
est Ali,
t[2]
est 45t[3].age
est 23
(ainsi que t[3]["age"]
)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.
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.
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).
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 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>
pars[]
, indexée comme
un tableau.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 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.
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
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 :
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).
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).
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 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>
Résultat:
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.
On va illustrer cela par quelques exemples simples.
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 à :
Quand on clique sur soumettre on obtient :
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.)
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
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)
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ù
open("POST", uri, true);
.onreadystatechange
pour
illustrer les étapes de la requête.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 :
setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
pour indiquer que les
valeurs sont encodées sous forme de couples clé=valeur
séparés par '&
'. En effet, avec GET
, ces
données apparaissent dans l'URL dans ce format. (cf. le
paramètre QUERY_STRING
de l'interface cgi).queryString =
"nom="+str1+"&"+"prenom="+str2;
qui sera paramètre de send().
En effet, avec POST
les données sont postées avec la
fonction send(). (Se rappeler qu'avec GET
, cette
fonction n'envoie rien.)Résultat :
où on voit en bas le suivi de l'état de la requête.
That's all Folks 😉