Corrigés Guidés des Travaux Pratiques JavaScript : Applications Interactives et Gestion de Données

yelallioui 4 views 68 slides Oct 28, 2025
Slide 1
Slide 1 of 68
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66
Slide 67
67
Slide 68
68

About This Presentation

Ce corrigé fournit les solutions commentées des exercices JavaScript, incluant la gestion d’array, la lecture d’entrée, la logique conditionnelle et les objets simples. Il constitue une référence pour consolider les compétences avant le passage à la manipulation du DOM ou à des projets w...


Slide Content

Y. EL ALLIOUI – [email protected] 1 / 68
2025
Programmation Web 2
Javascript

Travaux Pratiques (TP)
Correction
Youssouf EL ALLIOUI

[email protected]

Série 1
Fondamentaux JavaScript - Des structures de données aux
applications console interactives
Exercice 1 : Gestion d'une liste d’articles
Objectif :
Travailler avec les tableaux, les boucles et les fonctions.
Consignes :
1. Créez un tableau listeArticles contenant au moins 5 articles (par exemple : "pain",
"lait", "œufs", "fruits", "légumes" ).
2. Écrivez une fonction ajouterArticle qui prend un article (chaîne de caractères) en
paramètre et l’ajoute au tableau uniquement s’il n’est pas déjà présent. Affichez un
message approprié dans la console (ex. : "pain a été ajouté" ou "pain est déjà
dans la liste").
3. Écrivez une fonction supprimerArticle qui prend un article en paramètre et le
supprime du tableau s’il existe. Affichez un message pour indiquer si la suppression a
réussi ou non.

Y. EL ALLIOUI – [email protected] 2 / 68
2025
4. Écrivez une fonction afficherListe qui affiche tous les articles dans la console,
chacun précédé de son numéro (commençant à 1), après avoir trié la liste par ordre
alphabétique.
Testez vos fonctions en ajoutant un article, en supprimant un autre, puis en affichant la liste
après chaque modification.
Correction :
// 1. Création d'un tableau contenant 5 articles.
let listeArticles = ["pain", "lait", "œufs", "fruits", "légumes"];

/*
Fonction : ajouterArticle
Cette fonction prend un article en paramètre et l’ajoute au tableau s’il
n’est pas déjà présent.
Elle affiche un message dans la console pour indiquer si l'article a été
ajouté ou s’il existe déjà.
*/
function ajouterArticle(article) {
// Vérifier si l'article est déjà présent dans la liste.
if (listeArticles.indexOf(article) === -1) {
// S'il n'existe pas, on l'ajoute avec push().
listeArticles.push(article);
console.log(`"${article}" a été ajouté à la liste.` );
} else {
console.log(`"${article}" est déjà dans la liste.` );
}
}

/*
Fonction : supprimerArticle
Cette fonction prend un article en paramètre et le supprime du tableau s’il
existe.
Elle affiche un message indiquant si la suppression a réussi ou si
l'article n'est pas présent.
*/
function supprimerArticle(article) {
// Chercher l'indice de l'article dans le tableau.
let index = listeArticles.indexOf(article);
if (index !== -1) {
// Utilisation de splice() pour supprimer l'article.
listeArticles.splice(index, 1);
console.log(`"${article}" a été supprimé de la liste.` );
} else {
console.log(`"${article}" n'existe pas dans la liste.` );
}
}

/*
Fonction : afficherListe
Cette fonction trie la liste par ordre alphabétique, puis affiche chaque
article dans la console,
précédé de son numéro (commençant à 1).

Y. EL ALLIOUI – [email protected] 3 / 68
2025
*/
function afficherListe() {
// Créer une copie du tableau et le trier pour ne pas modifier l'ordre
original.
let listeTriee = listeArticles.slice().sort();
console.log("Liste des articles (triée par ordre alphabétique) :" );
// Parcourir la liste et afficher chaque article avec son numéro.
listeTriee.forEach((article, index) => {
console.log(`${index + 1}. ${article}`);
});
}

// ------------------- Tests -------------------

// Afficher la liste initiale
afficherListe();

// Ajouter un nouvel article qui n'existe pas.
ajouterArticle("beurre");
// Tenter d'ajouter un article déjà présent.
ajouterArticle("pain");

// Afficher la liste après ajout
afficherListe();

// Supprimer un article existant.
supprimerArticle("œufs");
// Tenter de supprimer un article inexistant.
supprimerArticle("sucre");

// Afficher la liste après suppression
afficherListe();

Y. EL ALLIOUI – [email protected] 4 / 68
2025
Exercice 2 : Calculatrice avancée
Objectif :
Utiliser les opérateurs, les structures conditionnelles et gérer un historique avec un tableau.
Consignes :
1. Créez une fonction calculer qui prend trois paramètres : deux nombres (a et b) et une
opération (chaîne : "+", "-", "*", "/", ou "%}). Elle doit retourner le résultat de
l’opération ou un message d’erreur si la division ou le modulo par zéro est tenté.
2. Créez un tableau historique pour stocker chaque calcul sous forme de chaîne (ex. : "5
+ 3 = 8"). Ajoutez chaque calcul réussi au tableau dans la fonction calculer.
3. Écrivez une fonction afficherHistorique qui affiche toutes les entrées de l’historique
dans la console.
4. Ajoutez une fonction effacerHistorique qui vide le tableau historique et affiche
un message de confirmation.
Testez avec plusieurs calculs, y compris une division par zéro, et affichez l’historique.
Correction :
// Création d'un tableau pour stocker l'historique des calculs.
let historique = [];

/*
Fonction : calculer
Cette fonction prend trois paramètres : deux nombres (a et b) et une
opération (chaîne de caractères).
Elle retourne le résultat de l'opération ou un message d'erreur en cas de
division ou modulo par zéro.
Si le calcul est réussi, il est ajouté à l'historique sous forme de chaîne
(ex. "5 + 3 = 8").
*/
function calculer(a, b, operation) {
let resultat;
switch (operation) {
case "+":
resultat = a + b;
break;
case "-":
resultat = a - b;
break;
case "*":
resultat = a * b;
break;
case "/":
if (b === 0) {
console.log("Erreur : division par zéro." );
return "Erreur : division par zéro." ;
}
resultat = a / b;
break;

Y. EL ALLIOUI – [email protected] 5 / 68
2025
case "%":
if (b === 0) {
console.log("Erreur : modulo par zéro." );
return "Erreur : modulo par zéro." ;
}
resultat = a % b;
break;
default:
console.log("Opération non reconnue." );
return "Opération non reconnue." ;
}
// Enregistrer le calcul dans l'historique
let calculStr = `${a} ${operation} ${b} = ${resultat}`;
historique.push(calculStr);
return resultat;
}

/*
Fonction : afficherHistorique
Cette fonction affiche chaque calcul stocké dans le tableau historique.
*/
function afficherHistorique() {
console.log("Historique des calculs :" );
historique.forEach((calcul, index) => {
console.log(`${index + 1}. ${calcul}`);
});
}

/*
Fonction : effacerHistorique
Cette fonction vide le tableau historique et affiche un message de
confirmation.
*/
function effacerHistorique() {
historique = [];
console.log("L'historique a été effacé." );
}

// ------------------- Tests -------------------

// Réalisation de plusieurs calculs.
console.log("5 + 3 =", calculer(5, 3, "+")); // Doit afficher 8
console.log("10 - 4 =", calculer(10, 4, "-")); // Doit afficher 6
console.log("7 * 2 =", calculer(7, 2, "*")); // Doit afficher 14
console.log("20 / 4 =", calculer(20, 4, "/")); // Doit afficher 5
console.log("20 / 0 =", calculer(20, 0, "/")); // Division par zéro
console.log("10 % 3 =", calculer(10, 3, "%")); // Doit afficher 1
console.log("10 % 0 =", calculer(10, 0, "%")); // Modulo par zéro

// Affichage de l'historique après les calculs.
afficherHistorique();

// Effacer l'historique.

Y. EL ALLIOUI – [email protected] 6 / 68
2025
effacerHistorique();

// Afficher l'historique pour vérifier qu'il est vide.
afficherHistorique();

Y. EL ALLIOUI – [email protected] 7 / 68
2025
Exercice 3 : Gestion d'une bibliothèque
Objectif :
Manipuler des objets, des tableaux et des boucles.
Consignes :
Créez un tableau bibliotheque contenant au moins 3 objets, chaque objet représentant un
livre avec les propriétés titre, auteur et année.
1. Écrivez une fonction ajouterLivre qui prend un titre, un auteur et une année,
puis ajoute un nouvel objet au tableau.
2. Écrivez une fonction rechercherLivre qui prend un titre et affiche les détails du
livre correspondant (ou un message s’il n’est pas trouvé).
3. Écrivez une fonction listerLivres qui affiche tous les livres avec leurs détails.
4. Ajoutez une fonction supprimerLivre qui supprime un livre par son titre et affiche
un message pour confirmer ou signaler une erreur.
Testez vos fonctions en ajoutant un livre, en recherchant un titre, et en supprimant un
livre.
Correction :
// Création d'un tableau "bibliotheque" contenant au moins 3 livres.
let bibliotheque = [
{ titre: "Le Petit Prince", auteur: "Antoine de Saint-Exupéry", annee:
1943 },
{ titre: "1984", auteur: "George Orwell", annee: 1949 },
{ titre: "L'Étranger", auteur: "Albert Camus", annee: 1942 }
];

/*
Fonction : ajouterLivre
Cette fonction prend en paramètres un titre, un auteur et une année,
et ajoute un nouvel objet livre au tableau bibliotheque.
*/
function ajouterLivre(titre, auteur, annee) {
let nouveauLivre = { titre: titre, auteur: auteur, annee: annee };
bibliotheque.push(nouveauLivre);
console.log(`Livre ajouté : "${titre}" par ${auteur} (${annee}).`);
}

/*
Fonction : rechercherLivre
Cette fonction prend un titre en paramètre et cherche le livre
correspondant dans le tableau.
Si le livre est trouvé, ses détails sont affichés dans la console ;
sinon, un message d'erreur est affiché.
*/
function rechercherLivre(titre) {
// Recherche du livre dont le titre correspond (en ignorant la casse)

Y. EL ALLIOUI – [email protected] 8 / 68
2025
let livreTrouve = bibliotheque.find(livre => livre.titre.toLowerCase()
=== titre.toLowerCase());
if (livreTrouve) {
console.log(`Livre trouvé : "${livreTrouve.titre}" par
${livreTrouve.auteur}, publié en ${livreTrouve.annee}.`);
} else {
console.log(`Le livre "${titre}" n'a pas été trouvé dans la
bibliothèque.`);
}
}

/*
Fonction : listerLivres
Cette fonction parcourt le tableau bibliotheque et affiche tous les
livres avec leurs détails dans la console.
*/
function listerLivres() {
console.log("Liste des livres dans la bibliothèque :" );
bibliotheque.forEach((livre, index) => {
console.log(
`${index + 1}. "${livre.titre}" par ${livre.auteur}, publié en
${livre.annee}`
);
});
}

/*
Fonction : supprimerLivre
Cette fonction prend un titre en paramètre, cherche le livre
correspondant et le supprime du tableau.
Elle affiche un message de confirmation ou d'erreur si le livre n'est pas
trouvé.
*/
function supprimerLivre(titre) {
// Recherche de l'indice du livre dans le tableau.
let index = bibliotheque.findIndex(livre => livre.titre.toLowerCase()
=== titre.toLowerCase());
if (index !== -1) {
let livreSupprime = bibliotheque.splice(index, 1)[0];
console.log(`Livre supprimé : "${livreSupprime.titre}".`);
} else {
console.log(`Le livre "${titre}" n'a pas été trouvé et ne peut pas
être supprimé.`);
}
}

// ------------------- Tests -------------------

// Afficher la bibliothèque initiale
listerLivres();

// Ajouter un nouveau livre
ajouterLivre("La Peste", "Albert Camus", 1947);

Y. EL ALLIOUI – [email protected] 9 / 68
2025

// Rechercher un livre existant
rechercherLivre("1984");

// Rechercher un livre inexistant
rechercherLivre("Le Nom de la Rose");

// Supprimer un livre existant
supprimerLivre("L'Étranger");

// Tenter de supprimer un livre inexistant
supprimerLivre("Le Hobbit");

// Afficher la bibliothèque après les modifications
listerLivres();

Y. EL ALLIOUI – [email protected] 10 / 68
2025
Exercice 4 : Gestion d'une bibliothèque interactive (Extension de l’Ex 3)
Objectif :
L’objectif de cet exercice est d’étendre l’Exercice 3 en ajoutant une interface utilisateur
textuelle sous forme de menu interactif. L’étudiant devra intégrer les fonctions déjà créées
(ajouterLivre, rechercherLivre, listerLivres, supprimerLivre) dans une boucle qui affiche un
menu et exécute l’action choisie par l’utilisateur.
Consignes :
1. Préparation du contexte :
o À partir de l’Exercice 3, vous disposez d’un tableau bibliotheque contenant
au moins trois objets livres. Chaque objet possède les propriétés suivantes
: titre, auteur, et annee.
o Vous avez également défini les fonctions suivantes :
§ ajouterLivre(titre, auteur, annee)
§ rechercherLivre(titre)
§ listerLivres()
§ supprimerLivre(titre)
2. Création du menu interactif :
o Au démarrage du programme, affichez le menu suivant :
---------- Menu ----------
1 : Ajouter un nouveau livre
2 : Rechercher un livre
3 : Lister tous les livres
4 : Supprimer un livre
0 : Quitter
---------------------------
o Demandez ensuite à l’utilisateur de saisir son choix.
3. Gestion des choix utilisateur : En fonction du choix de l’utilisateur, procédez comme
suit :
o Choix 1 (Ajouter un nouveau livre) :
§ Demandez à l’utilisateur de saisir le titre, l’auteur et l’année du livre.
§ Appelez la fonction ajouterLivre(titre, auteur, annee) pour
ajouter le livre au tableau.
§ Affichez un message de confirmation, par exemple :
"Un nouveau livre est ajouté."
o Choix 2 (Rechercher un livre) :
§ Demandez à l’utilisateur de saisir le titre du livre recherché.
§ Appelez la fonction rechercherLivre(titre) qui doit afficher les
détails du livre si trouvé, ou un message indiquant que le livre n’a pas
été trouvé.
o Choix 3 (Lister tous les livres) :

Y. EL ALLIOUI – [email protected] 11 / 68
2025
§ Appelez la fonction listerLivres().
§ Avant d’afficher la liste, vous pouvez afficher un en-tête tel que :
---------- Liste des livres ----------
suivi de la liste des livres et enfin un pied de page, par exemple :
--------------------------------------
o Choix 4 (Supprimer un livre) :
§ Demandez à l’utilisateur de saisir le titre du livre à supprimer.
§ Appelez la fonction supprimerLivre(titre) qui doit supprimer le
livre du tableau et afficher un message de confirmation ou un message
d’erreur si le livre n’est pas trouvé.
o Choix 0 (Quitter) :
§ Affichez un message de fin (par exemple : "Fin du programme.") et
terminez l’exécution du programme.
o Choix invalide :
§ Si l’utilisateur saisit une option qui n’est pas valide, affichez un message
indiquant que le choix est invalide et invitez-le à réessayer.
4. Structure de la boucle principale :
o Mettez en place une boucle (par exemple, une boucle do...while ou while)
qui permettra de réafficher le menu après chaque action, jusqu’à ce que
l’utilisateur choisisse de quitter en entrant 0.
5. Conseils pédagogiques :
o Validation des entrées : Veillez à valider les saisies utilisateur. Par exemple,
vérifiez que l’année est bien un nombre.
o Modularité : Organisez votre code en fonctions distinctes pour chaque
opération afin d’améliorer la lisibilité et la maintenabilité.
o Commentaires : Commentez votre code de manière claire afin que chaque
étape soit compréhensible pour un lecteur qui n’aurait pas participé à la
réalisation.
o Tests : Testez chaque option du menu pour vous assurer que toutes les
fonctionnalités se comportent comme prévu.
Correction :
/*
Exercice 4 : Extension de l'Exercice 3 – Gestion d'une bibliothèque interactive

Description :
Ce programme permet de gérer une bibliothèque à l’aide d’un menu interactif.
Les options disponibles sont :
1 : Ajouter un nouveau livre
2 : Rechercher un livre
3 : Lister tous les livres
4 : Supprimer un livre
0 : Quitter le programme

Y. EL ALLIOUI – [email protected] 12 / 68
2025

Les fonctions utilisées ici proviennent de l’Exercice 3.
*/

// Déclaration initiale de la bibliothèque avec trois livres
let bibliotheque = [
{ titre: "Livre A", auteur: "Auteur A", annee: 2001 },
{ titre: "Livre B", auteur: "Auteur B", annee: 2005 },
{ titre: "Livre C", auteur: "Auteur C", annee: 2010 }
];

// Fonction pour ajouter un nouveau livre à la bibliothèque
function ajouterLivre(titre, auteur, annee) {
bibliotheque.push({ titre: titre, auteur: auteur, annee: annee });
}

// Fonction pour rechercher un livre par son titre
function rechercherLivre(titre) {
// Utilisation de toLowerCase() pour rendre la recherche insensible à la
casse
let livre = bibliotheque.find(l => l.titre.toLowerCase() ===
titre.toLowerCase());
if (livre) {
console.log("Livre trouvé :", livre);
} else {
console.log("Livre non trouvé.");
}
}

// Fonction pour lister tous les livres de la bibliothèque
function listerLivres() {
console.log("---------- Liste des livres ----------");
bibliotheque.forEach((livre, index) => {
console.log(
(index + 1) + " : " + livre.titre + " par " + livre.auteur + " (" +
livre.annee + ")"
);
});
console.log("------------------------------------ ");
}

// Fonction pour supprimer un livre par son titre
function supprimerLivre(titre) {
let index = bibliotheque.findIndex(l => l.titre.toLowerCase() ===
titre.toLowerCase());
if (index !== -1) {
bibliotheque.splice(index, 1);
console.log("Le livre a été supprimé." );
} else {
console.log("Livre non trouvé. Suppression impossible." );
}
}

Y. EL ALLIOUI – [email protected] 13 / 68
2025
// Fonction pour afficher le menu interactif et gérer les entrées utilisateur
function afficherMenu() {
while (true) {
// Affichage du menu
console.log("---------- Menu ----------");
console.log("1 : Ajouter un nouveau livre" );
console.log("2 : Rechercher un livre" );
console.log("3 : Lister tous les livres" );
console.log("4 : Supprimer un livre" );
console.log("0 : Quitter");
console.log("--------------------------- ");

// Récupération du choix utilisateur via prompt
let choix = prompt("Entrez votre choix :" );

// Vérifier si l'utilisateur annule ou entre "0" pour quitter
if (choix === null || choix === "0") {
console.log("Fin du programme.");
break;
}

// Traitement des différents choix
switch (choix) {
case "1": {
// Option pour ajouter un nouveau livre
let titre = prompt("Entrez le titre du livre :" );
let auteur = prompt("Entrez l'auteur du livre :" );
let anneeInput = prompt("Entrez l'année de publication :" );
let annee = parseInt(anneeInput, 10);
// Vérification des données saisies
if (titre && auteur && !isNaN(annee)) {
ajouterLivre(titre, auteur, annee);
console.log("Un nouveau livre est ajouté." );
} else {
console.log("Données invalides. Veuillez réessayer." );
}
break;
}
case "2": {
// Option pour rechercher un livre
let titre = prompt("Entrez le titre du livre à rechercher :" );
if (titre) {
rechercherLivre(titre);
} else {
console.log("Titre invalide.");
}
break;
}
case "3":
// Option pour lister tous les livres
listerLivres();
break;
case "4": {

Y. EL ALLIOUI – [email protected] 14 / 68
2025
// Option pour supprimer un livre
let titre = prompt("Entrez le titre du livre à supprimer :" );
if (titre) {
supprimerLivre(titre);
} else {
console.log("Titre invalide.");
}
break;
}
default:
console.log("Choix invalide. Veuillez réessayer." );
}
}
}

// Démarrage du menu interactif
afficherMenu();

Y. EL ALLIOUI – [email protected] 15 / 68
2025
Exercice 5 : Gestion d'une Liste d'Étudiants
Objectif :
Mettre en pratique la création et la manipulation d’objets et de tableaux, ainsi que l’écriture de
fonctions pour le traitement de données.
Consignes :
1. Création des données :
o Déclarez un tableau contenant plusieurs objets « étudiant ».
o Chaque objet doit comporter au minimum les propriétés suivantes :
§ prenom (chaîne de caractères)
§ nom (chaîne de caractères)
§ age (nombre)
§ note (nombre, représentant la moyenne ou une note sur 20)
2. Fonctions à implémenter :
o Une fonction pour afficher la liste complète des étudiants dans la console.
o Une fonction pour calculer et retourner la moyenne des notes de tous les
étudiants.
o Une fonction qui, étant donné un seuil de note (passé en paramètre), filtre et
retourne la liste des étudiants ayant une note supérieure ou égale à ce
seuil.
o Une fonction pour trier les étudiants par ordre alphabétique (sur le nom) ou
par note décroissante, selon un paramètre de tri.
3. Exécution :
o À l’exécution du programme, appelez successivement ces fonctions et affichez
leurs résultats dans la console pour valider le bon fonctionnement de chaque
fonctionnalité.
Correction :
// Création d'un tableau d'étudiants.
// Chaque étudiant est représenté par un objet avec les propriétés :
prenom, nom, age et note.
const etudiants = [
{ prenom: "Ali", nom: "ALI", age: 20, note: 15 },
{ prenom: "Leila", nom: "LEILA", age: 22, note: 12 },
{ prenom: "Hicham", nom: "HICHAM", age: 19, note: 18 },
{ prenom: "Khalid", nom: "KHALID", age: 21, note: 14 }
];

/*
Fonction : afficherEtudiants ()
Cette fonction parcourt le tableau et affiche les informations de chaque
étudiant dans la console.
*/
function afficherEtudiants(liste) {
console.log("Liste des étudiants :" );
liste.forEach((etudiant, index) => {
console.log(

Y. EL ALLIOUI – [email protected] 16 / 68
2025
`${index + 1}. ${etudiant.prenom} ${etudiant.nom}, Age :
${etudiant.age}, Note : ${etudiant.note}`
);
});
}

/*
Fonction : calculerMoyenne ()
Cette fonction calcule la moyenne des notes de tous les étudiants.
Elle additionne toutes les notes puis divise la somme par le nombre
d'étudiants.
*/
function calculerMoyenne(liste) {
let somme = 0;
liste.forEach(etudiant => {
somme += etudiant.note;
});
let moyenne = somme / liste.length;
return moyenne;
}

/*
Fonction : filtrerParNote ()
Cette fonction filtre le tableau des étudiants pour ne conserver que ceux
dont la note est supérieure ou égale au seuil passé en paramètre.
*/
function filtrerParNote(liste, seuil) {
const etudiantsFiltrés = liste.filter(etudiant => etudiant.note >=
seuil);
return etudiantsFiltrés;
}

/*
Fonction : trierEtudiants ()
Cette fonction trie une copie du tableau d'étudiants selon un critère
donné :
- "nom" : tri alphabétique sur le nom
- "note" : tri par note décroissante
*/
function trierEtudiants(liste, critere) {
// Création d'une copie du tableau pour ne pas modifier l'original.
let copieListe = liste.slice();
if (critere === "nom") {
// Tri alphabétique sur la propriété nom.
copieListe.sort((a, b) => {
if (a.nom < b.nom) return -1;
if (a.nom > b.nom) return 1;
return 0;
});
} else if (critere === "note") {
// Tri par note décroissante.
copieListe.sort((a, b) => b.note - a.note);
} else {

Y. EL ALLIOUI – [email protected] 17 / 68
2025
console.log("Critère de tri non reconnu. Utilisez 'nom' ou 'note'." );
}
return copieListe;
}

// ------------------ Exécution ------------------

console.log("=== Exercice 1 : Gestion d'une Liste d'Étudiants ===" );

// Affichage de la liste initiale des étudiants
afficherEtudiants(etudiants);

// Calcul et affichage de la moyenne des notes
let moyenneNotes = calculerMoyenne(etudiants);
console.log("Moyenne des notes :" , moyenneNotes);

// Filtrage des étudiants ayant une note supérieure ou égale à 15
let etudiantsExcellents = filtrerParNote(etudiants, 15);
console.log("Étudiants avec une note >= 15 :" );
afficherEtudiants(etudiantsExcellents);

// Tri des étudiants par ordre alphabétique (sur le nom)
let etudiantsTriesParNom = trierEtudiants(etudiants, "nom");
console.log("Étudiants triés par nom :" );
afficherEtudiants(etudiantsTriesParNom);

// Tri des étudiants par note décroissante
let etudiantsTriesParNote = trierEtudiants(etudiants, "note");
console.log("Étudiants triés par note décroissante :" );
afficherEtudiants(etudiantsTriesParNote );

Y. EL ALLIOUI – [email protected] 18 / 68
2025
Exercice 6 : Analyse et Transformation d'une Phrase
Objectif :
Exploiter les méthodes de manipulation des chaînes de caractères pour analyser et transformer
un texte donné.
Consignes :
1. Entrée de la phrase :
o Déclarez une variable contenant une phrase (par exemple, « JavaScript est un
langage polyvalent qui permet de manipuler des données de manière
flexible »).
2. Analyse de la phrase :
o Affichez dans la console :
§ Le nombre total de caractères (avec la propriété length).
§ Le nombre de mots (en utilisant la méthode split() avec l’espace
comme séparateur).
§ La première et la dernière lettre de la phrase (en utilisant charAt() ou
l’accès par indice).
3. Transformations :
o Convertissez la phrase en majuscules puis en minuscules, et affichez les
résultats.
o Créez une fonction qui inverse la phrase (sans utiliser directement une
méthode de conversion en tableau suivie de reverse(), mais en parcourant la
chaîne avec une boucle) et affichez la phrase inversée.
o Utilisez la méthode replace() ou replaceAll() pour remplacer un mot
spécifique de la phrase par un autre (par exemple, remplacer « polyvalent » par
« puissant ») et affichez le résultat.
Correction :
// Déclaration d'une phrase à analyser
let phrase = "JavaScript est un langage polyvalent qui permet de manipuler
des données de manière flexible" ;

console.log("=== Exercice 2 : Analyse et Transformation d'une Phrase ===" );

// Affichage du nombre total de caractères de la phrase
console.log("Nombre total de caractères :" , phrase.length);

// Découpage de la phrase en mots en utilisant l'espace comme séparateur
let mots = phrase.split(" ");
console.log("Nombre total de mots :" , mots.length);

// Récupération de la première et de la dernière lettre de la phrase
let premiereLettre = phrase.charAt(0); // Première lettre
let derniereLettre = phrase.charAt(phrase.length - 1); // Dernière lettre
console.log("Première lettre :", premiereLettre);
console.log("Dernière lettre :", derniereLettre);

// Transformation de la phrase en majuscules et en minuscules

Y. EL ALLIOUI – [email protected] 19 / 68
2025
let phraseMaj = phrase.toUpperCase();
let phraseMin = phrase.toLowerCase();
console.log("Phrase en majuscules :" , phraseMaj);
console.log("Phrase en minuscules :" , phraseMin);

/*
Fonction : inverserChaine
Cette fonction inverse une chaîne de caractères sans utiliser directement
la méthode reverse(). On parcourt la chaîne de la fin vers le début et on
construit une nouvelle chaîne.
*/
function inverserChaine(str) {
let resultat = "";
// Boucle de la fin vers le début
for (let i = str.length - 1; i >= 0; i--) {
resultat += str[i];
}
return resultat;
}

// Inversion de la phrase
let phraseInversee = inverserChaine(phrase);
console.log("Phrase inversée :", phraseInversee);

// Remplacement d'un mot dans la phrase
// Remplacer "polyvalent" par "puissant"
let phraseModifiee = phrase.replace("polyvalent", "puissant");
console.log("Phrase modifiée (remplacement de 'polyvalent' par 'puissant')
:", phraseModifiee);

Y. EL ALLIOUI – [email protected] 20 / 68
2025
Exercice 7 : Système de Gestion de Produits
Objectif :
Développer une application console de gestion d’inventaire qui combine la manipulation
d’objets et de tableaux, l’utilisation de fonctions et l’application d’opérations arithmétiques et
conditionnelles.
Consignes :
1. Modélisation des produits :
o Créez un tableau d’objets, chaque objet représentant un produit avec les
propriétés suivantes :
§ id (identifiant unique, nombre ou chaîne)
§ nom (chaîne de caractères)
§ prix (nombre)
§ quantite (nombre représentant le stock)
2. Fonctionnalités à implémenter :
o Ajout d’un produit : Créez une fonction qui prend en paramètres les
informations d’un produit et l’ajoute au tableau d’inventaire.
o Mise à jour du stock : Créez une fonction qui, à partir d’un identifiant de
produit et d’un nombre (positif pour ajouter ou négatif pour retirer), met à jour
la quantité en stock.
o Affichage de l’inventaire : Créez une fonction pour afficher la liste de tous les
produits avec leurs détails dans la console.
o Calcul de la valeur totale de l’inventaire : Écrivez une fonction qui calcule
la somme du produit du prix et de la quantite pour chaque produit et affiche
la valeur totale.
o Recherche du produit le plus cher : Implémentez une fonction qui parcourt
le tableau et retourne le produit ayant le prix le plus élevé.
3. Exécution :
o Initialisez le tableau avec quelques produits exemples.
o Faites appel aux différentes fonctions et affichez les résultats dans la console
afin de vérifier le bon fonctionnement du système de gestion.
Correction :
// Initialisation d'un tableau d'objets produits.
let inventaire = [
{ id: 1, nom: "Clavier", prix: 30, quantite: 15 },
{ id: 2, nom: "Souris", prix: 20, quantite: 25 },
{ id: 3, nom: "Écran", prix: 150, quantite: 10 },
{ id: 4, nom: "Ordinateur", prix: 800, quantite: 5 }
];

/*
Fonction : ajouterProduit()
Cette fonction ajoute un nouveau produit à l'inventaire.
Elle reçoit les informations du produit et crée un nouvel objet qu'elle
ajoute au tableau.
*/
function ajouterProduit(inventaire, id, nom, prix, quantite) {

Y. EL ALLIOUI – [email protected] 21 / 68
2025
const nouveauProduit = { id: id, nom: nom, prix: prix, quantite:
quantite };
inventaire.push(nouveauProduit);
console.log(`Produit ajouté : ${nom}`);
}

/*
Fonction : mettreAJourStock()
Cette fonction met à jour la quantité d'un produit identifié par son id.
Le paramètre quantiteModif représente l'augmentation (nombre positif)
ou la diminution (nombre négatif) du stock.
*/
function mettreAJourStock(inventaire, id, quantiteModif) {
// Recherche du produit par son id
let produit = inventaire.find(item => item.id === id);
if (produit) {
produit.quantite += quantiteModif; // Mise à jour de la quantité
console.log(`Stock mis à jour pour ${produit.nom} : nouvelle quantité
= ${produit.quantite}`);
} else {
console.log(`Produit avec l'id ${id} non trouvé.`);
}
}

/*
Fonction : afficherInventaire()
Cette fonction affiche la liste complète des produits de l'inventaire
dans la console.
*/
function afficherInventaire(inventaire) {
console.log("=== Inventaire des Produits ===" );
inventaire.forEach(produit => {
console.log(`ID: ${produit.id}, Nom: ${produit.nom}, Prix:
${produit.prix}€, Quantité: ${produit.quantite}`);
});
}

/*
Fonction : valeurTotaleInventaire ()
Cette fonction calcule la valeur totale de l'inventaire.
Elle multiplie le prix par la quantité pour chaque produit et additionne
le résultat.
*/
function valeurTotaleInventaire (inventaire) {
let valeurTotale = 0;
inventaire.forEach(produit => {
valeurTotale += produit.prix * produit.quantite;
});
return valeurTotale;
}

/*
Fonction : trouverProduitPlusCher ()

Y. EL ALLIOUI – [email protected] 22 / 68
2025
Cette fonction parcourt l'inventaire et retourne le produit ayant le prix
le plus élevé.
*/
function trouverProduitPlusCher (inventaire) {
let produitPlusCher = inventaire[0]; // Initialisation avec le premier
produit
inventaire.forEach(produit => {
if (produit.prix > produitPlusCher.prix) {
produitPlusCher = produit;
}
});
return produitPlusCher;
}

// ------------------ Exécution ------------------

console.log("=== Exercice 3 : Système de Gestion de Produits ===" );

// Affichage de l'inventaire initial
afficherInventaire(inventaire);

// Ajout d'un nouveau produit à l'inventaire
ajouterProduit(inventaire, 5, "Imprimante", 120, 8);

// Mise à jour du stock pour certains produits :
// Ajouter 5 unités pour le produit avec id 2 (Souris)
mettreAJourStock(inventaire, 2, 5);
// Retirer 2 unités pour le produit avec id 4 (Ordinateur)
mettreAJourStock(inventaire, 4, -2);

// Affichage de l'inventaire après modifications
afficherInventaire(inventaire);

// Calcul et affichage de la valeur totale de l'inventaire
let valeurTotale = valeurTotaleInventaire (inventaire);
console.log("Valeur totale de l'inventaire :" , valeurTotale, "€");

// Recherche et affichage du produit le plus cher
let produitChere = trouverProduitPlusCher (inventaire);
console.log(
"Produit le plus cher :" ,
`ID: ${produitChere.id}, Nom: ${produitChere.nom}, Prix:
${produitChere.prix}€`
);

Y. EL ALLIOUI – [email protected] 23 / 68
2025
Série 2
Maîtrise du DOM JavaScript - De la manipulation
élémentaire aux applications interactives
Exercice 1 : Sélection et modification de contenu
Objectif
Appliquer les méthodes de sélection et modifier le contenu et les attributs d’éléments
existants.
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Exercice 1</title></head>
<body>
<h1 id="titre-principal">Titre initial</h1>
<p class="intro">Texte d’introduction à remplacer. </p>
<ul id="liste">
<li data-index="1">Item 1</li>
<li data-index="2">Item 2</li>
<li data-index="3">Item 3</li>
</ul>
<a href="https://example.com" id="mon-lien">Lien vers example.com </a>
</body>
</html>
Consignes
1. Sélectionner et remplacer le texte du <h1> par « Bienvenue au TP DOM ».
2. Récupérer tous les <li> de la liste et préfixer leur contenu par « Élément – ».
3. Modifier dynamiquement l’attribut href du lien pour qu’il pointe vers
« https://www.usms.ac.ma ».
4. À l’aide de querySelector, changer la classe de <p
class="intro"> en paragraph-intro et ajouter un title (attribut) décrivant son
rôle.
Méthodes clés :
document.getElementById() , getElementsByTagName(), querySelector(), element.te
xtContent, element.setAttribute() .
Exercice 2 : Création et insertion d’éléments
Objectif
Maîtriser la création, l’assignation d’attributs, puis l’insertion de nouveaux nœuds dans
l’arbre DOM.

Y. EL ALLIOUI – [email protected] 24 / 68
2025
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Exercice 2</title></head>
<body>
<div id="container">
<h2>Liste des fruits</h2>
<ul id="fruit-list"></ul>
</div>
</body>
</html>
Consignes
1. Créer en JavaScript un tableau :
const fruits = ['Pomme','Banane','Cerise','Orange']; .
2. Pour chaque élément du tableau, générer un <li> :
o utiliser document.createElement('li') ,
o renseigner le texte via textContent,
o ajouter un attribut data-fruit égal au nom du fruit,
o et enfin insérer l’<li> dans <ul id="fruit-list"> avec appendChild().
3. Avant le dernier <li>, insérer un nouvel <li> « Kiwi » en utilisant insertBefore().
4. Ajouter un paragraphe <p> sous la liste indiquant « Total : X fruits », où X est la
longueur du tableau (après insertion), en
utilisant createElement et appendChild.
Méthodes clés :
createElement(), setAttribute(), appendChild(), insertBefore() .
Exercice 3 : Clonage, remplacement et suppression de nœuds
Objectif
S’exercer au clonage profond, au remplacement et à la suppression de nœuds, ainsi
qu’à la navigation (parent/child).
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Exercice 3</title></head>
<body>
<div id="cartes">
<div class="carte">
<h3>Carte Prototype</h3>
<p>Description prototype. </p>
</div>
</div>
<button id="dupliquer">Dupliquer la carte</button>
<button id="remplacer">Remplacer la carte</button>
<button id="supprimer">Supprimer la dernière carte </button>
</body>
</html>
Consignes

Y. EL ALLIOUI – [email protected] 25 / 68
2025
1. Duplication : au clic sur « Dupliquer la carte », cloner la <div class="carte"> (y
compris ses enfants) et l’ajouter à la fin de <div id="cartes">.
2. Remplacement : au clic sur « Remplacer la carte », créer une nouvelle <div
class="carte"> avec un titre « Carte Remplaçante » et un paragraphe « Cette
carte a remplacé la première », puis remplacer la première carte existante.
3. Suppression : au clic sur « Supprimer la dernière carte », supprimer le dernier
nœud enfant de #cartes.
4. Vérifier avant chaque action que #cartes possède au moins un enfant, et aaicher
une alerte si la collection est vide (par exemple, « Aucune carte à supprimer »).
Méthodes clés :
cloneNode(), replaceChild(), removeChild(), hasChildNodes() .
Exercice 4 : Événements et styles dynamiques
Objectif
Apprendre à gérer les événements utilisateur et à modifier dynamiquement les styles via
le DOM.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 4</title>
<style>
.surbrillance { background-color: yellow; }
</style>
</head>
<body>
<ul id="liste-colors">
<li data-color="red">Rouge</li>
<li data-color="green">Vert</li>
<li data-color="blue">Bleu</li>
</ul>
<button id="toggle-highlight">Activer/Désactiver surbrillance </button>
</body>
</html>
Consignes
1. Sélectionner chaque <li> de la liste et, au survol (mouseover / mouseout),
changer la couleur de fond du <body>pour la valeur de data-color.
2. Au clic sur le bouton « Activer/Désactiver surbrillance », activer ou désactiver
(toggle) la classe surbrillancesur tous les <li> avec classList.toggle().
3. Utiliser addEventListener et removeEventListener pour pouvoir désactiver
totalement le comportement au survol lorsque la surbrillance est désactivée
(vérifier un flag boolean).

Y. EL ALLIOUI – [email protected] 26 / 68
2025
Méthodes clés :
element.addEventListener() , element.removeEventListener() , element.classList.
toggle(), event.currentTarget.dataset .
Exercice 5 : Formulaire interactif et validation
Objectif
Mettre en œuvre la gestion d’événements sur les formulaires pour créer un formulaire
interactif avec contrôles dynamiques.
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Exercice 5</title></head>
<body>
<form id="inscription">
<label>Email : <input type="email" id="email" required></label><br>
<label>
<input type="checkbox" id="newsletter"> S’abonner à la newsletter
</label><br>
<div id="options-newsletter" style="display:none;">
<label>Fréquence :
<select id="freq">
<option>Hebdomadaire</option>
<option>Mensuel</option>
</select>
</label>
</div>
<button type="submit">Envoyer</button>
</form>
Consignes
1. Sur le change de la checkbox #newsletter, aaicher ou masquer la <div
id="options-newsletter"> (propriétés style.display).
2. Sur la soumission du formulaire (submit), vérifier que l’email contient « @univ »
(simple test includes). Si non valide, bloquer l’envoi (event.preventDefault())
et aaicher une remarque dans un <p> ajouté sous le formulaire.
3. Une fois validé, réinitialiser le formulaire (form.reset()).
Méthodes clés :
form.addEventListener('submit', …) , event.preventDefault(),
element.style.display, form.reset().
Exercice 6 : Filtrage dynamique et délégation d’événements
Objectif
Utiliser l’événement input et la délégation d’événements pour filtrer en temps réel une
liste, et comprendre la propagation (bubbling).
<!DOCTYPE html>

Y. EL ALLIOUI – [email protected] 27 / 68
2025
<html lang="fr">
<head><meta charset="UTF-8"><title>Exercice 6</title></head>
<body>
<input type="text" id="filtre" placeholder="Rechercher…">
<ul id="taches">
<li>Faire les courses</li>
<li>Rendre le TP</li>
<li>Appeler le professeur </li>
<li>Préparer le quiz</li>
</ul>
</body>
</html>
Consignes
1. Sur chaque input de #filtre, récupérer la valeur saisie et masquer
(style.display = 'none') tous les <li>dont le texte ne contient pas la chaîne
(méthode textContent.toLowerCase().includes() ).
2. Mettre en place un seul gestionnaire click sur le <ul id="taches"> pour
détecter le clic sur un <li> (délégation) et, lors du clic, barrer le texte de
l’élément (style.textDecoration = 'line -through').
3. Sur double-clic (dblclick) sur un <li>, supprimer directement cet item du DOM
avec removeChild().
Méthodes clés :
element.addEventListener('input', …) , element.addEventListener('click', …) ,
propagation d’événements, element.textContent, parent.removeChild(child) .
Exercice 7 : Gestionnaire de tâches simple
Objectif : Créer une application simple de gestion de tâches qui permet d'ajouter, supprimer
et marquer les tâches comme terminées.
Consignes :
1. Créer une interface HTML avec:
o Un champ de saisie (input)
o Un bouton "Ajouter"
o Une liste non ordonnée (ul) vide pour afficher les tâches
2. Implémenter les fonctionnalités suivantes en JavaScript:
o Ajouter une nouvelle tâche à la liste quand l'utilisateur clique sur le bouton
"Ajouter"
o Chaque tâche doit avoir un bouton "Supprimer" qui permet de la retirer de la
liste
o Un clic sur la tâche doit la marquer comme "terminée" (ajouter une classe CSS
qui barre le texte)
o Empêcher l'ajout de tâches vides
o Effacer le champ de saisie après l'ajout d'une tâche
Compétences testées : Sélection d'éléments, création et insertion de nœuds, gestion des
événements, modification d'attributs et de classes CSS.

Y. EL ALLIOUI – [email protected] 28 / 68
2025
Correction :
Structure HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gestionnaire de tâches </title>
<style>
/* Style pour les tâches terminées */
.task-completed {
text-decoration: line-through;
color: #888;
}
/* Styles de base pour la liste de tâches */
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 10px;
margin: 5px 0;
background-color: #f9f9f9;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-text {
cursor: pointer;
flex-grow: 1;
}
.delete-btn {
background-color: #ff6b6b;
color: white;
border: none;
border-radius: 3px;
padding: 5px 10px;
cursor: pointer;
}
input {
padding: 8px;
margin-right: 5px;
border-radius: 3px;
border: 1px solid #ddd;
}
button {
padding: 8px 15px;
background-color: #4caf50;
color: white;
border: none;

Y. EL ALLIOUI – [email protected] 29 / 68
2025
border-radius: 3px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>Gestionnaire de tâches </h1>

<div>
<input type="text" id="taskInput" placeholder="Nouvelle tâche...">
<button id="addTaskBtn">Ajouter</button>
</div>

<ul id="taskList">
<!-- Les tâches seront ajoutées ici dynamiquement -->
</ul>

<script src="app.js"></script>
</body>
</html>
Code JavaScript (app.js)
// Attendre que le DOM soit complètement chargé
document.addEventListener('DOMContentLoaded', function() {
// Récupérer les éléments nécessaires du DOM
const taskInput = document.getElementById('taskInput');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskList = document.getElementById('taskList');

// Ajouter un écouteur d'événement sur le bouton d'ajout
addTaskBtn.addEventListener('click', addTask);

// Ajouter un écouteur d'événement pour permettre d'ajouter une tâche
avec la touche Entrée
taskInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addTask();
}
});

// Fonction pour ajouter une nouvelle tâche
function addTask() {
// Récupérer la valeur saisie et supprimer les espaces inutiles
const taskText = taskInput.value.trim();

// Vérifier si le champ n'est pas vide
if (taskText !== '') {
// Créer un nouvel élément de liste
const li = document.createElement('li');

// Créer un élément span pour le texte de la tâche
const taskTextSpan = document.createElement('span');

Y. EL ALLIOUI – [email protected] 30 / 68
2025
taskTextSpan.textContent = taskText;
taskTextSpan.className = 'task-text';

// Ajouter un écouteur d'événement pour marquer la tâche comme
terminée
taskTextSpan.addEventListener('click', function() {
this.classList.toggle('task-completed');
});

// Créer un bouton de suppression
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Supprimer';
deleteBtn.className = 'delete-btn';

// Ajouter un écouteur d'événement pour supprimer la tâche
deleteBtn.addEventListener('click', function() {
taskList.removeChild(li);
});

// Ajouter les éléments à la liste
li.appendChild(taskTextSpan);
li.appendChild(deleteBtn);
taskList.appendChild(li);

// Effacer le champ de saisie
taskInput.value = '';

// Remettre le focus sur le champ de saisie
taskInput.focus();
} else {
// Alerter l'utilisateur si la tâche est vide
alert('Veuillez entrer une tâche valide!' );
}
}
});
Explication du code
1. Structure HTML :
o Un champ de saisie (input) pour entrer de nouvelles tâches
o Un bouton pour ajouter des tâches
o Une liste non ordonnée (ul) qui contiendra les tâches
o Des styles CSS pour l'affichage des tâches
2. Fonctionnalités JavaScript :
o DOMContentLoaded : S'assure que le script s'exécute une fois que le DOM est
chargé
o Récupération des éléments du DOM avec getElementById
o Ajout d'écouteurs d'événements pour le clic sur le bouton et la touche Entrée
o Fonction addTask() qui :
§ Vérifie que le texte de la tâche n'est pas vide
§ Crée une nouvelle tâche (élément li) avec un texte et un bouton de
suppression

Y. EL ALLIOUI – [email protected] 31 / 68
2025
§ Ajoute des écouteurs d'événements pour marquer une tâche comme
terminée (clic sur le texte)
§ Ajoute des écouteurs d'événements pour supprimer une tâche (clic sur
le bouton supprimer)
§ Vide le champ de saisie après l'ajout d'une tâche
Exercice 8 : Formulaire dynamique avec validation
Objectif : Créer un formulaire d'inscription avec validation dynamique des champs.
Consignes :
1. Créer un formulaire HTML contenant:
o Nom (minimum 3 caractères)
o Email (format valide)
o Mot de passe (minimum 8 caractères, doit contenir au moins un chiffre)
o Confirmation de mot de passe
o Liste déroulante pour le pays
o Case à cocher pour accepter les conditions d'utilisation
o Bouton d'envoi
2. Implémenter les fonctionnalités suivantes en JavaScript:
o Valider chaque champ en temps réel (pendant que l'utilisateur tape)
o Afficher des messages d'erreur à côté des champs invalides
o Changer la couleur de bordure des champs (rouge pour invalide, vert pour
valide)
o Empêcher l'envoi du formulaire si tous les champs ne sont pas valides
o Afficher un résumé des informations saisies dans une div après validation
réussie (sans envoyer réellement le formulaire)
Compétences testées : Manipulation de formulaires, validation avec expressions régulières,
gestion avancée des événements (focus, blur, input), modification du DOM en fonction des
actions utilisateur.
Correction :
Structure HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Formulaire avec validation </title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.form-group {

Y. EL ALLIOUI – [email protected] 32 / 68
2025
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.error-message {
color: #d9534f;
font-size: 0.9em;
margin-top: 5px;
display: none;
}
.valid-field {
border: 1px solid #5cb85c !important;
}
.invalid-field {
border: 1px solid #d9534f !important;
}
button {
background-color: #5cb85c;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 1em;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
#summaryBox {
margin-top: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
display: none;
}
</style>
</head>
<body>
<h1>Formulaire d'inscription </h1>

<form id="registrationForm">
<div class="form-group">

Y. EL ALLIOUI – [email protected] 33 / 68
2025
<label for="name">Nom:</label>
<input type="text" id="name" name="name">
<div id="nameError" class="error-message">Le nom doit contenir
au moins 3 caractères. </div>
</div>

<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<div id="emailError" class="error-message">Veuillez entrer une
adresse email valide.</div>
</div>

<div class="form-group">
<label for="password">Mot de passe:</label>
<input type="password" id="password" name="password">
<div id="passwordError" class="error-message">Le mot de passe
doit contenir au moins 8 caractères et inclure au moins un chiffre. </div>
</div>

<div class="form-group">
<label for="confirmPassword">Confirmation du mot de
passe:</label>
<input type="password" id="confirmPassword"
name="confirmPassword">
<div id="confirmPasswordError" class="error-message">Les mots
de passe ne correspondent pas. </div>
</div>

<div class="form-group">
<label for="country">Pays:</label>
<select id="country" name="country">
<option value="">Sélectionnez un pays</option>
<option value="france">France</option>
<option value="canada">Canada</option>
<option value="belgique">Belgique</option>
<option value="suisse">Suisse</option>
<option value="maroc">Maroc</option>
</select>
<div id="countryError" class="error-message">Veuillez
sélectionner un pays.</div>
</div>

<div class="form-group">
<input type="checkbox" id="terms" name="terms">
<label for="terms" style="display: inline;">J'accepte les
conditions d'utilisation </label>
<div id="termsError" class="error-message">Vous devez accepter
les conditions d'utilisation. </div>
</div>

<button type="submit" id="submitBtn" disabled>S'inscrire</button>
</form>

Y. EL ALLIOUI – [email protected] 34 / 68
2025

<div id="summaryBox">
<h2>Résumé de l'inscription </h2>
<div id="summary"></div>
</div>

<script src="validation.js"></script>
</body>
</html>
Code JavaScript (validation.js)
document.addEventListener('DOMContentLoaded', function() {
// Récupérer les éléments du formulaire
const form = document.getElementById('registrationForm');
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
const confirmPasswordInput =
document.getElementById('confirmPassword');
const countrySelect = document.getElementById('country');
const termsCheckbox = document.getElementById('terms');
const submitBtn = document.getElementById('submitBtn');
const summaryBox = document.getElementById('summaryBox');
const summary = document.getElementById('summary');

// Objet pour stocker l'état de validation de chaque champ
const validationState = {
name: false,
email: false,
password: false,
confirmPassword: false,
country: false,
terms: false
};

// Fonction qui met à jour l'état du bouton de soumission
function updateSubmitButton() {
// Vérifier si tous les champs sont valides
const allValid = Object.values(validationState).every(state =>
state === true);
submitBtn.disabled = !allValid;
}

// Fonction générique pour valider un champ
function validateField(input, errorElement, validationFunction,
errorMessage) {
const value = input.value.trim();
const isValid = validationFunction(value);

if (isValid) {
input.classList.add('valid-field');
input.classList.remove('invalid-field');

Y. EL ALLIOUI – [email protected] 35 / 68
2025
errorElement.style.display = 'none';
validationState[input.id] = true;
} else {
input.classList.add('invalid-field');
input.classList.remove('valid-field');
errorElement.textContent = errorMessage;
errorElement.style.display = 'block';
validationState[input.id] = false;
}

updateSubmitButton();
return isValid;
}

// Fonction de validation du nom
function validateName() {
const nameError = document.getElementById('nameError');
return validateField(nameInput, nameError,
value => value.length >= 3,
'Le nom doit contenir au moins 3 caractères.' );
}

// Fonction de validation de l'email
function validateEmail() {
const emailError = document.getElementById('emailError');
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return validateField(emailInput, emailError,
value => emailRegex.test(value),
'Veuillez entrer une adresse email valide.' );
}

// Fonction de validation du mot de passe
function validatePassword() {
const passwordError = document.getElementById('passwordError');
const passwordRegex = /^(?=.*\d).{8,}$/;
return validateField(passwordInput, passwordError,
value => passwordRegex.test(value),
'Le mot de passe doit contenir au moins 8 caractères et inclure
au moins un chiffre.');
}

// Fonction de validation de la confirmation du mot de passe
function validateConfirmPassword () {
const confirmPasswordError =
document.getElementById('confirmPasswordError' );
return validateField(confirmPasswordInput, confirmPasswordError,
value => value === passwordInput.value && value !== '',
'Les mots de passe ne correspondent pas.' );
}

// Fonction de validation du pays
function validateCountry() {
const countryError = document.getElementById('countryError');

Y. EL ALLIOUI – [email protected] 36 / 68
2025
return validateField(countrySelect, countryError,
value => value !== '',
'Veuillez sélectionner un pays.' );
}

// Fonction de validation des conditions d'utilisation
function validateTerms() {
const termsError = document.getElementById('termsError');

if (termsCheckbox.checked) {
termsError.style.display = 'none';
validationState.terms = true;
} else {
termsError.style.display = 'block';
validationState.terms = false;
}

updateSubmitButton();
return termsCheckbox.checked;
}

// Ajouter des écouteurs d'événements pour valider en temps réel
nameInput.addEventListener('input', validateName);
emailInput.addEventListener('input', validateEmail);
passwordInput.addEventListener('input', validatePassword);
confirmPasswordInput.addEventListener('input',
validateConfirmPassword );
countrySelect.addEventListener('change', validateCountry);
termsCheckbox.addEventListener('change', validateTerms);

// Validation du mot de passe de confirmation quand le mot de passe
change
passwordInput.addEventListener('input', function() {
if (confirmPasswordInput.value !== '') {
validateConfirmPassword ();
}
});

// Gérer la soumission du formulaire
form.addEventListener('submit', function(e) {
e.preventDefault(); // Empêcher l'envoi réel du formulaire

// Valider tous les champs une dernière fois
const nameValid = validateName();
const emailValid = validateEmail();
const passwordValid = validatePassword();
const confirmPasswordValid = validateConfirmPassword ();
const countryValid = validateCountry();
const termsValid = validateTerms();

// Si tous les champs sont valides, afficher le résumé
if (nameValid && emailValid && passwordValid &&
confirmPasswordValid && countryValid && termsValid) {

Y. EL ALLIOUI – [email protected] 37 / 68
2025
// Créer le résumé
summary.innerHTML = `
<p><strong>Nom:</strong> ${nameInput.value}</p>
<p><strong>Email:</strong> ${emailInput.value}</p>
<p><strong>Pays:</strong>
${countrySelect.options[countrySelect.selectedIndex].text} </p>
<p><strong>Conditions acceptées: </strong> Oui</p>
`;

// Afficher la boîte de résumé
summaryBox.style.display = 'block';
}
});
});
Explication du code
1. Structure HTML :
o Un formulaire avec plusieurs champs : nom, email, mot de passe, confirmation
de mot de passe, pays et conditions d'utilisation
o Des messages d'erreur pour chaque champ (initialement cachés)
o Une zone pour afficher le résumé des informations après validation
o Des styles CSS pour indiquer visuellement la validité des champs
2. Fonctionnalités JavaScript :
o Un objet validationState pour suivre l'état de validation de chaque champ
o Une fonction générique validateField pour simplifier la validation
o Des fonctions spécifiques pour chaque type de validation :
§ validateName : vérifie que le nom contient au moins 3 caractères
§ validateEmail : utilise une expression régulière pour vérifier le
format de l'email
§ validatePassword : vérifie que le mot de passe contient au moins 8
caractères et un chiffre
§ validateConfirmPassword : vérifie que les mots de passe
correspondent
§ validateCountry : vérifie qu'un pays a été sélectionné
§ validateTerms : vérifie que les conditions d'utilisation ont été
acceptées
o Des écouteurs d'événements pour valider les champs en temps réel
o Une fonction updateSubmitButton qui active/désactive le bouton d'envoi
selon l'état de validation
o Un gestionnaire pour la soumission du formulaire qui affiche un résumé des
informations

Exercice 9 : Générateur de tableau dynamique
Objectif : Créer une application qui génère un tableau HTML dynamique à partir de données
et permet des opérations sur celui-ci.

Y. EL ALLIOUI – [email protected] 38 / 68
2025
Consignes :
1. Créer une interface HTML avec:
o Un tableau vide avec en-tête préparé (ID, Nom, Email, Actions)
o Des boutons pour: ajouter une ligne, trier par nom, filtrer
2. Implémenter les fonctionnalités suivantes en JavaScript:
o Charger des données initiales dans le tableau (depuis un tableau JavaScript)
o Permettre l'ajout de nouvelles lignes via un formulaire qui apparaît au clic sur
"Ajouter"
o Implémenter la fonctionnalité de tri du tableau (croissant/décroissant) en
cliquant sur l'en-tête
o Ajouter un champ de recherche qui filtre le tableau en temps réel
o Pour chaque ligne, ajouter:
§ Un bouton "Modifier" qui transforme la ligne en champs éditables
§ Un bouton "Supprimer" qui retire la ligne après confirmation
o Sauvegarder les modifications dans le tableau de données JavaScript
Données initiales suggérées :
const utilisateurs = [
{ id: 1, nom: "Dupont Jean", email: "[email protected]" },
{ id: 2, nom: "Martin Sophie", email: "[email protected]" },
{ id: 3, nom: "Dubois Pierre", email: "[email protected]" }
];
Compétences testées : Manipulation avancée du DOM, création dynamique de tables HTML,
tri et filtrage de données, gestion d'événements complexes, transformation d'éléments (cellules
de tableau en champs de formulaire), utilisation de méthodes de tableau JavaScript.
Correction :
Structure HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Générateur de tableau dynamique </title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;

Y. EL ALLIOUI – [email protected] 39 / 68
2025
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th {
background-color: #f2f2f2;
cursor: pointer;
}
th:hover {
background-color: #e0e0e0;
}
.action-buttons {
display: flex;
gap: 5px;
}
button {
padding: 5px 10px;
border: none;
border-radius: 3px;
cursor: pointer;
}
.add-btn {
background-color: #5cb85c;
color: white;
margin-bottom: 10px;
}
.edit-btn {
background-color: #5bc0de;
color: white;
}
.delete-btn {
background-color: #d9534f;
color: white;
}
.form-container {
margin-top: 20px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 5px;
display: none;
}
.form-group {
margin-bottom: 10px;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;

Y. EL ALLIOUI – [email protected] 40 / 68
2025
box-sizing: border-box;
}
.search-container {
margin-bottom: 20px;
}
#searchInput {
padding: 8px;
width: 60%;
}
</style>
</head>
<body>
<h1>Gestion des utilisateurs </h1>

<div class="search-container">
<input type="text" id="searchInput" placeholder="Rechercher...">
</div>

<button id="addButton" class="add-btn">Ajouter un utilisateur </button>
<button id="sortButton" class="add-btn">Trier par nom</button>

<div id="formContainer" class="form-container">
<h2 id="formTitle">Ajouter un utilisateur </h2>
<form id="userForm">
<input type="hidden" id="userId">
<div class="form-group">
<label for="userName">Nom:</label>
<input type="text" id="userName" required>
</div>
<div class="form-group">
<label for="userEmail">Email:</label>
<input type="email" id="userEmail" required>
</div>
<button type="submit">Enregistrer</button>
<button type="button" id="cancelButton">Annuler</button>
</form>
</div>

<table id="userTable">
<thead>
<tr>
<th>ID</th>
<th id="nameHeader">Nom</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tableBody">
<!-- Le contenu du tableau sera généré en JavaScript -->
</tbody>
</table>

<script src="table-generator.js"></script>

Y. EL ALLIOUI – [email protected] 41 / 68
2025
</body>
</html>
Code JavaScript (table-generator.js)
document.addEventListener('DOMContentLoaded', function() {
// Données initiales
let utilisateurs = [
{ id: 1, nom: "Dupont Jean", email: "[email protected]" },
{ id: 2, nom: "Martin Sophie", email: "[email protected]" },
{ id: 3, nom: "Dubois Pierre", email: "[email protected]" }
];

// Variables pour le suivi de l'état
let nextId = 4;
let sortDirection = 'asc';
let isEditing = false;

// Récupérer les éléments du DOM
const tableBody = document.getElementById('tableBody');
const addButton = document.getElementById('addButton');
const sortButton = document.getElementById('sortButton');
const formContainer = document.getElementById('formContainer');
const userForm = document.getElementById('userForm');
const cancelButton = document.getElementById('cancelButton');
const searchInput = document.getElementById('searchInput');
const nameHeader = document.getElementById('nameHeader');

// Fonction pour afficher les utilisateurs dans le tableau
function renderTable(data) {
tableBody.innerHTML = '';

data.forEach(user => {
const row = document.createElement('tr');

// Cellule ID
const idCell = document.createElement('td');
idCell.textContent = user.id;
row.appendChild(idCell);

// Cellule Nom
const nameCell = document.createElement('td');
nameCell.textContent = user.nom;
row.appendChild(nameCell);

// Cellule Email
const emailCell = document.createElement('td');
emailCell.textContent = user.email;
row.appendChild(emailCell);

// Cellule Actions
const actionsCell = document.createElement('td');
actionsCell.className = 'action-buttons';

Y. EL ALLIOUI – [email protected] 42 / 68
2025

// Bouton Modifier
const editButton = document.createElement('button');
editButton.textContent = 'Modifier';
editButton.className = 'edit-btn';
editButton.addEventListener('click', () => editUser(user));
actionsCell.appendChild(editButton);

// Bouton Supprimer
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Supprimer';
deleteButton.className = 'delete-btn';
deleteButton.addEventListener('click', () =>
deleteUser(user.id));
actionsCell.appendChild(deleteButton);

row.appendChild(actionsCell);
tableBody.appendChild(row);
});
}

// Fonction pour ajouter un nouvel utilisateur
function addUser(nom, email) {
const newUser = {
id: nextId++,
nom: nom,
email: email
};

utilisateurs.push(newUser);
renderTable(utilisateurs);
}

// Fonction pour préparer l'édition d'un utilisateur
function editUser(user) {
isEditing = true;
document.getElementById('formTitle').textContent = 'Modifier un
utilisateur';
document.getElementById('userId').value = user.id;
document.getElementById('userName').value = user.nom;
document.getElementById('userEmail').value = user.email;
formContainer.style.display = 'block';
}

// Fonction pour mettre à jour un utilisateur
function updateUser(id, nom, email) {
const index = utilisateurs.findIndex(user => user.id ===
parseInt(id));
if (index !== -1) {
utilisateurs[index].nom = nom;
utilisateurs[index].email = email;
renderTable(utilisateurs);
}

Y. EL ALLIOUI – [email protected] 43 / 68
2025
}

// Fonction pour supprimer un utilisateur
function deleteUser(id) {
if (confirm('Êtes-vous sûr de vouloir supprimer cet utilisateur
?')) {
utilisateurs = utilisateurs.filter(user => user.id !== id);
renderTable(utilisateurs);
}
}

// Fonction pour trier les utilisateurs par nom
function sortUsersByName() {
utilisateurs.sort((a, b) => {
const nameA = a.nom.toUpperCase();
const nameB = b.nom.toUpperCase();

if (sortDirection === 'asc') {
return nameA < nameB ? -1 : (nameA > nameB ? 1 : 0);
} else {
return nameA > nameB ? -1 : (nameA < nameB ? 1 : 0);
}
});

sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
sortButton.textContent = `Trier par nom (${sortDirection === 'asc'
? 'A-Z' : 'Z-A'})`;
renderTable(utilisateurs);
}

// Fonction pour filtrer les utilisateurs
function filterUsers(query) {
if (!query) {
renderTable(utilisateurs);
return;
}

const lowercaseQuery = query.toLowerCase();
const filteredUsers = utilisateurs.filter(user =>
user.nom.toLowerCase().includes(lowercaseQuery) ||
user.email.toLowerCase().includes(lowercaseQuery)
);

renderTable(filteredUsers);
}

// Gestionnaires d'événements

// Afficher le formulaire d'ajout
addButton.addEventListener('click', function() {
isEditing = false;
document.getElementById('formTitle').textContent = 'Ajouter un
utilisateur';

Y. EL ALLIOUI – [email protected] 44 / 68
2025
document.getElementById('userId').value = '';
document.getElementById('userName').value = '';
document.getElementById('userEmail').value = '';
formContainer.style.display = 'block';
});

// Cacher le formulaire
cancelButton.addEventListener('click', function() {
formContainer.style.display = 'none';
});

// Gérer la soumission du formulaire
userForm.addEventListener('submit', function(e) {
e.preventDefault();

const userId = document.getElementById('userId').value;
const nom = document.getElementById('userName').value.trim();
const email = document.getElementById('userEmail').value.trim();

if (nom && email) {
if (isEditing) {
updateUser(userId, nom, email);
} else {
addUser(nom, email);
}

formContainer.style.display = 'none';
}
});

// Trier les utilisateurs
sortButton.addEventListener('click', sortUsersByName);
nameHeader.addEventListener('click', sortUsersByName);

// Rechercher
searchInput.addEventListener('input', function() {
filterUsers(this.value);
});

// Afficher les utilisateurs initiaux
renderTable(utilisateurs);
});
Explication du code
1. Structure HTML :
o Un tableau avec des en-têtes pour ID, Nom, Email et Actions
o Un champ de recherche pour filtrer les utilisateurs
o Des boutons pour ajouter un utilisateur et trier la liste
o Un formulaire (initialement caché) pour ajouter ou modifier des utilisateurs
o Des styles CSS pour l'apparence du tableau et des boutons
2. Fonctionnalités JavaScript :

Y. EL ALLIOUI – [email protected] 45 / 68
2025
o Données initiales avec trois utilisateurs
o Une fonction renderTable pour afficher les utilisateurs dans le tableau
o Des fonctions CRUD pour les utilisateurs :
§ addUser : ajoute un nouvel utilisateur avec un ID auto-incrémenté
§ editUser : prépare le formulaire pour l'édition d'un utilisateur existant
§ updateUser : met à jour les données d'un utilisateur après édition
§ deleteUser : supprime un utilisateur après confirmation
o Fonctions de tri et de filtrage :
§ sortUsersByName : trie les utilisateurs par nom (alternance
ascendant/descendant)
§ filterUsers : filtre les utilisateurs selon le texte de recherche
o Gestionnaires d'événements pour :
§ Afficher/masquer le formulaire
§ Soumettre le formulaire (ajout ou mise à jour)
§ Trier la liste d'utilisateurs
§ Filtrer en temps réel selon le texte saisi dans le champ de recherche

Exercice 10 : Système de navigation par onglets
Objectif : Créer un système de navigation par onglets qui permet d'afficher différents
contenus sans recharger la page.
Consignes :
1. Créer une structure HTML avec :
o Une barre d'onglets (au moins 3 onglets)
o Des sections de contenu correspondant à chaque onglet
2. Implémenter les fonctionnalités suivantes en JavaScript :
o Au chargement de la page, seul le contenu du premier onglet est visible
o Cliquer sur un onglet affiche le contenu correspondant et masque les autres
o Mettre en évidence l'onglet actif (changer sa classe CSS)
o Permettre de passer d'un onglet à l'autre avec les touches de clavier (flèches
gauche/droite)
o Ajouter une animation simple lors du changement d'onglet
Compétences testées : Gestion des événements, manipulation de classes CSS, modifications
dynamiques de l'affichage, navigation au clavier, délégation d'événements.
Correction :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device -width, initial-scale=1.0">
<title>Système de navigation par onglets</title>
<style>

Y. EL ALLIOUI – [email protected] 46 / 68
2025
/* Style général */
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}

/* Style des onglets */
.tabs {
display: flex;
list-style: none;
padding: 0;
margin: 0;
border-bottom: 2px solid #ddd;
}

.tab {
padding: 10px 20px;
margin-right: 5px;
cursor: pointer;
background-color: #f1f1f1;
border-radius: 5px 5px 0 0;
border: 1px solid #ddd;
border-bottom: none;
}

.tab.active {
background-color: #4CAF50;
color: white;
border-color: #4CAF50;
}

/* Style des contenus */
.tab-content {
border: 1px solid #ddd;
border-top: none;
padding: 20px;
display: none;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}

.tab-content.active {
display: block;
opacity: 1;
}

/* Instructions */
.instructions {

Y. EL ALLIOUI – [email protected] 47 / 68
2025
background-color: #f8f9fa;
padding: 15px;
margin-top: 20px;
border-radius: 4px;
border-left: 4px solid #4CAF50;
}
</style>
</head>
<body>
<h1>Système de navigation par onglets</h1>

<!-- Barre d'onglets -->
<ul class="tabs" id="tabsNavigation">
<li class="tab active" data-tab="tab1">Onglet 1</li>
<li class="tab" data-tab="tab2">Onglet 2</li>
<li class="tab" data-tab="tab3">Onglet 3</li>
</ul>

<!-- Sections de contenu -->
<div class="tab-content active" id="tab1">
<h2>Contenu de l'onglet 1</h2>
<p>Ceci est le contenu du premier onglet. Il est visible par défaut
au chargement de la page.</p>
</div>

<div class="tab-content" id="tab2">
<h2>Contenu de l'onglet 2</h2>
<p>Voici le contenu du deuxième onglet qui apparaît quand on clique
sur "Onglet 2".</p>
</div>

<div class="tab-content" id="tab3">
<h2>Contenu de l'onglet 3</h2>
<p>Et enfin le contenu du troisième onglet, visible uniquement
quand on clique sur "Onglet 3".</p>
</div>

<div class="instructions">
<h3>Instructions :</h3>
<p>Vous pouvez naviguer entre les onglets de plusieurs façons :</p>
<ul>
<li>En cliquant sur les onglets</li>
<li>En utilisant les touches flèches gauche et droite du
clavier</li>
</ul>
</div>

<script>
// Attendre que le DOM soit complètement chargé
document.addEventListener('DOMContentLoaded', function() {

Y. EL ALLIOUI – [email protected] 48 / 68
2025
// Sélection des éléments du DOM
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab -content');

// Fonction pour activer un onglet spécifique
function activateTab(tabId) {
// Désactiver tous les onglets et contenus
tabs.forEach(tab => tab.classList.remove('active'));
tabContents.forEach(content =>
content.classList.remove('active'));

// Activer l'onglet et le contenu correspondant
const selectedTab = document.querySelector(`.tab[data -
tab="${tabId}"]`);
const selectedContent = document.getElementById(tabId);

if (selectedTab && selectedContent) {
selectedTab.classList.add('active');
selectedContent.classList.add('active');
}
}

// Ajouter un gestionnaire d'événements pour chaque onglet
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const tabId = this.getAttribute('data -tab');
activateTab(tabId);
});
});

// Gestion de la navigation au clavier
document.addEventListener('keydown', function(event) {
// Récupérer l'index de l'onglet actif
const activeTabIndex = Array.from(tabs).findIndex(tab =>
tab.classList.contains('active')
);

if (event.key === 'ArrowRight') {
// Passer à l'onglet suivant (ou revenir au premier)
const nextIndex = (activeTabIndex + 1) % tabs.length;
const nextTabId = tabs[nextIndex].getAttribute('data -
tab');
activateTab(nextTabId);
} else if (event.key === 'ArrowLeft') {
// Passer à l'onglet précédent (ou aller au dernier)
const prevIndex = (activeTabIndex - 1 + tabs.length) %
tabs.length;
const prevTabId = tabs[prevIndex].getAttribute('data -
tab');
activateTab(prevTabId);

Y. EL ALLIOUI – [email protected] 49 / 68
2025
}
});
});
</script>
</body>
</html>
Explications détaillées
1. Structure HTML
o La barre d'onglets est structurée sous forme d'une liste <ul> avec la classe
"tabs"
o Chaque onglet est un élément <li> avec un attribut data-tab qui sert à
l'identifier
o Les sections de contenu sont des <div> avec l'ID correspondant aux
attributs data-tab
2. CSS
o Les onglets sont stylisés pour ressembler à de vrais onglets avec des bordures
et des coins arrondis
o La classe .active change l'apparence de l'onglet sélectionné
o L'animation est réalisée avec une transition CSS sur l'opacité
o Les contenus sont initialement cachés avec display: none et opacity: 0
3. JavaScript
o La fonction activateTab(tabId) est au cœur du système de navigation:
§ Elle commence par désactiver tous les onglets et contenus
§ Puis active l'onglet et le contenu correspondant au tabId
o Un gestionnaire d'événements est ajouté à chaque onglet pour détecter les clics
o La navigation au clavier est implémentée avec keydown:
§ Flèche droite: passe à l'onglet suivant (ou revient au premier)
§ Flèche gauche: passe à l'onglet précédent (ou va au dernier)
o L'utilisation de Array.from(tabs).findIndex() permet de déterminer
l'index de l'onglet actif
o L'opération % tabs.length assure une navigation circulaire entre les onglets
4. Points pédagogiques importants
o Le code utilise des sélecteurs et des attributs data-* pour lier les onglets et
leurs contenus
o La séparation des responsabilités est claire: HTML pour la structure, CSS pour
l'apparence, JS pour le comportement
o L'animation simple est réalisée uniquement avec CSS pour de meilleures
performances

Exercice 11 : Éditeur de texte enrichi simple
Objectif : Créer un éditeur de texte minimal avec des contrôles de formatage.
Consignes :

Y. EL ALLIOUI – [email protected] 50 / 68
2025
1. Créer une interface HTML avec :
o Une barre d'outils avec des boutons pour les fonctions : gras, italique, souligné,
liste à puces, liste numérotée, alignement (gauche, centre, droite)
o Une zone d'édition (conteneur div avec attribut contenteditable)
o Un bouton pour afficher le code HTML généré
2. Implémenter les fonctionnalités suivantes en JavaScript :
o Utiliser les commandes document.execCommand() pour appliquer le
formatage au texte sélectionné
o Mettre en évidence les boutons actifs (par exemple, le bouton "gras" est en
surbrillance lorsque le texte sélectionné est en gras)
o Permettre l'utilisation de raccourcis clavier (Ctrl+B pour gras, Ctrl+I pour
italique, etc.)
o Ajouter un bouton qui affiche dans une fenêtre modale le code HTML généré
par l'éditeur
Compétences testées : Manipulation avancée du DOM, gestion des sélections de texte,
utilisation de document.execCommand(), détection des états de formatage, gestion des
raccourcis clavier.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device -width, initial-scale=1.0">
<title>Éditeur de texte enrichi simple</title>
<style>
/* Style général */
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}

/* Style de la barre d'outils */
.toolbar {
background-color: #f1f1f1;
padding: 10px;
border-radius: 4px 4px 0 0;
display: flex;
flex-wrap: wrap;
gap: 5px;
border: 1px solid #ddd;
border-bottom: none;
}

.toolbar button {
padding: 6px 12px;
background-color: white;
border: 1px solid #ddd;

Y. EL ALLIOUI – [email protected] 51 / 68
2025
border-radius: 3px;
cursor: pointer;
font-size: 14px;
}

.toolbar button:hover {
background-color: #e9e9e9;
}

.toolbar button.active {
background-color: #4CAF50;
color: white;
border-color: #4CAF50;
}

/* Style de la zone d'édition */
.editor {
border: 1px solid #ddd;
min-height: 200px;
padding: 15px;
overflow-y: auto;
background-color: white;
}

/* Style pour le bouton d'affichage HTML */
.view-html-btn {
margin-top: 15px;
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

/* Style pour la fenêtre modale */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 100;
}

.modal-content {
position: absolute;
top: 50%;

Y. EL ALLIOUI – [email protected] 52 / 68
2025
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 20px;
border-radius: 5px;
width: 80%;
max-width: 700px;
max-height: 80%;
overflow-y: auto;
}

.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}

.close-modal {
font-size: 24px;
cursor: pointer;
}

.html-code {
width: 100%;
min-height: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
white-space: pre-wrap;
background-color: #f8f9fa;
}

/* Style pour la séparation des groupes d'outils */
.toolbar-group {
display: flex;
gap: 5px;
margin-right: 10px;
padding-right: 10px;
border-right: 1px solid #ddd;
}

.toolbar-group:last-child {
border-right: none;
}

/* Instructions */
.instructions {
background-color: #f8f9fa;

Y. EL ALLIOUI – [email protected] 53 / 68
2025
padding: 15px;
margin-top: 20px;
border-radius: 4px;
border-left: 4px solid #4CAF50;
}
</style>
</head>
<body>
<h1>Éditeur de texte enrichi simple</h1>

<!-- Barre d'outils -->
<div class="toolbar">
<div class="toolbar-group">
<button id="bold" title="Gras
(Ctrl+B)"><strong>G</strong></button>
<button id="italic" title="Italique
(Ctrl+I)"><em>I</em></button>
<button id="underline" title="Souligné
(Ctrl+U)"><u>S</u></button>
</div>
<div class="toolbar-group">
<button id="insertUnorderedList" title="Liste à puces">•
Liste</button>
<button id="insertOrderedList" title="Liste numérotée">1.
Liste</button>
</div>
<div class="toolbar-group">
<button id="justifyLeft" title="Aligner à
gauche">Gauche</button>
<button id="justifyCenter" title="Centrer">Centre</button>
<button id="justifyRight" title="Aligner à
droite">Droite</button>
</div>
</div>

<!-- Zone d'édition -->
<div class="editor" id="editor" contenteditable="true">
<p>Bienvenue dans l'éditeur de texte enrichi ! Vous pouvez
commencer à écrire ici...</p>
<p>Sélectionnez du texte et utilisez les boutons de la barre
d'outils pour le formater.</p>
</div>

<!-- Bouton pour afficher le code HTML -->
<button class="view-html-btn" id="viewHtmlBtn">Afficher le code
HTML</button>

<!-- Fenêtre modale pour afficher le code HTML -->
<div class="modal" id="htmlModal">
<div class="modal-content">

Y. EL ALLIOUI – [email protected] 54 / 68
2025
<div class="modal-header">
<h2>Code HTML généré</h2>
<span class="close-modal" id="closeModal">&times;</span>
</div>
<pre class="html-code" id="htmlCode"></pre>
</div>
</div>

<div class="instructions">
<h3>Instructions et raccourcis clavier :</h3>
<ul>
<li><strong>Ctrl+B</strong> : Mettre en gras</li>
<li><strong>Ctrl+I</strong> : Mettre en italique</li>
<li><strong>Ctrl+U</strong> : Souligner</li>
<li>Utilisez les boutons de la barre d'outils pour formater
votre texte</li>
<li>Cliquez sur "Afficher le code HTML" pour voir le code
généré</li>
</ul>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
// Sélection des éléments du DOM
const editor = document.getElementById('editor');
const toolbarButtons = document.querySelectorAll('.toolbar
button');
const viewHtmlBtn = document.getElementById('viewHtmlBtn');
const htmlModal = document.getElementById('htmlModal');
const htmlCode = document.getElementById('htmlCode');
const closeModal = document.getElementById('closeModal');

// Fonction pour exécuter les commandes d'édition
function execCommand(command) {
document.execCommand(command, false, null);
updateActiveButtons();
// Donner le focus à l'éditeur après l'exécution de la
commande
editor.focus();
}

// Ajouter des gestionnaires d'événements pour les boutons de
la barre d'outils
toolbarButtons.forEach(button => {
button.addEventListener('click', function() {
const command = this.id;
execCommand(command);
});
});

Y. EL ALLIOUI – [email protected] 55 / 68
2025
// Mettre à jour les boutons actifs en fonction de l'état de
formatage actuel
function updateActiveButtons() {
toolbarButtons.forEach(button => {
const command = button.id;
if (document.queryCommandState(command)) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
});
}

// Mettre à jour les boutons actifs lorsque la sélection change
editor.addEventListener('mouseup', updateActiveButtons);
editor.addEventListener('keyup', updateActiveButtons);

// Gestionnaire pour les raccourcis clavier
editor.addEventListener('keydown', function(event) {
// Vérifier si Ctrl est enfoncé
if (event.ctrlKey) {
// Gras: Ctrl+B
if (event.key === 'b') {
event.preventDefault();
execCommand('bold');
}
// Italique: Ctrl+I
else if (event.key === 'i') {
event.preventDefault();
execCommand('italic');
}
// Souligné: Ctrl+U
else if (event.key === 'u') {
event.preventDefault();
execCommand('underline');
}
}
});

// Afficher le code HTML dans la fenêtre modale
viewHtmlBtn.addEventListener('click', function() {
// Échapper les caractères HTML pour l'affichage
const escapedHtml = editor.innerHTML
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");

Y. EL ALLIOUI – [email protected] 56 / 68
2025
htmlCode.textContent = editor.innerHTML; // Le texte brut
préserve les sauts de ligne
htmlModal.style.display = 'block';
});

// Fermer la fenêtre modale
closeModal.addEventListener('click', function() {
htmlModal.style.display = 'none';
});

// Fermer la fenêtre modale en cliquant en dehors
window.addEventListener('click', function(event) {
if (event.target === htmlModal) {
htmlModal.style.display = 'none';
}
});

// Initialiser l'état des boutons au chargement
updateActiveButtons();

// Donner le focus à l'éditeur au chargement
editor.focus();
});
</script>
</body>
</html>
Explications détaillées
1. Structure HTML
o La barre d'outils contient des boutons regroupés par fonctionnalité
o La zone d'édition est un <div> avec l'attribut contenteditable="true"
o Une fenêtre modale est incluse pour afficher le code HTML généré
2. CSS
o La barre d'outils est stylisée avec un fond gris clair
o Les boutons actifs sont mis en évidence avec une couleur verte
o La modal utilise un positionnement fixe et un fond semi-transparent
o Les groupes de boutons sont séparés par des bordures verticales
3. JavaScript
o La fonction execCommand() est utilisée pour appliquer les commandes de
formatage
o document.queryCommandState() permet de vérifier si un formatage est
appliqué
o La fonction updateActiveButtons() met à jour l'apparence des boutons
selon l'état de formatage
o Les raccourcis clavier sont détectés avec keydown et l'attribut event.ctrlKey
o L'affichage du code HTML utilise une fenêtre modale
4. Points pédagogiques importants
o L'utilisation de document.execCommand() simplifie considérablement
l'implémentation de l'éditeur

Y. EL ALLIOUI – [email protected] 57 / 68
2025
o Les raccourcis clavier sont essentiels pour une expérience utilisateur fluide
o La mise à jour de l'état des boutons en fonction du formatage actuel offre un
feedback visuel
o L'échappement des caractères HTML dans la fenêtre modale est important
pour l'affichage correct

Exercice 12 : Système de glisser-déposer (drag and drop)
Objectif : Créer une interface permettant de déplacer des éléments par glisser-déposer.
Consignes :
1. Créer une interface HTML avec :
o Une zone contenant plusieurs éléments déplaçables (cartes, blocs, etc.)
o Une ou plusieurs zones de destination où déposer ces éléments
o Un compteur indiquant le nombre d'éléments dans chaque zone
2. Implémenter les fonctionnalités suivantes en JavaScript :
o Permettre de déplacer les éléments d'une zone à l'autre par glisser-déposer
o Afficher un indicateur visuel pendant le déplacement (par exemple, changer la
couleur de la zone de destination)
o Mettre à jour le compteur d'éléments dans chaque zone après chaque
déplacement
o Ajouter la possibilité de trier les éléments dans une même zone par glisser-
déposer
o Permettre de dupliquer un élément en maintenant une touche spécifique
enfoncée (par exemple, Ctrl ou Alt) pendant le glisser-déposer
Bonus :
• Implémenter une fonctionnalité pour sauvegarder la configuration actuelle (dans
localStorage)
• Ajouter un bouton pour réinitialiser la disposition initiale
Compétences testées : Utilisation de l'API Drag and Drop, gestion avancée des événements,
modification dynamique du DOM, manipulation des styles CSS, stockage local, gestion des
modificateurs de clavier.
Correction :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device -width, initial-scale=1.0">
<title>Système de Glisser-Déposer</title>
<style>
body {

Y. EL ALLIOUI – [email protected] 58 / 68
2025
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans -serif;
margin: 20px;
background-color: #f5f5f5;
}

h1 {
color: #2c3e50;
text-align: center;
}

.container {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin-bottom: 20px;
}

.zone {
min-width: 250px;
min-height: 300px;
border: 2px solid #3498db;
border-radius: 8px;
padding: 10px;
margin: 10px;
background-color: #ecf0f1;
flex-grow: 1;
}

.zone-title {
font-weight: bold;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 5px;
border-bottom: 1px solid #bdc3c7;
}

.counter {
background-color: #3498db;
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 14px;
}

.item {
padding: 10px;
margin: 8px 0;
background-color: white;

Y. EL ALLIOUI – [email protected] 59 / 68
2025
border: 1px solid #bdc3c7;
border-radius: 4px;
cursor: move;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.item:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
transform: translateY(-2px);
}

.zone.drag-over {
background-color: #d6eaf8;
border-color: #2980b9;
}

.item.dragging {
opacity: 0.5;
}

.buttons {
display: flex;
justify-content: center;
margin: 20px 0;
}

button {
padding: 10px 15px;
margin: 0 5px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}

button:hover {
background-color: #2980b9;
}

.instructions {
max-width: 800px;
margin: 0 auto 30px;
padding: 15px;
background-color: #d6eaf8;
border-radius: 8px;
}
</style>

Y. EL ALLIOUI – [email protected] 60 / 68
2025
</head>
<body>
<h1>Système de Glisser-Déposer</h1>

<div class="instructions">
<p><strong>Instructions:</strong></p>
<ul>
<li>Glissez et déposez les éléments d'une zone à l'autre</li>
<li>Maintenez la touche <kbd>Ctrl</kbd> (ou <kbd>Cmd</kbd> sur
Mac) pendant le glisser-déposer pour dupliquer l'élément</li>
<li>Vous pouvez réorganiser les éléments au sein d'une même
zone</li>
<li>Utilisez les boutons ci-dessous pour sauvegarder ou
réinitialiser la configuration</li>
</ul>
</div>

<div class="container">
<div class="zone" id="zone1">
<div class="zone-title">
Zone 1 <span class="counter" id="counter1">0</span>
</div>
<div class="item" draggable="true">Élément 1</div>
<div class="item" draggable="true">Élément 2</div>
<div class="item" draggable="true">Élément 3</div>
<div class="item" draggable="true">Élément 4</div>
</div>

<div class="zone" id="zone2">
<div class="zone-title">
Zone 2 <span class="counter" id="counter2">0</span>
</div>
<div class="item" draggable="true">Élément 5</div>
<div class="item" draggable="true">Élément 6</div>
</div>

<div class="zone" id="zone3">
<div class="zone-title">
Zone 3 <span class="counter" id="counter3">0</span>
</div>
</div>
</div>

<div class="buttons">
<button id="saveBtn">Sauvegarder la configuration</button>
<button id="resetBtn">Réinitialiser</button>
</div>

<script>
// Variables globales

Y. EL ALLIOUI – [email protected] 61 / 68
2025
let draggedItem = null; // Élément en cours de déplacement
let isDuplicating = false; // Si l'utilisateur est en train de
dupliquer (touche Ctrl/Cmd enfoncée)
const initialState = {}; // Pour stocker l'état initial

// Sélection des éléments DOM
const zones = document.querySelectorAll('.zone');
const saveBtn = document.getElementById('saveBtn');
const resetBtn = document.getElementById('resetBtn');

// Fonction d'initialisation
function initialize() {
// Stockage de l'état initial des zones
saveInitialState();

// Mise en place des événements pour les éléments déplaçables
setupDraggableItems();

// Mise en place des événements pour les zones de dépôt
setupDropZones();

// Configuration des boutons
setupButtons();

// Mise à jour initiale des compteurs
updateAllCounters();

// Tentative de restauration depuis localStorage si disponible
restoreFromLocalStorage();
}

// Sauvegarde de l'état initial des zones pour la réinitialisation
function saveInitialState() {
zones.forEach(zone => {
const id = zone.id;
initialState[id] = zone.innerHTML;
});
}

// Configuration des éléments déplaçables
function setupDraggableItems() {
// Sélection de tous les éléments déplaçables
const items = document.querySelectorAll('.item');

items.forEach(item => {
// Événement déclenché au début du glisser
item.addEventListener('dragstart', function(e) {
draggedItem = this;

Y. EL ALLIOUI – [email protected] 62 / 68
2025
// Vérification si la touche Ctrl (ou Cmd sur Mac) est
enfoncée pour duplication
isDuplicating = e.ctrlKey || e.metaKey;

// Ajouter une classe pour l'effet visuel
setTimeout(() => {
this.classList.add('dragging');
}, 0);
});

// Événement déclenché à la fin du glisser
item.addEventListener('dragend', function() {
draggedItem = null;
this.classList.remove('dragging');
isDuplicating = false;
});
});
}

// Configuration des zones de dépôt
function setupDropZones() {
zones.forEach(zone => {
// Empêcher le comportement par défaut pour permettre le
drop
zone.addEventListener('dragover', function(e) {
e.preventDefault();
});

// Événement quand on entre dans une zone de dépôt
zone.addEventListener('dragenter', function(e) {
e.preventDefault();
this.classList.add('drag-over');
});

// Événement quand on quitte une zone de dépôt
zone.addEventListener('dragleave', function() {
this.classList.remove('drag-over');
});

// Événement quand on dépose l'élément
zone.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('drag-over');

if (!draggedItem) return;

// Position à l'intérieur de la zone (pour le tri dans
une même zone)
const afterElement = getDragAfterElement(zone,
e.clientY);

Y. EL ALLIOUI – [email protected] 63 / 68
2025

if (isDuplicating) {
// Duplication de l'élément
const duplicatedItem = draggedItem.cloneNode(true);
setupDraggableEventListeners(duplicatedItem);

if (afterElement) {
zone.insertBefore(duplicatedItem,
afterElement);
} else {
// Insérer à la fin de la zone (après le titre)
const zoneTitle = zone.querySelector('.zone -
title');
if (zoneTitle.nextElementSibling) {
zone.insertBefore(duplicatedItem,
zoneTitle.nextElementSibling);
} else {
zone.appendChild(duplicatedItem);
}
}
} else {
// Déplacement de l'élément
if (afterElement) {
zone.insertBefore(draggedItem, afterElement);
} else {
// Insérer à la fin de la zone
zone.appendChild(draggedItem);
}
}

// Mise à jour des compteurs
updateAllCounters();
});
});
}

// Déterminer où insérer l'élément dans la zone (pour le tri)
function getDragAfterElement(zone, y) {
// Récupérer tous les éléments déplaçables dans la zone, sauf
celui en cours de déplacement
const draggableElements =
[...zone.querySelectorAll('.item:not(.dragging)')];

// Trouver l'élément le plus proche en fonction de la position
Y de la souris
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2; // Distance
au milieu de l'élément

Y. EL ALLIOUI – [email protected] 64 / 68
2025
// Si l'offset est négatif (au -dessus de l'élément) mais
plus grand que l'élément le plus proche,
// alors cet élément devient le plus proche
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}

// Configuration d'événements pour un élément déplaçable
function setupDraggableEventListeners(item) {
item.addEventListener('dragstart', function(e) {
draggedItem = this;
isDuplicating = e.ctrlKey || e.metaKey;
setTimeout(() => {
this.classList.add('dragging');
}, 0);
});

item.addEventListener('dragend', function() {
draggedItem = null;
this.classList.remove('dragging');
isDuplicating = false;
});
}

// Mise à jour de tous les compteurs
function updateAllCounters() {
zones.forEach(zone => {
const id = zone.id;
const counter =
document.getElementById(`counter${id.slice( -1)}`);
const itemCount = zone.querySelectorAll('.item').length;
counter.textContent = itemCount;
});
}

// Configuration des boutons
function setupButtons() {
// Bouton de sauvegarde
saveBtn.addEventListener('click', function() {
saveToLocalStorage();
alert('Configuration sauvegardée !');
});

// Bouton de réinitialisation
resetBtn.addEventListener('click', function() {
resetToInitialState();

Y. EL ALLIOUI – [email protected] 65 / 68
2025
alert('Configuration réinitialisée !');
});
}

// Sauvegarde dans le localStorage
function saveToLocalStorage() {
const state = {};

zones.forEach(zone => {
const id = zone.id;
state[id] = {
html: zone.innerHTML,
itemCount: zone.querySelectorAll('.item').length
};
});

localStorage.setItem('dragDropState', JSON.stringify(state));
}

// Restauration depuis le localStorage
function restoreFromLocalStorage() {
const savedState = localStorage.getItem('dragDropState');

if (savedState) {
const state = JSON.parse(savedState);

zones.forEach(zone => {
const id = zone.id;
if (state[id]) {
zone.innerHTML = state[id].html;
}
});

// Reconfigurer les événements pour les éléments restaurés
setupDraggableItems();

// Mettre à jour les compteurs
updateAllCounters();
}
}

// Réinitialisation à l'état initial
function resetToInitialState() {
zones.forEach(zone => {
const id = zone.id;
zone.innerHTML = initialState[id];
});

// Reconfigurer les événements pour les éléments réinitialisés
setupDraggableItems();

Y. EL ALLIOUI – [email protected] 66 / 68
2025

// Mettre à jour les compteurs
updateAllCounters();
}

// Démarrer l'application
initialize();
</script>
</body>
</html>
Explications détaillées du code
Structure HTML
Le code HTML crée trois zones distinctes où les éléments peuvent être glissés-déposés:
• Zone 1: Contient initialement 4 éléments (Élément 1 à 4)
• Zone 2: Contient initialement 2 éléments (Élément 5 et 6)
• Zone 3: Vide au départ
Chaque zone possède un compteur qui affiche le nombre d'éléments qu'elle contient.
CSS
Les styles CSS définissent l'apparence visuelle:
• Les zones sont représentées avec des bordures bleues et un fond clair
• Les éléments déplaçables ont un style avec ombre et effet de survol
• Des effets visuels sont ajoutés pour indiquer le glisser-déposer en cours:
o .drag-over: Style appliqué à la zone de destination
o .dragging: Style appliqué à l'élément en cours de déplacement
JavaScript
Le code JavaScript est organisé en plusieurs fonctions pour une meilleure lisibilité:
1. Fonctions d'initialisation
• initialize(): Point d'entrée principal qui configure l'application
• saveInitialState(): Sauvegarde l'état initial pour la réinitialisation ultérieure
2. Gestion du glisser-déposer
• setupDraggableItems(): Configure les événements pour les éléments déplaçables
o dragstart: Déclenché quand on commence à glisser un élément
o dragend: Déclenché quand on termine le glissage
• setupDropZones(): Configure les événements pour les zones de dépôt
o dragover: Permet le dépôt dans la zone (annule le comportement par défaut)

Y. EL ALLIOUI – [email protected] 67 / 68
2025
o dragenter: Ajoute des effets visuels quand un élément entre dans la zone
o dragleave: Retire les effets visuels quand un élément quitte la zone
o drop: Gère le dépôt d'un élément dans la zone
3. Fonctionnalités principales
• Déplacement d'éléments: Les éléments peuvent être déplacés entre les zones
• Tri dans une même zone: La fonction getDragAfterElement() permet de
réorganiser les éléments dans une même zone
• Duplication d'éléments: Maintenir la touche Ctrl/Cmd pendant le glisser-déposer
duplique l'élément
• Compteurs: La fonction updateAllCounters() met à jour le nombre d'éléments
dans chaque zone
4. Sauvegarde et restauration
• saveToLocalStorage(): Sauvegarde la configuration actuelle dans le localStorage du
navigateur
• restoreFromLocalStorage() : Restaure la configuration depuis le localStorage
• resetToInitialState(): Réinitialise l'application à son état de départ
Points techniques importants
1. API Drag and Drop: Le code utilise l'API native de HTML5 pour le glisser-déposer,
sans dépendance externe.
2. Modification dynamique du DOM: Les éléments sont ajoutés, déplacés et supprimés
dynamiquement.
3. Détection de position: La fonction getDragAfterElement() calcule où insérer un
élément en fonction de la position du curseur.
4. Gestion des modificateurs de clavier: Le code détecte si Ctrl/Cmd est enfoncé pour
la duplication avec e.ctrlKey || e.metaKey.
5. Stockage local: Le localStorage est utilisé pour sauvegarder la configuration.
Conseils pédagogiques
• Expérimentez avec les différentes fonctionnalités: déplacement, tri, duplication et
réinitialisation
• Observez comment les compteurs se mettent à jour automatiquement
• Essayez de comprendre le fonctionnement de la
fonction getDragAfterElement() qui permet le tri précis
• Examinez comment la duplication fonctionne différemment du déplacement
• Testez la sauvegarde et la restauration en rechargeant la page après sauvegarde
Ce code remplit toutes les exigences de l'exercice, y compris les fonctionnalités bonus. Il est
organisé de manière modulaire pour faciliter la compréhension et la maintenance.

Y. EL ALLIOUI – [email protected] 68 / 68
2025