Aide mémoire: S2 - R2.02 - JavaScript

Sujet 1

Sujet 2

Sujet 3

Sujet 4

Sujet 5

Sujet 7

Sujet 8

Sujet 9

Sujet 1

JavaScript dans la console

 JavaScript est un langage de programmation déjà intégré dans votre navigateur !

Vous n'avez pas besoin d'installer d'autres logiciels... il suffit d'ouvrir la console 😀

Ouvrez la console:
Ctrl + Maj + K sur Firefox
Ctrl + Maj + J sur Chrome

Dans la console tapez: 1+1 suivi d'entrée


Boucles for

Pour créer des boucles simples en JavaScript on peut utiliser "for".
"for" s'écrit comme ceci:

for(initialisation; condition pour continuer; code fin de chaque itération) {
...
}

Cette boucle est équivalente à :

initialisation
while(condition pour continuer){
    ...
   code fin de chaque itération
}

Très souvent, la boucle "for" est utilisée pour parcourir des nombres.

Par exemple:
for(let i=0; i < 10 ; i++) {
...
}

ceci est équivalent à:

let i=0;
while(i<10) {
   ...
   i++;
}

Quand vous avez ces 3 parties (initialisation, condition, fin), c'est plus propre (plus lisible) d'utiliser une boucle for qu'une boucle while.

En une seule ligne, écrivez une boucle for sur i qui affiche dans la console tous les nombres de 0 à 5  (donc 0 1 2 3 4 5).
(Essayez dans la console avant de répondre)

Fonction valeur

Attention: probablement nouveau pour vous ... et très utilisé !  Important.

En JS, une fonction est une valeur qui peut être, par exemple, mise dans une variable.

function ceci_est_une_fonction(x)
{
    console.log(x);
}
// On met une référence de la fonction dans la variable a:
let a=ceci_est_une_fonction;
// On peut donc appeler "a" comme une fonction:
a('hello');

Ce sont les parenthèses qui distinguent l'appel d'une fonction de sa "valeur" (en fait une référence vers la fonction):

Prenons les deux fonctions suivantes:

function affiche(x) { console.log(x); }
function ma_fonction()
{
document.body.style.color='green';
}

Fonction en paramètre d'une fonction

Comme une fonction est une "valeur", on peut aussi la passer en paramètre à une autre fonction.
Ça semble tiré par les cheveux, mais c'est très utilisé en JS  ... et on l'a même déjà fait ensemble ! 😲
Souvenez-vous:

document.querySelector('h1').addEventListener('click',ma_fonction);
function ma_fonction()
{
document.querySelector('img').src='https://moodle.iutv.univ-paris13.fr/img/bjs/smile.png';
}
On passe "ma_fonction" en 2e paramètre de la fonction "addEventListener".

Ceci demande au navigateur: "Enregistre la fonction ma_fonction sur le h1, pour qu'elle puisse être appelée, plus tard, quand l'utilisateur clique sur ce h1"

Voyons un autre exemple. Que fait le code suivant ?

function f()
{
    console.log('Bonjour');
}

function g(x)
{
    x();
}

g(f);


HTML et arbre

Le HTML est un format texte, mais il est interprété par le navigateur comme un arbre.

Chaque noeud de l'arbre correspond à une balise. Dans le navigateur, chaque noeud sera un objet qu'on pourra manipuler en JS.
L'ordre des balises compte.

Le texte contenu dans chaque balise est représenté par un noeud « Text ». En général, on ne montrera pas sur les schémas les noeuds « Text ».

C'est très important d'avoir toujours en tête la correspondance HTML <=> arbre

DOM

Le navigateur reçoit et analyse le HTML. En mémoire, il crée des objets (au sens Programmation Orientée Objet) correspondant à chaque balise. Ces objets sont organisés dans un arbre.

Dans cet exemple « i » est l'objet DOM correspondant à une image.
« i » est un objet avec des propriétés. On y accède comme ceci i.nomProprieté
Certaines propriétés correspondent aux attributs HTML (style, src...):

on peut lire et parfois modifier ces propriétés

Sélecteurs CSS

http://moodle.iutv.univ-paris13.fr/img/web/webs1-cm-1-17-1.png

En JavaScript, on utilise souvent les « sélecteurs ».

Vous avez appris à utiliser les sélecteurs en CSS. Les sélecteurs permettent de désigner depuis le CSS des éléments HTML (des éléments dans l'arbre DOM). On va aussi les utiliser en JavaScript pour designer les éléments du DOM.



Événements

Presque tous les programme JS que l'on va écrire réagiront à des événements.
Le navigateur gère de nombreux types d'événements. En voici quelques-uns:


addEventListener

Regardons en détail ce que fait

b.addEventListener('click',fonction_a_appeler);
Important :

Dans ce cours, addEventListener sera presque toujours appelée au chargement de la page.
"fonction_a_appeler" n'est pas appelée au chargement de la page. Elle n'est appelée que plus tard, lorsque l'utilisateur clique avec la souris. On appelle cette fonction un « gestionnaire d'événement » (« event listener » en anglais).
addEventListener ne fait que enregistrer la fonction "fonction_a_appeler" sur le bouton.

Informations à propos d'un événement

document.addEventListener('click',fonction_a_appeler);
function fonction_a_appeler()
{
console.log('coucou');
}

Quand l'utilisateur clique, le navigateur appelle fonction_a_appeler. Le navigateur fournit un paramètre qui contient des informations sur l'événement. Par exemple:

Jusqu'à la, on n'avait pas indiqué de paramètre à fonction_a_appeler. Le navigateur l'appelait avec ce paramètre, mais notre fonction l'ignorait. Fournissons maintenant un paramètre. On peut le nommer comme on veut, par exemple "event", ou "e" (pour faire court).

document.addEventListener('click',fonction_a_appeler);
function fonction_a_appeler(event)
{
console.log('coucou',event.pageX,event.pageY);
}
Cliquez sur la page et regardez la console.

Que fait le JavaScript ?

Le JavaScript est un langage de programmation. Dans ce cours, le JS s’exécute uniquement dans le navigateur.

Un exemple simple:
Vous cliquez sur un lien pour visiter une page.

  1. Le navigateur reçoit le HTML de la page. 
  2. Le navigateur transforme le HTML en un arbre DOM. Il ne garde pas le HTML.
  3. Le navigateur construit l'affichage à partir de l'arbre DOM
  4. Le navigateur exécute du JS. Ce JS modifie le DOM. Le JS ne modifie pas le HTML (qui n'existe plus pour le navigateur).
  5. Le navigateur met à jour l'affichage, suite aux modifications faites par le JS dans le DOM
On dit souvent (même dans ce cours) "Le JS modifie le HTML". Ce n'est pas exact. En réalité, le JS modifie le DOM, pas le HTML.


Grandes lignes d'un programme JS

Exemple HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Exemple</title>
</head>
<body>
<h1>Cliquez-moi!</h1>
<p>Bonjour!</p>
<script>
let abc=document.querySelector('h1');
abc.addEventListener('click',fonction_a_appeler);
function fonction_a_appeler()
{
let xyz=document.querySelector('p');
xyz.style.color='green';
}
</script>
</body>
</html>

La plupart des programmes JS qu'on écrit ici font les opérations suivantes:

1) Chercher des éléments DOM
Pour pouvoir manipuler en JS les éléments DOM de la page (h1,p, ...) il faut d'abord les chercher.
Par exemple:
let abc=document.querySelector('h1');
Ceci veut dire « Bonjour "document", cherche le premier h1» ... puis met ce h1 dans la variable "abc"
Maintenant, qu'on a l'élément h1 dans la variable abc on, peut faire des choses avec.

2) Enregistrer des fonctions « gestionnaires d'événements »

Les programmes JS vont réagir à des événements (click souris, touche clavier, mouvement souris ...)
Ces événements surviennent toujours sur un élément DOM (h1, p, ....) précis.
Pour réagir, on enregistre notre fonction sur cet élément DOM:
Si on veut réagir à un click sur cet h1, il faut enregistrer notre fonction sur cet h1.
Exemple :
abc.addEventListener('click',fonction_a_appeler);
on enregistre la fonction « fonction_a_appeler » sur « abc » (« abc » est la variable qui fait référence au h1).
Cette opération (enregistrer la fonction) se fait, en général, au chargement de la page.

3) Réagir à un événement

Plus tard (bien après le chargement de la page), quand le navigateur s’aperçoit d'un événement sur un élément il regarde si une fonction (gestionnaires d'événements) est enregistrée sur cet élément. Si c'est le cas, le navigateur appelle la fonction.
Exemple:
L'utilisateur clique sur h1. Le navigateur voit que fonction_a_appeler est enregistrée sur ce h1. Il appelle cette fonction.

Quand est executé le JS ?


1: <!DOCTYPE html>
2: <html lang="fr">
3: <head>
4: <meta charset="utf-8"/>
5: <title>Exemple</title>
6: </head>
7: <body>
8: <h1>Cliquez-moi!</h1>
9: <p>Bonjour!</p>
10: <script>
11: let abc=document.querySelector('h1');
12: abc.addEventListener('click',fonction_a_appeler);
13: function fonction_a_appeler()
14: {
15: let xyz=document.querySelector('p');
16: xyz.style.color='green';
17: }
18: </script>
19: </body>
20: </html>

Au chargement de la page, le navigateur reçoit le HTML et crée le DOM au fur et à mesure.
Si le navigateur voit une balise ouvrante, il la crée dans le DOM. Si cette balise est <script> alors il exécute le JS immédiatement.

Les lignes 11 et 12 sont donc exécutées dès le chargement de la page.
Les lignes 13,14,15,16,17 forment juste une déclaration de fonction. Cette fonction n'est pas exécutée au chargement de la page.
Remarquez que la ligne 12 enregistre la fonction "fonction_a_appeler" sur abc (h1).

A la ligne 18, notre script a fini de s’exécuter. On est toujours au chargement de la page.

Chaque fois que l'utilisateur clique sur le h1, "fonction_a_appeler" est appelée. Ceci arrive bien après le chargement de la page.

Créer un élément


Jusqu'à là, on a vu comment modifier un élément (une balise existante sur une page). Par exemple, on a changé sa couleur (avec ...style=).
Maintenant voyons comment créer puis ajouter un élément.

On le fait généralement en plusieurs étapes, comme le montre le schéma:
  1. La situation initiale
  2. Création d'un élément en mémoire, mais pas encore dans le document (la page)
  3. Changement des propriétés de cet élément que l'on vient de créer
  4. Insertion de l'élément crée dans l'arbre DOM

Position absolute

On voudrait pouvoir positionner l'image "splat.png" en indiquant des coordonnées x et y.
Pour pouvoir le faire on utilise la propriété CSS "position".


Quand un élément (ici l'image) a « position: absolute », il n'affecte plus du tout le positionnement des autres éléments ou texte autour. Il s'affiche donc « par-dessus » ou « par-dessous » les éléments l'entourant.

Tout seul « position: absolute » n'est pas utile ici. Par contre, une fois qu'un élément est « position: absolute », on peut le déplacer facilement avec les propriétés  CSS "left" et "top" (ce qui correspond à x et y):



Sujet 2

NodeList

Oui, un NodeList est une liste de Node.

let tds=document.querySelectorAll('td');

Ici, chaque Node est un élément "td".
On peut parcourir un NodeList comme un tableau. Sa longueur est donnée par length:   tds.length

Écrivez une boucle for où une variable i va de 0 au nombre d'éléments du tableau.
(laissez le contenu de la boucle vide pour l'instant)

Fonctions anonymes

Jusqu'à là, on a utilisé des fonctions pour les événements comme ceci:

document.addEventListener('click', fonction_a_appeler);
function fonction_a_appeler() {
   console.log('bonjour!');
   ...
}

En JavaScript il est possible de créer des fonctions sans nom, donc "anonymes":

function() {
   console.log('bonjour!');
  ...
}

Mais alors, comment est-t-elle appelée ? A quoi sert-elle ?
On pourrait mettre une référence vers cette fonction dans une variable, puis appeler la variable:

let a=function() {
   console.log('bonjour!');
  ...
}
// appel de la fonction:
a();

... mais c'est plus long que d'utiliser une fonction habituelle.
On pourrait aussi envoyer la fonction anonyme directement en paramètre à une fonction qui en a besoin... par exemple addEventListener:

document.addEventListener('click', function() {
   console.log('bonjour!');
   ...
});
Remarquez bien les accolades et la parenthèse fermante.
On vient de faire la même chose qu'au début, mais sans avoir à donner un nom à la fonction. Par ailleurs, cette écriture est plus courte. On n'a plus à gérer la fonction à deux endroits séparés.
En pratique, les fonctions anonymes sont très utilisées. Pour addEventListener, on utilisera le plus souvent une fonction anonyme. On n'utilisera des fonctions avec un nom que quand elles sont trop longues ou que la fonction doit être réutilisée à un deuxième endroit.



Class

L'attribut "class" est très utilisé en HTML, CSS et JS.
Ici, on va voir comment le manipuler en JS.

Exemple:

<p class="important">Lina a gagné !</p>

Il permet ensuite de spécifier la mise en forme dans le CSS:

.important { 
   color: red;
   font-style: italic;
}

On peut avoir plusieurs classes pour un même élément. Elles sont séparées par des espaces:

<p class="important actu encart">Lina a gagné !</p>
Elles sont bien vues par le CSS comme des classes différentes:

.important { 
   color: red;
   font-style: italic;
}

.actu {
background-color: #ffe;
}

.encart {
border: 1px solid #aaa;
}

Un élément peut avoir à la fois des classes et un id:

<p id="para3" class="important actu encart">Lina a gagné !</p>

CSS:


#para3 {
margin-top: 1em;
}

.important {
   color: red;
   font-style: italic;
}

.actu {
background-color: #ffe;
}

.encart {
border: 1px solid #aaa;
}

Manipulation en JS

Supposons qu'on veuille manipuler la classe de para3:

let p=document.querySelector('#para3');
Attention: pour accéder à "class" on ne peut pas utiliser p.class
car "class" est un mot clé en JS. On doit donc utiliser p.className

  • On peut lire className :  console.log(p.className) => "important actu encart"
  • On peut écrire dans className :  p.className="abc". Ceci change tout l'attribut class:
<p id="para3" class="abc">Lina a gagné !</p>


classList

Quand on doit gérer plusieurs classes, className devient pénible à utiliser.
<p id="para3" class="important actu encart">Lina a gagné !</p>
Typiquement, on voudrait ajouter ou enlever une classe.  On veut aussi savoir si une classe est présente.

classList permet de le faire :

let p=document.querySelector('#para3');
p.classList.add('abc');
on a maintenant:

<p id="para3" class="important actu encart abc">Lina a gagné !</p>
Essayons remove():

p.classList.remove('actu');
on a maintenant:

<p id="para3" class="important encart abc">Lina a gagné !</p>
Avec contains() on peut interroger:

console.log(p.classList.contains('important'); => true
console.log(p.classList.contains('xyz')); => false

Sélecteur de descendants

Un sélecteur CSS sert à dire « je veux appliquer ce style à tels éléments ».

Pour l'instant on a vu

Comment faire pour, par exemple, « appliquer ce style uniquement aux éléments <a> qui sont contenus dans un titre <h1> » ?

r

Prenons un autre exemple:

<ul id="liste1">
<li>abc</li>
<li>def</li>
</ul>

<ul id="liste2">
<li>xyz</li>
<li>rst</li>
</ul>


Supposons qu'on veuille changer uniquement la couleur des "li" de la deuxième liste.
li {color: red;}
ne marche pas, car elle change la couleur des "li" dans les deux listes.

On peut utiliser un sélecteur composé de deux sélecteurs simples:
#liste2 li {color: red;}
L'espace entre "#liste2" et "li" est très important: il veut dire "descendant de".

Donc: "#liste2 li
" veut dire "tous les li qui sont descendants de #liste2

Remarquez: pour bien comprendre le sélecteur il faut le dire en français de droite à gauche.



Descendants directs

descendant direct

Le sélecteur " " (espace) sélectionne les descendants directs et indirects.
Par contre le sélecteur ">" sélectionne uniquement les descendants directs.

Compositions de sélecteurs

On peut aussi composer des sélecteurs en les accolant. Regardons ça sur un exemple:

<h1 class="actu">Mon titre</h1>
<p class="actu">
<strong class="actu">abc</strong> defg <a href="...">un lien </a>
</p>

Par exemple, imaginons qu'il y ait de nombreux types d'éléments (des "p" des "h1" ...) ayant tous une même classe "actu". On pourrait alors sélectionner uniquement les paragraphes ayant la classe "actu" comme ceci:
p.actu { color: red;}

(remarquez: il n'y a pas d'espace entre "p" et ".actu")

"p.actu" veut dire : les éléments à la fois de type "p" et appartenant à la classe "actu"

et ensuite on pourrait continuer, par exemple en spécifiant une relation de descendance:

p.actu a { color: red;}

qui veut dire : les éléments "a" contenus dans des paragraphes de classe "actu" seront en rouge

Ce genre d'approche est très utilisée en pratique.

r

On peut faire la même chose avec les id:
ul#menu { color: red;}

Comme il n'y a qu'un seul élément avec id="menu", ceci peut paraître redondant. Mais il y a des situations où c'est utile.

Priorité des sélecteurs

Dans les exercices suivants on va voir comment des règles des style peuvent s'appliquer indirectement à des éléments et ce qu'il se passe lorsque plusieurs règles différentes s'appliquent à un même élément.

Pour résumer:

Sujet 3

Événements

Les événements sont des actions de l'utilisateur. Par exemple: bouton de souris enfoncé, touche de clavier relâchée,  etc.
Presque tous les programmes qu'on écrit dans ce cours réagissent à des événements.
La gestion d'événements est un des aspects les plus difficiles à comprendre de ce cours.

Déroulement:

  1.  Au chargement de la page, le navigateur exécute notre programme JS.
  2. Notre JS dit au navigateur « enregistre une fonction sur un élément »
    Exemple:
    b.addEventListener("click",fonction_a_appeler); (voir illustration ci-desous)
    veut dire « Enregistre "fonction_a_appeler" sur le bouton »
    Important: "fonction_a_appeler" n'est pas appelée au chargement de la page... elle est juste enregistrée sur le bouton.
  3. Plus tard, à chaque fois que l'utilisateur clique sur le bouton, le navigateur s’aperçoit qu'une fonction est enregistrée sur le bouton et l'appelle.

Vocabulaire:
La fonction (ici fonction_a_appeler) en paramètre de addEventListener est appelée « Gestionnaire d'événements » (« event listener » en anglais).
Cette fonction est appelée par le navigateur quand survient l'événement.

Rappel:


Ordre exécution: rendre la main navigateur

L'ordre est bien A,B,D,C
bouton.addEventListener() ne fait qu'enregistrer la fonction anonyme sur le bouton. Il n’appelle pas la fonction anonyme.

Essayons maintenant de comprendre ce qui se passe entre D et C:
Après D, notre JS dans <script> a fini de s’exécuter. Notre JS rend la main au navigateur.
Le navigateur doit en permanence gérer plein de choses (rafraîchissement de l'affichage, interactions avec l'utilisateur, ...).
Si on ne rendait pas la main au navigateur, il resterait bloqué ... inutilisable !
Le navigateur continue donc son travail.
Si l'utilisateur clique sur le bouton, alors le navigateur s’aperçoit qu'il y a une fonction (notre fct anonyme) enregistrée sur ce bouton et l’appelle.

...
<input type="button" value="Appuyer"/>
<script>
console.log('A');
let bouton=document.querySelector('input');
console.log('B');
bouton.addEventListener('click',function()
{
console.log('C');
});
console.log('D');
</script>
...
L'utilisateur ouvre la page puis clique sur le bouton.
Déterminez l'ordre.


getElementById

La ligne suivante fonctionne:

let tux=document.querySelector('#tux');

mais dans ce cas, on peut utiliser une fonction plus simple getElementById (qui se traduit par "Chercher élement par son id):

let tux=document.getElementById('tux');

Dorénavant, on utilisera getElementById si on connaît le id de l'élément.


Supprimer un élément

Pour supprimer un élément on utilise la méthode element.remove()
Faites en sorte que l'image splat soit supprimée lorsqu'elle a touché le pingouin.

Par ailleurs, on voudrait que les images qui n'ont pas touché soient affichées derrière le pingouin, pas devant.
Pour ça, on peut utiliser la propriété CSS z-index (en JS elle s'écrit zIndex).
Faites en sorte que l'image splat prenne un zIndex de -1 quand elle n'a pas touché.

JavaScript et développement web

Le JavaScript (JS) est un langage de programmation, initialement  développé pour le navigateur, mais maintenant aussi utilisé dans d'autres contextes. Il suit un standard appelé ECMAScript (ES). Le langage JavaScript n'a aucun lien avec le Java. Son nom a été choisi pour des raisons commerciales.

Le JavaScript est souvent utilisé dans le navigateur. Il est, en pratique, le langage qui permet d’interagir directement avec le navigateur. L’interaction avec le navigateur se fait à travers le DOM. C'est ce qu'on fait depuis le début de ce cours.

Le JavaScript peut aussi être utilisé en dehors du navigateur, principalement avec Node.js
De nombreux logiciels et librairies utilisant Node.js sont disponible par npm. Node.js est souvent utilisé coté serveur.

Page HTML statique

Le "serveur" musique.org est une machine dans un Datacenter distant.
Sur cette machine tourne un logiciel appelé un "serveur web".
Remarquez que le terme "serveur" peut avoir deux sens: la machine et le logiciel

1) Le navigateur demande au serveur web la page.html. C'est ce qu'on appelle une "requête"
2) Sur la machine tourne un logiciel appelé serveur web qui, reçoit la demande.
Le serveur web trouve le fichier page.html
3) Le serveur envoie page.html au navigateur. C'est ce qu'on appelle une "réponse"
4) Le navigateur reçoit page.html et l'affiche.

Ensuite, le navigateur va recommencer la même opération pour les fichiers CSS, JS, images, etc. référencés dans page.html

Page dynamique

1) Le navigateur (le client) demande une page en précisant ce qu'il cherche dans la requête
2) Le serveur web reçoit la requête et exécute un logiciel. Ce logiciel peut-être écrit dans différents langages de programmation (PHP, Python, Java, JS+Node.js, ...)
3) Le logiciel demande des informations à la base de données
4) A l'aide de ces informations le logiciel génère une page HTML
5) Le HTML est envoyé au navigateur
6) Le navigateur affiche le HTML

La distinction statique / dynamique est uniquement faite coté serveur. Dans les deux cas, le navigateur reçoit du HTML et ne voit pas la différence.

Technologies client / serveur

En pratique, les interactions client/serveur peuvent devenir complexes. De nombreuses requêtes se font du navigateur au serveur. Les informations et même les logiciels couvrent l'un et l'autre. Pour ne pas se perdre, il est important de bien garder en tête où est employée chaque technologie (client ou serveur ?).
Pour le web, le terme "client" veut dire la même chose que "navigateur".

Objets : Pas de classe

En Java:

  •  on crée une classe
  •  on instancie un objet de cette classe (new)
  •  on accède à une propriété de l’objet

En JavaScript

  • on crée directement un objet (ici, il est vide, mais il pourrait-être initialisé)
  • on peut ajouter des propriétés simplement

Créer un objet

En JavaScript, on peut créer un objet en utilisant les accolades "{", "}", et en spécifiant directement des propriétés et leurs valeurs.

Cette syntaxe est très utilisée en pratique.

Elle sert aussi comme un format d'échange de données appelé JSON. JSON est utilisé, entre autres, pour la communication entre le navigateur et le serveur. On étudiera JSON dans un autre cours.

Propriétés dynamiques

En Java on ne peut pas utiliser une propriété qui n'a pas été déclarée dans la classe.

En JavaScript on peut ajouter des propriétés dynamiquement. Il n'y a pas de classe.


Objets et fonctions

En JavaScript, les fonctions sont des valeurs comme les autres (number, String, object...). On peut les attribuer à des variables. On peut aussi les attribuer aux propriétés d'un objet.

Donc, pour créer une méthode, il suffit de créer une propriété dont la valeur est de type "function".

Le JS ne fait rien de spécial pour gérer la méthode. afficherNotes est juste une propriété de l'objet, dont la valeur se trouve être de type "function".


Tableaux

Les tableaux JavaScript se déclarent avec des crochets [ ... ]. Les tableaux sont indexés par un entier et sont numérotés à partir de 0.

De nombreuses méthodes sont associées aux tableaux. Par exemple :

.push(v) permet d'ajouter une valeur à la fin d'un tableau.

.indexOf(v) renvoi l'indice de la première valeur v trouvée dans le tableau

Pour parcourir un tableau, utilisez une boucle for

.length permet d'obtenir la taille d'un tableau.


Sujet 4

Expressions régulières

Une des tâches fréquentes en JS est de vérifier que les utilisateurs ont bien saisi les champs d'un formulaire.

Validation & expressions régulières
Par exemple, si on a demandé un nombre, il faut vérifier qu'il n'a pas rentré de lettres.
Si on a demandé une adresse mail, il faut vérifier que la saisie ressemble bien à une adresse mail.

Pour çà, on va utiliser des expressions régulières.

N'hésitez pas à relire des explications sur le web.
Par exemple: https://dridk.me/expression-reguliere.html

Sécurité: La vérification en JS permet d'afficher immédiatement un message d'erreur, sans passer par le serveur, elle est donc agréable pour l'utilisateur. Néanmoins, la validation devra aussi être faite impérativement coté serveur. Il est très facile pour un utilisateur mal-intentionné de contourner la validation coté client (JS).

Syntaxe d'une expression régulière:

Quelques exemples qu'on étudiera plus en détail après:
  •  /^abc$/ : correspond uniquement à la chaîne "abc"
  • /^Prix: [0-9]+$/ : correspond à "Prix: " suivi d'un nombre: Exemples: "Prix: 123" "Prix: 5" "Prix: 1000000"
  • /bonjour/ : correspond à "bonjour", mais n'importe où. Exemples: "bonjour Karim", "bien le bonjour"
  • /^Choix: [abcd]$/ : correspond à "Choix: " suivi d'une des lettres a,b,c ou d. Exemples: "Choix: c" , "Choix: a"
Utilisation en JS

Une expression régulière est appliquée à une chaîne de caractères, en général pour vérifier ou remplacer:
  • vérifier si la chaîne correspond à l'expression
    Exemple en JS:
    if( /^abc$/.test("axc") ) {console.log(...);}
  • remplacer :
    Exemple en JS
    let s='abcdef';
    s=s.replace(/abc/,'xyz');
    => s vaut 'xyzdef'

Expression régulière simple


Regardons cet exemple d'expression régulière
 /^abc$/
On suppose qu'un utilisateur a saisi une chaîne dans un formulaire et qu'on veut vérifier si la chaîne correspond bien à l'expression.
L'expression veut dire:
/ : début de l'expression régulière
^ : on doit se trouver au début de la chaîne saisie
a: suivi de la lettre "a"
b: suivi de la lettre "b"
c: suivi de la lettre "c"
$: on doit se trouver à la fin de la chaîne saisie
/ : fin de l'expression régulière

Donc dans ce cas on impose que l'utilisateur ait saisi la chaîné "abc".

Voyons si vous avez bien compris ^ et $

Crochets

Les crochets représentent un seul caractère parmi plusieurs.
Le tiret peut-être utilisé pour représenter une gamme de caractères.

Par exemple :
[ab] veut dire "soit a, soit b". Mais un seul caractère.
On peut avoir "a" ou "b" mais pas "ab" et pas "ba" et pas "ababa"
[abcd] veut dire "soit a, soit b, soit c, soit d".
[a-d] veut dire la même chose que [abcd] "soit a, soit b, soit c, soit d". Mais toujours un seul caractère.
[0-9]  veut dire la même chose que [0123456789], c'est à dire "un seul chiffre"
[a-z] veut dire "une seule lettre minuscule"

Quelles saisies fonctionnent pour

/^a[bcd]e$/ 


Répétition

Le "+" et le "*" permettent d'indiquer une répétition de l'expression qui précède.

Par exemple:

Les expressions comme [a-z]+ ou [0-9]* sont très utilisées

Quelles saisies fonctionnent pour

/^ab+c$/ 


Uniquement des chiffres

Quelques exemples:

/^ab[cd]$/ : la lettre "a" puis la lettre "b" puis la lettre "c" OU la lettre "d" (donc abc ou abd sont ok.)

/^ab*$/ : la lettre "a" puis la lettre "b" répétée zero ou plusieurs fois (donc a ab abbb abbbbb sont ok.)

/^ab+$/ : la lettre "a" puis la lettre "b" répétée une ou plusieurs fois (donc ab abbb abbbbb sont ok.)

/^ab[a-z]$/ : la lettre "a" puis la lettre "b" puis n'importe quelle lettre (de "a" à "z")

/^ab[0-9]$/ : la lettre "a" puis la lettre "b" puis n'importe quel chiffre (de 0 à 9)

Maintenant, c'est à vous:
Quelle expression régulière si on veut une saisie uniquement composée de chiffres (au moins un chiffre) ?



Adresse mail - 2 : correction

Correction:

/^[a-z]+@[a-z]+\.[a-z]+$/.test('aqsdfsdf')

Voila!

Nous avons une expression régulière qui nous permet de vérifier des adresses mails simples.

Une expression plus complète
La validation d'une adresse mail correspondant aux normes RFC 822, RFC 5322 est très très compliquée, mais en pratique, on peut simplifier.
Un site de référence en la matière conseille :
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
le /i à la fin est l'option pour ignore la case (minuscule / majuscules)

Essayez de taper dans la console des outils de dév.:

/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test("aaa@bbb.ccc")

ou

/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test("aaabbb.ccc")


Négation classe de caractères

Une dernière expression:

[^abcd]

Qui veut dire « tout caractère SAUF a, b, c ou d. »

Attention à ne pas confondre avec le "^" en dehors de crochets, qui veut dire « le début de la chaîne ».

Un autre exemple:

[^a-z]

Qui veut dire « Tout caractère sauf une lettre. »


Un autre exemple :
si on veut trouver quelque-chose qui ressemble à une balise : <div class="xyz">

On peut écrire :

<[^>]+>

Çà se lit "<" suivi d'un n'importe quel caractère qui ne soit pas ">", ceci au moins une fois, suivi de ">".


à vous:


On veut identifier une expression des chaînes du type
nom="Toto"
prenom="Jo"
adresse="123 rue grande, Trifouilli"

C'est à dire : une suite de lettres minuscules, suivie d'un égal suivi d'un guillemet, suivi de caractères qui ne sont pas des guillemets, suivi d'un guillemet.

Écrivez l'expression entière : /^...$/


String

On a souvent besoin de manipuler des chaînes de caractères (String).

Les fonctionnalités les plus utiles :

Retrouvez la documentation de String ici.

Essayons :

Dans la console tapez :

var a='bonjour'

Ensuite tapez:

a.length

Ensuite tapez :

a=a+" c'est moi"
a.replace(/o/,'X')

Attention, le résultat du remplacement est affiché, mais "a" n'a pas été modifié (pour ça il faut utiliser : a=a.replace(/o/,'X') )

/xyz/ est une expression rationnelle
Par défaut le remplacement ne se fait que sur la première occurrence de "o" trouvée:
"bonjour c'est moi" => "bXnjour c'est moi"
On peut spécifier l'option "g" à la fin de l'expression pour remplacer toutes les occurrences:

a.replace(/o/g,"X")
"bonjour c'est moi" => "bXnjXur c'est mXi"


Portée des variables

En JavaScript les variables ont une « portée », c'est à dire qu'elles sont visibles uniquement dans un contexte.

Par exemple:

function exemple1(){
   let v1=100;
}
function exemple2(){
   console.log(v1); // Erreur !
}

La variable v1 est visible uniquement dans la fonction exemple1 et pas dans exemple2.
Un « bloc » est crée par des accolades :

if(x){
   // ceci est un bloc
}

Une variable définie avec « let » dans un bloc est uniquement visible dans ce bloc:

if(x==1){
let v=123;
}
console.log(v); // Erreur !

Elle est visible même si on est dans un bloc inclus dans celui qui l'a défini:

if(x==1){
let v=123;
for(let i=0;i<10;i++){
console.log(v); // OK !
}
}
console.log(v); // Erreur !


Portée des variables: fonctions

En JS des fonctions peuvent être définies à l'intérieur d'autres fonctions.
Dans une fonction, on peut utiliser des variables du bloc au-dessus (contenant):

function exemple1(){
   let v1=100;
function exemple2(){
   console.log(v1); // OK !
}
}


On l'a déjà utilisé. Par exemple dans morpion.html :

			let joueur='X';
function click_td(event)
{
console.log('Click');
if(this.textContent!=''){return;}
this.textContent=joueur;
if(joueur==='X'){joueur='O';}else{joueur='X';}
}

La variable joueur est définie à l'extérieur de la fonction click_td, mais elle est utilisée dans cette fonction click_td.


Variable masquée par une variable locale

Si une variable locale est déclarée avec le même nom qu'une autre variable accessible, elle la masque:

let a=123; // Déclaration 1
exemple1();
console.log(a); // Affiche 456
exemple2();
console.log(a); // Affiche 456

function exemple1(){
a=456; // Pas de let: donc le JS utilise "a" de la Déclaration 1
}

function exemple2(){
let a=789; // let crée une nouvelle variable locale.
// le "a" ici, n'est pas le même que celui de la Déclaration 1
// On ne modifie donc pas le "a" de la Déclaration 1

}


let, const, var

En JS il y a 3 manières de déclarer une variable: let, const et var
Pour l'instant, on a vu let.

Voyons la différence entre let et var:

function exemple(){
if(x>3){
   var a=123;
   let b=456;
}
console.log(a); // OK
console.log(b); // Erreur
}

En pratique, on va se contenter surtout utiliser let et const.

Sujet 5

Élément DOM, Navigation dans l'arbre

Pour pouvoir manipuler un élément DOM en JS, il faut d'abord aller le chercher.
C'est ce qu'on a fait avec querySelector et getElementById.
On obtient alors un objet (ici "v") dont on pourra manipuler les propriétés.

Vocabulaire:


textContent / value

Il est fréquent de confondre "textContent" et "value".
Voyons la différence


textContent

La propriété textContent d'un élément DOM permet de lire et écrire le texte qui, en HTML, se trouve entre la balise ouvrante et fermante.

<p id="para1">Bonjour</p>
document.getElementById('para1').textContent="Aurevoir";

Le paragraphe devient alors:

<p id="para1">Aurevoir</p>

value

La propriété value d'un champ texte input permet de lire et écrire la valeur que l'utilisateur a pu taper dans ce champ texte.

<input id="nom" type="text" value="xyz" />

document.getElementById('nom').textContent="Karim";
Le input devient alors

<input id="nom" type="text" value="Karim" />

Naviguer l'arbre DOM

On a souvent besoin de naviguer dans l'arbre DOM. C'est à dire, de partir d'un élément (ici p1) et de devoir remonter (parentNode) ou descendre (children). 

On peut aussi utiliser querySelectorAll. Jusqu'à là on l'a toujours utilisé sur document:  document.querySelectorAll(...). C'est à dire qu'on faisait partir la recherche "querySelectorAll" de "document".  "document" est la racine de l'arborescence DOM, donc on obtenait tous les résultats de la page. Parfois on veut limiter les résultats et partir d'un élément donné (ici p1). On peut alors lancer "querySelectorAll" à partir de cet élément. Donc ici: p1.querySelectorAll(...)

children et querySelectorAll() renvoient un NodeList, qui peut être utilisé comme un tableau.
Par exemple:

Remarque: tout comme querySelectorAll, querySelector peut être appliqué aussi sur un élément:
p1.querySelector('.xyz') renvoie l'élément em

Grandes lignes d'un programme JS

Exemple HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Exemple</title>
</head>
<body>
<h1>Cliquez-moi!</h1>
<p>Bonjour!</p>
<script>
let titre=document.querySelector('h1');
titre.addEventListener('click',fonction_a_appeler);
function fonction_a_appeler()
{
let paragraphe=document.querySelector('p');
paragraphe.style.color='green';
}
</script>
</body>
</html>

La plupart des programmes JS qu'on écrit ici font les opérations suivantes:

1) Chercher des éléments DOM
Pour pouvoir manipuler en JS les éléments DOM de la page (<h1>,<p>, ...) il faut d'abord les chercher.
Par exemple:
let titre=document.querySelector('h1');
Ceci veut dire « Bonjour "document", cherche le premier h1» ... puis met ce h1 dans la variable "titre"
Maintenant, qu'on a l'élément h1 dans la variable titre on, peut faire des choses avec.

2) Enregistrer des « gestionnaires d'événements »

Les programmes JS vont réagir à des événements (click souris, touche clavier, mouvement souris ...)
Ces événements surviennent toujours sur un élément DOM (<h1>, <p>, ....) précis.
Pour réagir, on enregistre une fonction qu'on a écrit sur cet élément DOM:
Si on veut réagir à un click sur cet <h1>, il faut enregistrer notre fonction sur cet <h1>.
Exemple :
titre.addEventListener('click',fonction_a_appeler);
on enregistre la fonction fonction_a_appeler sur titre (titre est la variable qui fait référence au <h1>).
Cette opération (enregistrer la fonction) se fait, en général, au chargement de la page.

3) Réagir à un événement

Plus tard, quand le navigateur s’aperçoit d'un événement sur un élément il regarde si une fonction (gestionnaires d'événements) est enregistrée sur cet élément. Si c'est le cas, le navigateur appelle la fonction.
Exemple:
L'utilisateur clique sur <h1>. Le navigateur voit que fonction_a_appeler est enregistrée sur ce <h1>. Il appelle cette fonction.

Sujet 7

Bubbling : présentation

Cette remontée s’appelle le « bubbling ». C'est très utile.

Bubbling: this, Event.target

Quand on utilise le bubbling, il faut distinguer

Conclusion: HTML généré et bubbling

Que ce soit pour le panier ou pour le tableau joueur, on a deux situations où:

Dans ces deux cas on ne peut pas utiliser addEventListener au démarrage de la page, car les éléments (panier: <li>, joueurs: <tr>) n'existent pas encore. On exploite alors le bubbling: on utilise addEventListener sur un parent qui existe au démarrage (panier: <ul>, joueurs: <table>).

On aura donc du code de ce type:

unParent.addEventListener('exemple', function(event){
    if( /* c'est n'est pas l'élément descendant souhaité */ ) { return; }
});

Cette approche est très courante. En particulier, c'est le cas des programmes AJAX (qu'on verra plus tard) qui génèrent beaucoup de HTML en cours d’exécution.


Sujet 8

Arrow functions

En JS on utilise souvent des fonction anonymes.

On peut simplifier leur écriture avec les « Arrow functions ». En français: « Fonctions fléchées ».

On peut supprimer « function » et utiliser une flèche =>

function(a,b,c){...}    devient   (a,b,c)=>{...}
Quand il n'y a qu'un seul paramètre, on peut omettre les parenthèses:

function(a){...}    devient   a=>{...}
Sans paramètre, on utilise des parenthèses vides:

function(){...}    devient   ()=>{}
Quand il n'y a qu'une seule instruction, et qu'on veut retourner sa valeur, on peut omettre les accolades:

function(a,b,c){return a+b+c}    devient   (a,b,c)=>a+b+c
function(a){return 2*a}    devient   a=>2*a

Exemples
document.addEventListener('click',()=>console.log('click'));
let tab=[100,50,320];
let total=0;
tab.forEach((v)=>total+=v);
Attention:  « this » a une valeur différente dans une fonction flèche.

Boucles

Pour parcourir un tableau, on a utilisé des boucles for de la manière suivante:

let tab=['Liam','Joe','Leila'];
for(let i=0; i<tab.length; i++){ console.log(tab[i]); }

JavaScript propose d'autres manières de faire.

Boucle for .. of

Pour un tableau, mais aussi pour d'autres objets dits « itérables », comme un NodeList, on peut utiliser ceci:

// Ceci affiche Liam, Joe et Lelila
for(const v of tab){console.log(v);}

C'est plus court et ça évite de définir une variable numérique (comme « i »), dont on n'a pas besoin en général.

Exemple avec NodeList:

let list=document.querySelectorAll('p');
for(const p of list){ console.log(p.textContent) ; }
Fonction .forEach
La fonction .forEach s'applique sur les tableaux mais aussi sur les NodeList. Elle prend en paramètre une fonction qui sera appelée pour chaque élément du tableau.

// Ceci affiche Liam, Joe et Lelila
tab.forEach(function(v){console.log(v);});
On l'utilise souvent avec des fonctions fléchées:
tab.forEach(v=>console.log(v));
let nombres=[100,50,320];
let total=0;
nombres.forEach((v)=>total+=v);

Sujet 9

jQuery : introduction

jQuery est une librairie JavaScript utilisée sur la grande majorité des sites web (77% en 2022).
Elle simplifie l'accès aux DOM.

JQuery logo.svg

jQuery est une librairie, c'est à dire des fonctions et des objets écrits par les développeurs de jQuery.
jQuery n'est pas fourni par votre navigateur, vous devez l'inclure, par exemple, comme ceci:

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

L'accès à jQuery se fait par un raccourcis « $ ».
$ est à la fois une fonction et un objet.

Listes jQuery

En jQuery on manipule en permanence des « listes jQuery »
Une liste jQuery contient une liste d'éléments DOM.
Vous pouvez l'imaginer comme un tableau d'éléments DOM, ou comme une NodeList.

En général, pour créer une liste jQuery, on appelle la fonction « $ » avec un sélécteur CSS:

$('.important') 

Ceci renvoie une liste jQuery contenant tous les éléments ayant class="important"

C'est très similaire à querySelectorAll:

document.querySelectorAll('.important')
Une liste jQuery est un objet. On peut la mettre dans une variable:

let a=$('.xyz');




Fonctions jQuery

Une fois qu'on a une liste jQuery, on peut appeler une des nombreuses fonctions jQuery définies dessus. Il y en a plus de 140.
Les fonctions jQuery s'appliquent sur toute la liste... ce qui évite d'écrire des boucles. C'est très pratique ! 🙂

Exemple: fonction .hide()

Pour cacher tous les éléments ayant class="important"

$('.important').hide()

C'est l'équivalent de:

document.querySelectorAll('.important').forEach(d=>d.style.display='none');

Exemple: fonction .on()
Pour installer un gestionnaire d'événements sur tous les éléments ayant class="important"

$('.important').on('click',()=>console.log('bonjour'))

C'est l'équivalent de:

document.querySelectorAll('.important').forEach(d=>d.addEventListener('click',()=>console.log('bonjour')));
Variable

Si nécessaire, on peut mettre une liste jQuery dans une variable, puis appeler une fonction dessus. Par exemple, on peut décomposer :

$('.important').hide();

en :

let a=$('.important');
a.hide();

Comme vous le voyez, jQuery permet d'écrire simplement des choses qui seraient plus compliquées en DOM.

.css()

<p id="abc">exemple</p>

La fonction .css() permet de modifier l'attribut « style »:

Les trois approches suivantes ont le même résultat:

$('#abc').css('color','red');
document.getElementById('abc').style.color='red';
<p id="abc" style="color:red">exemple</p>

.text()

<span>abcd</span>

La fonction .text() permet d'accéder à textContent
Les deux lignes suivantes font la même chose:

$('span').text('defg');
document.querySelector('span').textContent='defg';

Et le span devient:

<span>defg</span>

.val()

<input type="text" />

La fonction .val() permet d’accéder à la propriété value, par exemple, d'un champ texte.

Pour lire ce que l'utilisateur à tapé dans le champ texte:

let a=$('input').val();

Pour modifier le contenu du champ texte:

$('input').val('exemple');

C'est l'équivalent de:

let a=document.querySelector('input').value;
document.querySelector('input').value='exemple';

jQuery: Créer un élément

Depuis le SUjet-1, en DOM, sans jQuery, pour créer un élément, on suivait les étapes suivantes:

(rappel et plus de détails)

En jQuery, on suit des étapes similaires.

DOM et liste jQuery / $(this)

Dans la variable « this » on a un élément DOM. On voudrait le manipuler avec jQuery.
Pour créer une liste jQuery à partir d'un élément DOM, il suffit d'appeler la fonction jQuery:

$(this)
On peut le faire aussi avec d'autres éléments:

const x=document.querySelector('p');
$(x).css('color','red');
Dans cet exemple, $(x) transforme un élément DOM x en une liste jQuery $(x)

.addClass()

La fonction jQuery .addClass() permet d'ajouter une classe aux éléments d'une liste jQuery.

Exemple:

<p>Tom</p>
<p>Liam</p>
$('p').addClass('exemple');
<p class="exemple">Tom</p>
<p class="exemple">Liam</p>

--

En tenant compte de ceci et de ce qu'on a vu à la page précédente, que faut-il taper pour ajouter la classe « choisi » à l'élément cliqué.
(Essayez dans votre programme avant de répondre)

.hasClass() et .removeClass()

La fonction .hasClass() permet de savoir si une classe est présente:

<p class="abc def'>bonjour</p>

$('p').hasClass('abc')  renvoie true
$('p').hasClass('xyz')  renvoie false

.removeClass() permet de supprimer une classe:

$('p').removeClass('abc')  transforme le paragraphe en ceci:

<p class="def'>bonjour</p>

preventDefault

Les événements possèdent souvent des « actions par défaut ».

Par exemple :

On peut bloquer l'action par défaut avec la fonction event.preventDefault()

Par exemple:
Quand l'utilisateur clique sur le lien, rien ne se passe:

<a href="https://exemple.com">lien</a>
$('a').on('click',function(event){
    event.preventDefault();
});