LIFWEB - TP2 - Premiers pas dans le DOM
L’environnement de développement standard pour cette UE est un OS Linux, l’éditeur VSCode, Node.js 18.x ou supérieur, et le navigateur Firefox.
Pour ce deuxième TP, on utilisera un éditeur, un terminal, et Firefox.
Comme pour les autres TPs, s’appuyer sur les documentations de référence : https://javascript.info/ et https://developer.mozilla.org/en-US/docs/Web/JavaScript.
Il est recommandé d’utiliser l’extension https://ritwickdey.github.io/vscode-live-server/ (VSCode, Open-VSX) et de choisir qu’elle ouvre la page dans un navigateur en navigation privée (option liveServer.settings.CustomBrowser
).
Important : Quand des liens vers des fichiers sont fournis dans le TP, enregistrez les sur votre compte en faisant clic-droit Save Link As… / Enregistrer sous….
On donne les fichiers de départs suivants :
- tp2.html
- tp2.js, chargé par le précédent, à compléter
- tp2_demo_1.js, juste pour l’exemple de chargement
- tp2_demo_2.js, juste pour l’exemple de chargement
- tp2.css
Quand on indique dynamiquement, on entend en JS en manipulant le DOM, pas en éditant les sources des HTML ou CSS.
Exercice 0 (PRÉPARATION) : Chargement de JS
L’objectif de cet exercice est de comprendre comment peuvent être chargés des scripts JavaScript et comment ces derniers sont ensuite interprétés par le navigateur.
- Identifier où sont chargés les différents scripts JavaScript.
- Expliquer l’ordre des affichages dans la console.
- Conclure sur les longueurs des
NodeList
affichées. - Avec
console.log()
afficher le nombre de<li>
de la page.
Exercice 1 : DOM et Événements
- Ajouter un handler aux boutons + et - pour qu’ils modifient la valeur du compteur, et modifie l’élément span (
id=counter
).- Pour ce TP, on se satisfera pour l’instant d’une variable
count
globale. - Utiliser
.textContent
pour modifier l’affichage du conteur.
- Pour ce TP, on se satisfera pour l’instant d’une variable
- Ajouter dynamiquement à
document.body
un bouton avec le texte reset. - Ajouter un handler au bouton reset qui aura pour effet de remettre à zéro le compteur précédent.
Exercice 2 : Validation d’input et manipulation de CSS
Modifier le champ d’input
pour rajouter un type email
, une taille de 30 caractères, et indiquer que le champs est obligatoire (dans le html).
Dans le fichier javascript, ajouter deux eventListener :
- l’un écoutant si le bouton Valider a été cliqué : on lancera alors une vérification et le champs deviendra rouge si le format ne respecte pas celui d’un email, et vert s’il est correct.
- l’autre écoutant toute modification du champ d’
input
: on lancera une vérification, et on affichera alors un message d’erreur dans le paragraphe (errorMessage
). Utiliser la même technique que dans l’exercice 1.
Pour tester le format email vous pouvez utiliser le code suivant :
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
let emailCorrectementFormatte = emailRegex.test(TODO_Chaine_à_tester);
Pour changer la couleur, il faut modifier la propriété CSS background-color
du champ input, plusieurs stratégies sont possible soit via des classes CSS dédiées, soit en modifiant directement la couleur de fond de l’élément: monElement.style.backgroundColor=macouleur;
Exercice 3 : Affichage dynamique d’images
L’élément <ul id="images-list">
contient des liens vers des images.
- Modifier la page pour qu’à chaque clic sur un lien, l’image correspondante soit affichée dans l’élément
<div id="image-container">
.- Il faut bloquer le comportement par défaut des liens pour éviter d’être redirigé.
- Définir des classes CSS pour contrôler l’affichage de l’image afin qu’elle soit de taille 80% de la largeur d’affichage et centrée dans le
<div>
.- Utiliser les Flexbox pour le positionnement
- Cela peut se faire en statique via la CSS (en donnant si besoin les classes via JS)
Exercice 4 : Gestion d’événements clavier
L’exercice consiste à gérer le déplacement d’un joueur sur une grille. Créer un eventListener global sur toute page (document
) qui écoute le clavier, modifie la position du joueur (playerPosition
) et raffraichit la grille updatePlayerPosition()
.
Le code JavaScript suivant crée dynamiquement un damier de 8x8 cases.
const $grid = document.querySelector("#game-grid");
// Create 8x8 grid
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
const cell = document.createElement("div");
cell.classList.add("grid-cell");
cell.dataset.x = x;
cell.dataset.y = y;
$grid.appendChild(cell);
}
}
Le code suivant définit la position initiale du joueur, et (re)dessine la grille.
let playerPosition = { x: 4, y: 4 };
function updatePlayerPosition() {
// Remove previous player cell
document.querySelector(".player")?.classList.remove("player");
// Add player to new cell
const cell = document.querySelector(
`.grid-cell[data-x="${playerPosition.x}"][data-y="${playerPosition.y}"]`,
);
cell.classList.add("player");
}
Exercice 5 : Gestion d’événements souris
On donne la définition de la classe dot
suivante qui permet d’afficher un gros point. On va utiliser cette classe pour traquer le curseur de la souris avec ce point.
.dot {
pointer-events: none;
height: 50px;
width: 50px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
position: absolute;
}
- Ajouter dynamiquement (en JavaScript) ou statiquement (dans le HTML) un
div
avec la classedot
. - Creer une fonction
followMouse()
qui aura le comportement suivant.- Elle garde une variable locale
mousePos
qui va stocker la position courante de la souris. - Elle associe à l’événement
mousemove
un handler qui met à jourmousePos
. - Elle déclenche toutes les 50ms avec
setInterval(handler, delay)
(MDN) une fonction qui litmousePos
et positionne le point sous le curseur en modifiant les attributs CSSleft
ettop
dudiv
.
- Elle garde une variable locale
Utiliser le bouton Follow mouse, pour que lors d’un clic :
- Si le traqueur n’est pas actif, on l’active,
- Si le traqueur est déjà actif, on le désactive :
- on cache le cercle avec
circle.style.display = "none";
; - on supprime le handler de
mousemove
; - on arrête le timer de
setInterval
.
- on cache le cercle avec
Notons que là aussi, on se satisfera de variables globales pour ce TP.
Plutôt que d’utiliser setInterval(handler, delay)
, on pourra faire une version avec requestAnimationFrame() qui produira une animation beaucoup plus fluide.
Exercice 6 : Tableau et tri
Un tableau HTML est fournit dans le fichier tp2.html
.
On note qu’on utilise les éléments <thead>
, <th>
ainsi que des attributs HTML data-*
qui permettent de personnaliser les éléments (MDN). On peut accéder à ces attributs avec la propriété HTMLElement.dataset
(MDN), par exemple avec le header du tableau :
document.querySelector("#the-table th").dataset.type;
Ajouter une fonctionnalité qui permet de trier les lignes du tableau quand clique sur le nom de la colonne. On utilisera :
- Une délégation d’événements sur
<thead>
- L’attribut
data-type
pour utiliser le bon type lors du tri des lignes, avecString.localeCompare()
pour les chaînes (MDN). - L’attribut
cellIndex
pour connaître le numéro de colonne (MDN).
Le travail consiste essentiellement à écrire la bonne fonction de comparaison pour la méthode Array.sort()
sur les lignes <tr>
du tableau, Array.sort()
par défaut ne fait pas vraiment ce qu’on attend.
Ensuite, ajouter un état aux colonnes pour qu’on puisse trier dans l’ordre croissant au premier clic, puis décroissant au second, etc.
Enfin, rajouter une colonne de type date (MDN) triable elle aussi. On fera vérifiera que la date 2023-12-01 14:20
est bien après 2023-12-1 11:23
(c’est l’inverse dans l’ordre lexicographique).
Exercice 1bis (Bonus): undo-redo fonctionnel
Cette exercice est un aperçu simpliste de la façon dont est géré l’état d’une application React ou Vue.
Basculer la variable count
en constante.
Garder un historique des changements sous forme d’un tableau qui contient une liste ordonnée des opérations effectuées (incrémentation, décrémentation, remise à zéro).
Quand une modification a lieu, appliquer toutes les opérations enregistrées sur votre constante count
puis modifier l’élément du DOM avec le résultat.
- Rajouter deux boutons “undo” et “redo” (statiquement ou dynamiquement).
- Créer une variable
operationPointer
qui suit la quantité d’opérations effectuée. - Quand on presse sur undo ou redo, on modifiera
operationPointer
pour indiquer de combien d’étape on veut revenir dans le passé. Puis on appliquera toutes les opérations enregistrées surcount
jusqu’à arriver à l’étape souhaitée. Enfin on modifiera l’élément du DOM avec le résultat. - Désactiver les boutons undo ou redo quand il n’est plus possible d’aller en arrière (ou d’aller dans le futur).
Rajouter un menu dropdown qui affiche la liste des opérations effectuée et permette de revenir à une étape donnée.
Exercice 2bis (Bonus) : validation HTML
Les champs input
ont en réalité une propriété permettant la validation.
Cette dernière est associée à une pseudo-classe CSS :valid.
Créer un nouveau champ email qui cette fois validera une adresse se terminant par @etu.univ-lyon1.fr
. Le formulaire devra être signalé par des basiles <form></form>
pour que la validation soit effectuée à la soumission du formulaire. Pour cela il faut que le bouton de soumission soit de type=submit
.
Exercice 4 suite (Bonus)
Des sprites vous sont fournies ici
-
Rajouter des classes CSS pour gérer ces sprites, on utilisera la propriété background-image.
-
Choisir le bon sprite de personnage selon la direction dans laquelle il avance
-
Il y a deux sprites pour chaque direction, en alternant leur affichage vous donnerez une impression de mouvement.