Appearance
Module 13 : JavaScript – Événements clavier et souris, sélecteurs multiples
Introduction
Ce module explore les événements du clavier et de la souris en JavaScript, ainsi que les méthodes de sélection d'éléments HTML qui retournent plusieurs résultats. Vous apprendrez à détecter les touches enfoncées par l'utilisateur, à réagir au survol de la souris, et à cibler efficacement des groupes d'éléments dans le DOM avec
getElementsByClassName(),getElementsByTagName()etquerySelectorAll().
Objectifs du cours
- Comprendre et utiliser les événements clavier :
keydownetkeyup - Détecter quelle touche a été pressée grâce à
event.key - Comprendre et utiliser les événements souris :
mouseoveretmouseout - Sélectionner plusieurs éléments du DOM avec
getElementsByClassName(),getElementsByTagName()etquerySelectorAll() - Parcourir une collection d'éléments HTML pour leur appliquer des traitements
Théorie
1 : Événements du clavier et détection des touches
Définition
Les événements du clavier permettent de détecter l'interaction de l'utilisateur avec les touches de son clavier. JavaScript offre deux événements principaux :
keydownetkeyup. Ces événements sont accompagnés de la propriétéevent.keyqui permet de savoir exactement quelle touche a été pressée.
Contexte d'utilisation
Ces événements sont couramment utilisés pour :
- Filtrer une liste de données au fur et à mesure que l'utilisateur tape dans un champ de recherche
- Empêcher la saisie de certains caractères (par exemple, bloquer les lettres dans un champ numérique)
- Déclencher une action lorsqu'une touche spécifique est pressée (ex. : Entrée pour soumettre, Échap pour fermer)
Les événements clavier principaux
| Événement | Déclenchement | Usage courant |
|---|---|---|
keydown | Quand l'utilisateur appuie sur une touche | Détecter les raccourcis, filtrer en temps réel |
keyup | Quand l'utilisateur relâche une touche | Valider la saisie après la frappe |
Important :
keydownest l'événement le plus utilisé pour détecter les touches du clavier.keyupest utile lorsqu'on veut accéder à la valeur mise à jour du champ de saisie.
Détecter quelle touche a été pressée avec event.key
Lorsqu'un événement clavier est déclenché (keydown ou keyup), l'objet event contient la propriété key, qui est une chaîne de caractères représentant la touche pressée. Cela permet de savoir exactement quelle touche l'utilisateur a utilisée et d'agir en conséquence.
Valeurs courantes de event.key
| Touche | Valeur de event.key |
|---|---|
| Lettre A | "a" ou "A" (selon Shift) |
| Chiffre 5 | "5" |
| Entrée | "Enter" |
| Échapper | "Escape" |
| Espace | " " |
| Flèche haut | "ArrowUp" |
| Flèche bas | "ArrowDown" |
| Retour arrière | "Backspace" |
| Tabulation | "Tab" |
Note : La propriété
event.keyretourne une chaîne de caractères. La comparaison est sensible à la casse pour les lettres ("a"≠"A"), mais les touches spéciales ont un nom fixe ("Enter","Escape", etc.).
Exemple pratique : démonstration des événements et détection des touches
HTML correspondant
html
<p>Taper le nom d'un produit et, au fur à mesure que les touchents du clavier sont tappées, vérifier la console pour voir les types d'événements déclanchés</p>
<input type="text" id="recherche" placeholder="Rechercher un produit...">javascript
const champRecherche = document.getElementById('recherche');
function gererKeydown(event) {
console.log('keydown — touche enfoncée : ' + event.key);
console.log('Contenu du champ : ' + champRecherche.value); // Ne contient PAS encore le nouveau caractère
}
function gererKeyup(event) {
console.log('keyup — touche relâchée : ' + event.key);
console.log('Contenu du champ : ' + champRecherche.value); // Contient le nouveau caractère
}
function init()
{
// Se déclenche AVANT que le caractère apparaisse dans le champ
champRecherche.addEventListener('keydown', gererKeydown);
// Se déclenche APRÈS que le caractère soit apparu dans le champ
champRecherche.addEventListener('keyup', gererKeyup);
}
addEventListener('load', init);Exemple pratique : détecter la touche Entrée et Échapper
Prenons un exemple concret avec une source de données :
HTML correspondant
html
<input type="text" id="recherche" placeholder="Rechercher un produit...">
<p id="resultats"></p>javascript
// Tableau linéaire (indexé) contenant des objets. Chaque objet est un PRODUIT
const produits = [
{
nom: 'Clavier mécanique',
prix: 89
},
{
nom: 'Souris ergonomique',
prix: 45
},
{
nom: 'Écran 27 pouces',
prix: 350
}
];
function gererToucheClavier(event) {
const champRecherche = document.getElementById('recherche');
// Si la touche enter est appuyée
if (event.key === 'Enter') {
// Afficher les résultats de recherche
const terme = champRecherche.value.toLowerCase();
const resultats = document.getElementById('resultats');
resultats.textContent = '';
for (const produit of produits) {
if (produit.nom.toLowerCase().includes(terme)) {
resultats.textContent += produit.nom + ' — ' + produit.prix + ' $ | ';
}
}
}
// Si ESCAPE est appuyée
if (event.key === 'Escape') {
// Vider le champ de recherche
champRecherche.value = '';
document.getElementById('resultats').textContent = '';
}
}
function init() {
const champRecherche = document.getElementById('recherche');
champRecherche.addEventListener('keydown', gererToucheClavier);
}
addEventListener('load', init);Exemple pratique : filtrer une liste en temps réel avec keyup
Dans cet exemple, on utilise keyup pour filtrer une liste de produits affichés dans la page, au fur et à mesure que l'utilisateur tape dans le champ de recherche.
HTML correspondant
html
<input type="text" id="recherche" placeholder="Filtrer les produits...">
<div id="liste-produits"></div>javascript
const produits = [
{ nom: 'Clavier mécanique', prix: 89 },
{ nom: 'Souris ergonomique', prix: 45 },
{ nom: 'Écran 27 pouces', prix: 350 }
];
function afficherProduits(liste) {
const zone = document.getElementById('liste-produits');
zone.textContent = '';
for (const produit of liste) {
const paragraphe = document.createElement('p');
paragraphe.textContent = produit.nom + ' — ' + produit.prix + ' $';
zone.appendChild(paragraphe);
}
}
function filtrerProduits(event) {
const terme = event.currentTarget.value.toLowerCase();
const resultats = [];
for (const produit of produits) {
if (produit.nom.toLowerCase().includes(terme)) {
resultats.push(produit);
}
}
afficherProduits(resultats);
}
function init() {
// Afficher tous les produits au chargement
afficherProduits(produits);
const champRecherche = document.getElementById('recherche');
champRecherche.addEventListener('keyup', filtrerProduits);
}
addEventListener('load', init);Note : On utilise
keyupici plutôt quekeydownparce qu'on veut lire la valeur mise à jour du champ. Aveckeydown, la valeur ne contient pas encore le caractère en cours de frappe.
Points importants
keydownest l'événement le plus utilisé pour détecter les touches du clavierkeyupest utile lorsqu'on veut accéder à la valeur mise à jour du champ de saisie- Si la touche est maintenue enfoncée,
keydownse répète continuellement, maiskeyupne se déclenche qu'une seule fois au relâchement event.keypermet de connaître exactement quelle touche a été pressée- Pour les lettres, la valeur change selon que Shift est enfoncé ou non
- Toujours utiliser
event.keyplutôt queevent.keyCode(déprécié) keyupest préférable pour lire la valeur du champ de saisie, car elle est déjà à jour
Exercices pratiques
Voir l'exercice 1
2 : Les événements de la souris — mouseover et mouseout
Définition
Les événements
mouseoveretmouseoutpermettent de détecter le survol de la souris sur un élément HTML.mouseoverse déclenche lorsque le curseur entre sur un élément, etmouseoutse déclenche lorsque le curseur quitte cet élément.
Contexte d'utilisation
Ces événements sont utiles pour :
- Afficher des informations supplémentaires au survol d'un produit ou d'une carte
- Mettre en surbrillance un élément de liste lorsque la souris passe dessus
- Créer des effets visuels interactifs sans recourir uniquement au CSS
:hover - Afficher ou masquer des détails dynamiquement selon la position de la souris
Note : Pour des effets purement visuels (changement de couleur, ombre, etc.), le pseudo-sélecteur CSS
:hoverest souvent suffisant. Les événementsmouseoveretmouseouten JavaScript sont utiles lorsqu'on doit exécuter de la logique (modifier du texte, afficher des données, etc.) au survol.
Exemple pratique : afficher les détails d'un employé au survol
HTML correspondant
html
<div id="liste-employes"></div>
<p id="details">Survolez un employé pour voir ses détails.</p>CSS correspondant
css
.employe-item {
padding: 10px;
margin: 5px 0;
background-color: #ecf0f1;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.employe-item:hover {
background-color: #3498db;
color: white;
}javascript
const employes = [
{
nom: 'Alice Tremblay',
poste: 'Développeuse',
salaire: 72000 },
{
nom: 'Bob Martin',
poste: 'Designer',
salaire: 65000 },
{
nom: 'Clara Dubois',
poste: 'Analyste',
salaire: 68000
}
];
function afficherDetails(event) {
// Rappel : le dataset permet de stocker des données personnalisées sur les éléments HTML
// Dans ce cas-ci, on extrait l'attribut "data-index"
const index = event.currentTarget.dataset.index;
const employe = employes[index];
const details = document.getElementById('details');
details.textContent = employe.nom + ' — ' + employe.poste + ' — ' + employe.salaire + ' $';
}
function cacherDetails() {
const details = document.getElementById('details');
details.textContent = 'Survolez un employé pour voir ses détails.';
}
function init() {
const div = document.getElementById('liste-employes');
for (let i = 0; i < employes.length; i++) {
const element = document.createElement('p');
element.textContent = employes[i].nom;
// On créer un attribut data-index à la balise <p data-index=""> ... </p> pour stocker l'index de l'employé (sa position dans le tableau)
element.dataset.index = i;
element.classList.add('employe-item');
// On ajoute des événements
element.addEventListener('mouseover', afficherDetails);
element.addEventListener('mouseout', cacherDetails);
// On ajoute dans la div l'employé la balise <p> contenant les informations de l'employé
div.appendChild(element);
}
}
addEventListener('load', init);Différence entre mouseover/mouseout et mouseenter/mouseleave
| Événement | Se propage aux enfants ? | Usage |
|---|---|---|
mouseover | ✓ Oui | Se déclenche aussi quand on entre sur un enfant |
mouseout | ✓ Oui | Se déclenche aussi quand on entre sur un enfant |
mouseenter | ✗ Non | Se déclenche uniquement sur l'élément ciblé |
mouseleave | ✗ Non | Se déclenche uniquement sur l'élément ciblé |
Note :
mouseenteretmouseleavesont souvent préférés lorsqu'un élément contient des enfants, car ils ne se redéclenchent pas à chaque passage sur un élément enfant. Pour des éléments simples (sans enfants),mouseoveretmouseoutfonctionnent très bien.
Points importants
mouseoverse déclenche quand le curseur entre sur l'élémentmouseoutse déclenche quand le curseur quitte l'élément- Ces événements se propagent aux éléments enfants (contrairement à
mouseenter/mouseleave) event.currentTargetfait référence à l'élément sur lequel l'écouteur est attaché- On peut combiner
datasetpour stocker l'index de l'objet et retrouver ses données
Exercices pratiques
Voir l'exercice 2
3 : Sélectionner plusieurs éléments du DOM
Définition
Jusqu'ici, nous avons utilisé
document.getElementById()pour cibler un seul élément par son identifiant. Cependant, il est fréquent de vouloir cibler plusieurs éléments à la fois — par exemple, tous les paragraphes d'une section, toutes les images d'une galerie, ou tous les éléments ayant une même classe CSS. JavaScript offre trois méthodes pour sélectionner des collections d'éléments.
Contexte d'utilisation
Ces méthodes sont essentielles pour :
- Parcourir tous les éléments d'une liste pour leur ajouter un événement
- Modifier le style ou le contenu de plusieurs éléments simultanément
- Appliquer un traitement à tous les éléments partageant une même classe CSS
- Cibler un ensemble d'éléments avec un sélecteur CSS précis
4.1 : getElementsByClassName(nomDeClasse)
Cette méthode retourne une collection HTML (HTMLCollection) de tous les éléments possédant la classe CSS spécifiée.
javascript
const elements = document.getElementsByClassName('carte-produit');Exemple complet :
html
<div class="carte-produit">Produit 1</div>
<div class="carte-produit">Produit 2</div>
<div class="carte-produit">Produit 3</div>
<p id="compteur"></p>javascript
function init() {
const cartes = document.getElementsByClassName('carte-produit');
document.getElementById('compteur').textContent = 'Nombre de cartes : ' + cartes.length;
// Parcourir la collection avec une boucle for classique
for (let i = 0; i < cartes.length; i++) {
cartes[i].style.backgroundColor = '#3498db';
cartes[i].style.color = 'white';
cartes[i].style.padding = '10px';
cartes[i].style.margin = '5px';
}
}
addEventListener('load', init);Note :
getElementsByClassName()ne prend pas de point (.) devant le nom de la classe. On écrit'carte-produit'et non'.carte-produit'.
4.2 : getElementsByTagName(nomDeBalise)
Cette méthode retourne une collection HTML (HTMLCollection) de tous les éléments correspondant au nom de balise spécifié.
javascript
const paragraphes = document.getElementsByTagName('p');Exemple complet :
html
<p>Premier paragraphe</p>
<p>Deuxième paragraphe</p>
<p>Troisième paragraphe</p>
<button id="btn-modifier">Modifier les paragraphes</button>javascript
function modifierParagraphes() {
const paragraphes = document.getElementsByTagName('p');
for (let i = 0; i < paragraphes.length; i++) {
paragraphes[i].style.fontWeight = 'bold';
paragraphes[i].style.color = '#e74c3c';
}
}
function init() {
const bouton = document.getElementById('btn-modifier');
bouton.addEventListener('click', modifierParagraphes);
}
addEventListener('load', init);Note : Le nom de la balise est écrit sans les chevrons (
<>). On écrit'p'et non'<p>'.
4.3 : querySelectorAll(selecteurCSS)
Cette méthode retourne une NodeList de tous les éléments correspondant au sélecteur CSS fourni. C'est la méthode la plus polyvalente, car elle accepte n'importe quel sélecteur CSS valide.
javascript
const elements = document.querySelectorAll('.carte-produit');
const items = document.querySelectorAll('ul li');
const boutons = document.querySelectorAll('button.btn-action');Exemple complet :
html
<ul id="liste-cours">
<li class="cours">HTML</li>
<li class="cours">CSS</li>
<li class="cours">JavaScript</li>
</ul>
<button id="btn-surligner">Surligner les cours</button>javascript
function surlignerCours() {
const cours = document.querySelectorAll('.cours');
for (const element of cours) {
element.style.backgroundColor = '#f1c40f';
element.style.padding = '5px';
}
}
function init() {
const bouton = document.getElementById('btn-surligner');
bouton.addEventListener('click', surlignerCours);
}
addEventListener('load', init);Important : Avec
querySelectorAll(), on utilise la syntaxe des sélecteurs CSS :.pour les classes,#pour les identifiants, rien pour les balises. C'est la même syntaxe que dans un fichier CSS.
Comparaison des trois méthodes
| Méthode | Paramètre | Retour | Syntaxe CSS ? |
|---|---|---|---|
getElementsByClassName('nom') | Nom de classe (sans .) | HTMLCollection | ✗ |
getElementsByTagName('balise') | Nom de balise (sans <>) | HTMLCollection | ✗ |
querySelectorAll('sélecteur') | Sélecteur CSS complet | NodeList | ✓ |
Exemple pratique combiné : afficher des données à partir d'un tableau d'objets
Voici un exemple complet qui utilise querySelectorAll() pour cibler et modifier tous les éléments d'une liste générée dynamiquement à partir d'un tableau d'objets.
HTML
html
<div id="zone-livres"></div>
<button id="btn-promotion">Appliquer 10% de rabais</button>
<p id="message-promo"></p>JavaScript
javascript
const livres = [
{ titre: 'Le Petit Prince', prix: 15 },
{ titre: 'Harry Potter', prix: 25 },
{ titre: 'Dune', prix: 20 }
];
function afficherLivres() {
const zone = document.getElementById('zone-livres');
for (const livre of livres) {
const carte = document.createElement('div');
carte.classList.add('carte-livre');
const titre = document.createElement('h3');
titre.textContent = livre.titre;
const prix = document.createElement('p');
prix.classList.add('prix-livre');
prix.textContent = livre.prix + ' $';
carte.appendChild(titre);
carte.appendChild(prix);
zone.appendChild(carte);
}
}
function appliquerPromotion() {
// Sélectionner TOUS les éléments ayant la classe 'prix-livre'
const prixElements = document.querySelectorAll('.prix-livre');
for (const element of prixElements) {
// Extraire le prix numérique du texte (ex: "25 $" → 25)
const prixOriginal = parseInt(element.textContent);
const nouveauPrix = (prixOriginal * 0.9).toFixed(2);
element.textContent = nouveauPrix + ' $';
element.style.color = '#e74c3c';
element.style.fontWeight = 'bold';
}
document.getElementById('message-promo').textContent = 'Promotion de 10% appliquée !';
}
function init() {
afficherLivres();
const bouton = document.getElementById('btn-promotion');
bouton.addEventListener('click', appliquerPromotion);
}
addEventListener('load', init);Points importants
getElementsByClassName()etgetElementsByTagName()n'utilisent pas la syntaxe CSS (pas de.ni de<>)querySelectorAll()utilise la syntaxe CSS complète (.classe,#id,balise, etc.)- Les trois méthodes retournent une collection qu'on peut parcourir avec une boucle
forclassique oufor...of querySelectorAll()est la méthode la plus flexible et la plus recommandée pour la plupart des cas
Exercices pratiques
Voir l'exercice 3
Objectif
Créer un répertoire d'employés qui permet de filtrer la liste par nom en tapant dans un champ de recherche (
keyup), d'afficher les détails au survol (mouseover/mouseout), et de mettre en surbrillance tous les éléments d'une classe avecquerySelectorAll().
Structure du projet VS Code :
Module13-Demo1/
├── index.html
├── css/
│ └── styles.css
└── js/
└── scripts.jsindex.html
Voir le code
html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Répertoire d'employés</title>
<link rel="stylesheet" href="css/styles.css">
<script src="js/scripts.js"></script>
</head>
<body>
<header>
<h1>Répertoire d'employés</h1>
</header>
<main>
<section id="section-recherche">
<input type="text" id="recherche" placeholder="Rechercher un employé... (Échap pour effacer)">
</section>
<section id="section-liste">
<div id="liste-employes"></div>
</section>
<section id="section-details">
<h2>Détails</h2>
<p id="details">Survolez un employé pour voir ses informations complètes.</p>
</section>
<section id="section-actions">
<button id="btn-surligner">Surligner tous les employés</button>
<button id="btn-reinitialiser">Réinitialiser</button>
</section>
</main>
</body>
</html>css/styles.css
Voir le code
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
padding: 20px;
}
header {
text-align: center;
margin-bottom: 20px;
}
header h1 {
color: #2c3e50;
}
#section-recherche {
text-align: center;
margin-bottom: 20px;
}
#recherche {
width: 100%;
max-width: 400px;
padding: 12px 16px;
font-size: 1em;
border: 2px solid #bdc3c7;
border-radius: 8px;
}
#recherche:focus {
outline: none;
border-color: #3498db;
}
#liste-employes {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
margin-bottom: 20px;
}
.carte-employe {
width: 220px;
padding: 15px;
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.carte-employe:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.carte-employe h3 {
margin-bottom: 5px;
color: #2c3e50;
}
.carte-employe p {
color: #7f8c8d;
font-size: 0.9em;
}
.surligne {
background-color: #f1c40f;
border-color: #f39c12;
}
#section-details {
text-align: center;
margin: 20px 0;
padding: 20px;
background-color: #eaf2f8;
border-radius: 8px;
}
#section-details h2 {
margin-bottom: 10px;
color: #2c3e50;
}
#details {
font-size: 1.1em;
color: #34495e;
}
#section-actions {
text-align: center;
margin-top: 20px;
}
#section-actions button {
padding: 10px 20px;
font-size: 1em;
margin: 0 10px;
border: none;
border-radius: 5px;
cursor: pointer;
}
#btn-surligner {
background-color: #3498db;
color: white;
}
#btn-surligner:hover {
background-color: #2980b9;
}
#btn-reinitialiser {
background-color: #e74c3c;
color: white;
}
#btn-reinitialiser:hover {
background-color: #c0392b;
}js/scripts.js
Voir le code
javascript
"use strict";
const employes = [
{ nom: 'Alice Tremblay', poste: 'Développeuse', salaire: 72000 },
{ nom: 'Bob Martin', poste: 'Designer', salaire: 65000 },
{ nom: 'Clara Dubois', poste: 'Analyste', salaire: 68000 }
];
/**
* Crée et affiche les cartes d'employés dans la zone de liste.
* @param {Array} liste - Le tableau d'objets employés à afficher
*/
function afficherEmployes(liste) {
const zoneEmployes = document.getElementById('liste-employes');
zoneEmployes.textContent = '';
for (let i = 0; i < liste.length; i++) {
const carte = document.createElement('div');
carte.classList.add('carte-employe');
carte.dataset.index = i;
const nom = document.createElement('h3');
nom.textContent = liste[i].nom;
const poste = document.createElement('p');
poste.textContent = liste[i].poste;
carte.appendChild(nom);
carte.appendChild(poste);
carte.addEventListener('mouseover', gererSurvol);
carte.addEventListener('mouseout', gererQuitter);
zoneEmployes.appendChild(carte);
}
}
/**
* Affiche les détails de l'employé survolé dans la zone de détails.
* @param {Event} event - L'événement mouseover
*/
function gererSurvol(event) {
const index = event.currentTarget.dataset.index;
const employe = employes[index];
const details = document.getElementById('details');
details.textContent = employe.nom + ' — ' + employe.poste + ' — ' + employe.salaire + ' $';
}
/**
* Réinitialise le texte de la zone de détails quand la souris quitte un employé.
*/
function gererQuitter() {
const details = document.getElementById('details');
details.textContent = 'Survolez un employé pour voir ses informations complètes.';
}
/**
* Filtre les employés affichés selon le texte tapé dans le champ de recherche.
* Détecte aussi la touche Échap pour vider le champ.
* @param {Event} event - L'événement keyup
*/
function gererRecherche(event) {
const champRecherche = document.getElementById('recherche');
if (event.key === 'Escape') {
champRecherche.value = '';
}
const terme = champRecherche.value.toLowerCase();
const resultats = [];
for (const employe of employes) {
if (employe.nom.toLowerCase().includes(terme)) {
resultats.push(employe);
}
}
afficherEmployes(resultats);
}
/**
* Ajoute la classe 'surligne' à toutes les cartes d'employés avec querySelectorAll.
*/
function surlignerTous() {
const cartes = document.querySelectorAll('.carte-employe');
for (const carte of cartes) {
carte.classList.add('surligne');
}
}
/**
* Retire la classe 'surligne' de toutes les cartes et réaffiche tous les employés.
*/
function reinitialiser() {
const cartes = document.querySelectorAll('.carte-employe');
for (const carte of cartes) {
carte.classList.remove('surligne');
}
document.getElementById('recherche').value = '';
afficherEmployes(employes);
}
/**
* Initialise la page au chargement : affiche les employés et attache les événements.
*/
function init() {
afficherEmployes(employes);
const champRecherche = document.getElementById('recherche');
champRecherche.addEventListener('keyup', gererRecherche);
const btnSurligner = document.getElementById('btn-surligner');
btnSurligner.addEventListener('click', surlignerTous);
const btnReinitialiser = document.getElementById('btn-reinitialiser');
btnReinitialiser.addEventListener('click', reinitialiser);
}
addEventListener('load', init);Résultat attendu
Un répertoire d'employés interactif qui combine les trois concepts vus dans ce module :
- La recherche filtre les employés en temps réel avec
keyup, et la toucheEscapevide le champ - Le survol de chaque carte affiche les détails complets (nom, poste, salaire) grâce à
mouseoveretmouseout - Le bouton « Surligner » utilise
querySelectorAll()pour cibler toutes les cartes et leur ajouter une classe CSS - Le bouton « Réinitialiser » retire la surbrillance et réaffiche la liste complète
Exercices pratiques
Exercice 1 : Recherche de jeux vidéo au clavier
Énoncé
Créer une page qui affiche une liste de jeux vidéo et permet de les filtrer en temps réel à l'aide du clavier.
Source de données
javascript
const jeux = [
{
titre: 'The Legend of Zelda',
plateforme: 'Nintendo',
prix: 79 },
{
titre: 'God of War',
plateforme: 'PlayStation',
prix: 69 },
{
titre: 'Halo Infinite',
plateforme: 'Xbox',
prix: 59
}
];Fichiers à créer
index.htmlcss/styles.cssjs/scripts.js
Consignes
- Au chargement de la page, afficher tous les jeux dans une zone
<div id="zone-jeux">sous forme de paragraphes affichant le titre, la plateforme et le prix - Ajouter un champ de recherche
<input>dans la page - Utiliser l'événement
keyupsur le champ pour filtrer les jeux dont le titre contient le texte saisi (insensible à la casse) - Si l'utilisateur appuie sur la touche
Escape, vider le champ de recherche et réafficher tous les jeux - Si l'utilisateur appuie sur la touche
Enter, afficher dans un<p id="compteur">le nombre de résultats trouvés (ex : « 2 jeux trouvés »)
Exercice 2 : Catalogue de films avec survol
Énoncé
Créer un catalogue de films affiché sous forme de cartes. Au survol d'une carte, afficher les détails du film dans une zone dédiée. Au départ de la souris, remettre le message par défaut.
Source de données
javascript
const films = [
{
titre: 'Inception',
realisateur: 'Christopher Nolan',
annee: 2010
},
{
titre: 'Parasite',
realisateur: 'Bong Joon-ho',
annee: 2019
},
{
titre: 'Interstellar',
realisateur: 'Christopher Nolan',
annee: 2014
}
];Fichiers à créer
index.htmlcss/styles.cssjs/scripts.js
Consignes
- Au chargement de la page, générer dynamiquement une carte
<div class="carte-film">pour chaque film, contenant uniquement le titre dans un<h3> - Utiliser
datasetsur chaque carte pour stocker l'index du film dans le tableau - Attacher un événement
mouseoverà chaque carte pour afficher, dans un<p id="info-film">, les détails du film survolé : titre, réalisateur et année - Attacher un événement
mouseoutà chaque carte pour remettre le texte du paragraphe à : « Survolez un film pour voir ses détails. » - Styliser les cartes avec CSS pour qu'elles soient disposées en ligne avec Flexbox, avec un effet visuel au survol (changement de couleur de fond, ombre, etc.)
Exercice 3 : Gestion d'une liste de tâches avec sélecteurs multiples
Énoncé
Créer une page affichant une liste de tâches. L'utilisateur peut ajouter une tâche via un champ de texte (touche
Enter), marquer toutes les tâches comme complétées avec un bouton, et voir les détails de chaque tâche au survol. Cet exercice combine les événements clavier, souris et les sélecteurs multiples.
Fichiers à créer
index.htmlcss/styles.cssjs/scripts.js
Consignes
- Afficher dans la page un champ de saisie
<input>et un<div id="zone-taches"> - Lorsque l'utilisateur tape du texte et appuie sur
Enter, créer dynamiquement un élément<div class="tache">contenant le texte saisi, et l'ajouter dans la zone de tâches. Vider ensuite le champ de saisie - Si l'utilisateur appuie sur
Escape, vider le champ de saisie sans ajouter de tâche - Chaque tâche créée doit réagir au
mouseoveren changeant sa couleur de fond, et aumouseouten revenant à la couleur d'origine - Ajouter un bouton « Tout compléter » qui utilise
querySelectorAll('.tache')pour parcourir toutes les tâches et leur ajouter la classe CSScompletee(texte barré, couleur atténuée) - Ajouter un bouton « Réinitialiser » qui utilise
getElementsByClassName('tache')pour retirer la classecompleteede toutes les tâches
Exercice 4 : Bataille de cartes (Synthèse)
Énoncé
Créer un jeu de bataille de cartes. L'utilisateur crée ses propres cartes avec une valeur et une couleur, puis affronte 10 combats en devinant laquelle de deux cartes tirées au hasard est la plus forte.
Source de données
javascript
const cartes = [];Fichiers à créer
index.htmlcss/styles.cssjs/scripts.js
Consignes
Partie 1 — Création des cartes
- Afficher un formulaire contenant :
- Un champ numérique
<input type="number">pour la valeur de la carte (entre 1 et 13) - Une liste de choix
<select>pour la couleur de fond de la carte - Un bouton « Ajouter la carte »
- Un champ numérique
- Lorsque l'utilisateur clique sur le bouton, ajouter un objet
{ valeur: ..., backgroundColor: '...' }dans le tableaucartes - L'utilisateur peut ajouter un maximum de 10 cartes. Une fois la limite atteinte, désactiver le bouton d'ajout
- On prend pour acquis que l'utilisateur ne mettra pas deux cartes ayant la même valeur
- À droite du formulaire, afficher en temps réel le contenu du tableau dans une zone
<div id="zone-liste-cartes">, sous la forme : « Carte #1 : 10, red », « Carte #2 : 5, blue », etc. - Lorsque l'utilisateur survole (
mouseover) un élément de la liste des cartes, changer sa couleur de fond pour qu'elle corresponde à la propriétébackgroundColorde la carte. Aumouseout, remettre la couleur de fond par défaut
Partie 2 — Bataille
L'utilisateur doit avoir créé au moins 2 cartes avant de pouvoir commencer la bataille. Sinon, afficher un message
- En dessous de la section de création, afficher une zone de combat avec un bouton « Prochain combat »
- Lorsque ce bouton est appuyée, désactiver le bouton (disabled) pour éviter de cliquer plusieurs fois pendant un combat
- À chaque clic sur le bouton, piger 2 cartes au hasard dans le tableau
carteset les afficher côte à côte dans la zone de combat. Chaque carte affiche sa valeur et a comme couleur de fond sa propriétébackgroundColor. Se baser sur W3Schools — JavaScript Random pour utiliser une fonctionRandomafin de générer la position de la carte à tirer dans le tableau - L'utilisateur doit cliquer sur la carte qu'il pense être la plus forte (valeur la plus élevée)
- Si le choix est correct, incrémenter le score de 1 et afficher un message de succès
- Si le choix est incorrect, afficher un message d'erreur
- Penser à une façon de prévenir la situation où l'utilisateur pour cliquer plusieurs fois sur les cartes avant de passer au prochain combat.
- Lorsque le choix a été fait, le message affichée en conséquence, il faut réactiver le bouton « Prochain combat »
- Après 10 combats, afficher un
alertindiquant le score final de l'utilisateur sur 10 (ex. : « Votre score : 7 / 10 »), puis désactiver le bouton de combat
Correction
Correction Exercice 1
Voir le code complet
Solution HTML
html
<body>
<header>
<h1>Recherche de jeux vidéo</h1>
</header>
<main>
<input type="text" id="recherche" placeholder="Rechercher un jeu... (Entrée = compter, Échap = effacer)">
<p id="compteur"></p>
<div id="zone-jeux"></div>
</main>
</body>Solution CSS
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f5f5f5;
}
header {
text-align: center;
margin-bottom: 20px;
}
#recherche {
display: block;
width: 100%;
max-width: 400px;
margin: 0 auto 15px auto;
padding: 12px 16px;
font-size: 1em;
border: 2px solid #bdc3c7;
border-radius: 8px;
}
#recherche:focus {
outline: none;
border-color: #3498db;
}
#compteur {
text-align: center;
font-weight: bold;
color: #2c3e50;
margin-bottom: 15px;
}
#zone-jeux {
max-width: 500px;
margin: 0 auto;
}
.jeu-item {
padding: 12px;
margin: 8px 0;
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
}
.jeu-item strong {
color: #2c3e50;
}Solution JavaScript
javascript
"use strict";
const jeux = [
{ titre: 'The Legend of Zelda', plateforme: 'Nintendo', prix: 79 },
{ titre: 'God of War', plateforme: 'PlayStation', prix: 69 },
{ titre: 'Halo Infinite', plateforme: 'Xbox', prix: 59 }
];
function afficherJeux(liste) {
const zone = document.getElementById('zone-jeux');
zone.textContent = '';
for (const jeu of liste) {
const paragraphe = document.createElement('div');
paragraphe.classList.add('jeu-item');
paragraphe.innerHTML = '<strong>' + jeu.titre + '</strong> — ' + jeu.plateforme + ' — ' + jeu.prix + ' $';
zone.appendChild(paragraphe);
}
}
function filtrerJeux() {
const terme = document.getElementById('recherche').value.toLowerCase();
const resultats = [];
for (const jeu of jeux) {
if (jeu.titre.toLowerCase().includes(terme)) {
resultats.push(jeu);
}
}
return resultats;
}
function gererRecherche(event) {
const champRecherche = document.getElementById('recherche');
if (event.key === 'Escape') {
champRecherche.value = '';
document.getElementById('compteur').textContent = '';
afficherJeux(jeux);
return;
}
const resultats = filtrerJeux();
afficherJeux(resultats);
if (event.key === 'Enter') {
document.getElementById('compteur').textContent = resultats.length + ' jeux trouvés';
}
}
function init() {
afficherJeux(jeux);
const champRecherche = document.getElementById('recherche');
champRecherche.addEventListener('keyup', gererRecherche);
}
addEventListener('load', init);Explications
La fonction
filtrerJeux()est séparée pour être réutilisable. L'événementkeyuppermet de lire la valeur mise à jour du champ. La toucheEscapevide le champ et réaffiche tous les jeux, tandis queEnteraffiche le compteur de résultats.
Correction Exercice 2
Voir le code complet
Solution HTML
html
<body>
<header>
<h1>Catalogue de films</h1>
</header>
<main>
<div id="zone-films"></div>
<p id="info-film">Survolez un film pour voir ses détails.</p>
</main>
</body>Solution CSS
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f5f5f5;
}
header {
text-align: center;
margin-bottom: 20px;
}
#zone-films {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
margin-bottom: 20px;
}
.carte-film {
width: 200px;
padding: 20px;
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.carte-film:hover {
background-color: #3498db;
color: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.carte-film h3 {
font-size: 1.1em;
}
#info-film {
text-align: center;
font-size: 1.1em;
padding: 15px;
background-color: #eaf2f8;
border-radius: 8px;
color: #34495e;
}Solution JavaScript
javascript
"use strict";
const films = [
{ titre: 'Inception', realisateur: 'Christopher Nolan', annee: 2010 },
{ titre: 'Parasite', realisateur: 'Bong Joon-ho', annee: 2019 },
{ titre: 'Interstellar', realisateur: 'Christopher Nolan', annee: 2014 }
];
function afficherDetailsFilm(event) {
const index = event.currentTarget.dataset.index;
const film = films[index];
const info = document.getElementById('info-film');
info.textContent = film.titre + ' — Réalisé par ' + film.realisateur + ' (' + film.annee + ')';
}
function cacherDetailsFilm() {
const info = document.getElementById('info-film');
info.textContent = 'Survolez un film pour voir ses détails.';
}
function init() {
const zone = document.getElementById('zone-films');
for (let i = 0; i < films.length; i++) {
const carte = document.createElement('div');
carte.classList.add('carte-film');
carte.dataset.index = i;
const titre = document.createElement('h3');
titre.textContent = films[i].titre;
carte.appendChild(titre);
carte.addEventListener('mouseover', afficherDetailsFilm);
carte.addEventListener('mouseout', cacherDetailsFilm);
zone.appendChild(carte);
}
}
addEventListener('load', init);Points de discussion
- Chaque carte utilise
dataset.indexpour stocker sa position dans le tableau, ce qui permet de retrouver l'objet complet lors du survol - Un seul gestionnaire
afficherDetailsFilmest partagé par toutes les cartes - La séparation entre la logique de survol (
mouseover) et le retour à l'état initial (mouseout) rend le code clair et maintenable
Correction Exercice 3
Voir le code complet
Solution HTML
html
<body>
<header>
<h1>Gestion de tâches</h1>
</header>
<main>
<section id="section-ajout">
<input type="text" id="nouvelle-tache" placeholder="Ajouter une tâche (Entrée pour ajouter, Échap pour effacer)">
</section>
<section id="section-actions">
<button id="btn-completer">Tout compléter</button>
<button id="btn-reinitialiser">Réinitialiser</button>
</section>
<div id="zone-taches"></div>
</main>
</body>Solution CSS
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f5f5f5;
}
header {
text-align: center;
margin-bottom: 20px;
}
#section-ajout {
text-align: center;
margin-bottom: 15px;
}
#nouvelle-tache {
width: 100%;
max-width: 500px;
padding: 12px 16px;
font-size: 1em;
border: 2px solid #bdc3c7;
border-radius: 8px;
}
#nouvelle-tache:focus {
outline: none;
border-color: #3498db;
}
#section-actions {
text-align: center;
margin-bottom: 20px;
}
#section-actions button {
padding: 10px 20px;
font-size: 1em;
margin: 0 10px;
border: none;
border-radius: 5px;
cursor: pointer;
color: white;
}
#btn-completer {
background-color: #27ae60;
}
#btn-completer:hover {
background-color: #219a52;
}
#btn-reinitialiser {
background-color: #e74c3c;
}
#btn-reinitialiser:hover {
background-color: #c0392b;
}
#zone-taches {
max-width: 500px;
margin: 0 auto;
}
.tache {
padding: 12px;
margin: 8px 0;
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.completee {
text-decoration: line-through;
color: #95a5a6;
background-color: #ecf0f1;
}Solution JavaScript
javascript
"use strict";
function gererSurvolTache(event) {
if (event.currentTarget.classList.contains('completee') === false) {
event.currentTarget.style.backgroundColor = '#d5f5e3';
}
}
function gererQuitterTache(event) {
if (event.currentTarget.classList.contains('completee') === false) {
event.currentTarget.style.backgroundColor = 'white';
}
}
function ajouterTache(event) {
const champTache = document.getElementById('nouvelle-tache');
if (event.key === 'Escape') {
champTache.value = '';
return;
}
if (event.key === 'Enter') {
const texte = champTache.value.trim();
if (texte === '') {
return;
}
const zone = document.getElementById('zone-taches');
const tache = document.createElement('div');
tache.classList.add('tache');
tache.textContent = texte;
tache.addEventListener('mouseover', gererSurvolTache);
tache.addEventListener('mouseout', gererQuitterTache);
zone.appendChild(tache);
champTache.value = '';
}
}
function completerTout() {
const taches = document.querySelectorAll('.tache');
for (const tache of taches) {
tache.classList.add('completee');
tache.style.backgroundColor = '';
}
}
function reinitialiser() {
const taches = document.getElementsByClassName('tache');
for (let i = 0; i < taches.length; i++) {
taches[i].classList.remove('completee');
taches[i].style.backgroundColor = '';
}
}
function init() {
const champTache = document.getElementById('nouvelle-tache');
champTache.addEventListener('keyup', ajouterTache);
const btnCompleter = document.getElementById('btn-completer');
btnCompleter.addEventListener('click', completerTout);
const btnReinitialiser = document.getElementById('btn-reinitialiser');
btnReinitialiser.addEventListener('click', reinitialiser);
}
addEventListener('load', init);Vérifications des connaissances
Testez vos connaissances acquises dans ce cours en répondant aux questions suivantes. Cliquez sur chaque question pour révéler la réponse.
Question 1
Quelle est la différence principale entre keydown et keyup ?
Réponse
keydown se déclenche au moment où la touche est enfoncée (avant que le caractère apparaisse dans le champ), tandis que keyup se déclenche au moment où la touche est relâchée (après que le caractère soit apparu dans le champ). Pour lire la valeur mise à jour d'un champ de saisie, on préfère keyup.
Question 2
Comment accéder au nom de la touche pressée dans un gestionnaire d'événement clavier ?
Réponse
On utilise la propriété event.key, qui retourne une chaîne de caractères représentant la touche pressée. Par exemple :
javascript
function gererTouche(event) {
if (event.key === 'Enter') {
// L'utilisateur a appuyé sur Entrée
}
}Question 3
Quelle est la différence entre mouseover et mouseenter ?
Réponse
mouseover se propage aux éléments enfants : il se redéclenche lorsque la souris passe sur un élément enfant contenu dans l'élément ciblé. mouseenter ne se propage pas aux enfants et ne se déclenche qu'une seule fois lorsque la souris entre sur l'élément ciblé. Pour des éléments contenant des enfants, mouseenter évite les déclenchements multiples.
Question 4
Quelle est la syntaxe correcte pour sélectionner tous les éléments ayant la classe produit avec getElementsByClassName() ?
Réponse
javascript
const produits = document.getElementsByClassName('produit');On écrit le nom de la classe sans le point (.). Le point est utilisé uniquement avec querySelectorAll(), qui accepte la syntaxe CSS :
javascript
const produits = document.querySelectorAll('.produit');Question 5
Quelle méthode de sélection multiple accepte un sélecteur CSS complet ?
Réponse
querySelectorAll() est la seule méthode qui accepte un sélecteur CSS complet. On peut lui passer n'importe quel sélecteur valide en CSS :
javascript
document.querySelectorAll('.carte-produit'); // Par classe
document.querySelectorAll('ul li'); // Sélecteur descendant
document.querySelectorAll('button.btn-action'); // Balise + classeQuestion 6
Comment, à l'aide de getElementsByTagName, modifier la couleur de tous les liens <a> d'un site web pour « red » ?
Réponse
Vous pouvez utiliser getElementsByTagName('a') pour sélectionner tous les liens, puis parcourir la collection et modifier la propriété style.color de chaque élément. Exemple :
javascript
function colorerLiensEnRouge() {
const liens = document.getElementsByTagName('a');
for (let i = 0; i < liens.length; i++) {
liens[i].style.color = 'red';
}
}
addEventListener('load', colorerLiensEnRouge);Question 7
Comment ajouter, à l'aide de JavaScript, un événement mouseover sur toutes les images d'un site web afin que, au survol, l'image survolée reçoive une bordure de 3 pixels ?
Réponse
javascript
function gererMouseOver(event)
{
// Lors du survol de la souris sur cette image, changer sa bordure.
event.currentTarget.style.border = '3px solid #000';
}
function init()
{
// Récupérer les balises img
const images = document.querySelectorAll('img');
// Les parcourir, et ajouter un evenement
for (const img of images) {
img.addEventListener('mouseover', gererMouseOver);
}
}
addEventListener('load', init);Question 8
Soit le code HTML suivant :
html
<nav>
<ul>
<li><a href="#" id="lien1" class="lien">Accueil</a></li>
<li><a href="#" id="lien2" class="lien">À propos</a></li>
<li><a href="#" id="lien3" class="lien">Services</a></li>
<li><a href="#" id="lien4" class="lien">Contact</a></li>
</ul>
</nav>Question : comment déterminer et afficher (alert) le nombre d'items dans le menu ?
Réponse
On peut sélectionner les éléments <li> présents dans le nav avec document.querySelectorAll('nav ul li'), puis utiliser la propriété length et afficher le nombre avec alert(). Exemple :
javascript
function afficherNombreItemsMenu() {
const items = document.querySelectorAll('nav ul li');
alert('Nombre d\'items dans le menu : ' + items.length);
}
addEventListener('load', afficherNombreItemsMenu);