Tu as sûrement déjà rempli un formulaire où la liste des villes change selon le pays choisi. Ce sont des listes déroulantes liées (ou dynamiques) : la seconde se remplit en JavaScript en fonction de la première. Un petit projet parfait pour apprendre à manipuler le DOM et les évènements. Durée : environ 30 minutes.
Ce que tu vas construire
- une liste de pays remplie automatiquement à partir des données ;
- une liste de villes désactivée tant qu'aucun pays n'est choisi, puis remplie avec les villes du pays ;
- un message qui confirme le choix, annoncé par les lecteurs d'écran.
Crée un dossier avec index.html, style.css et script.js.
1. La structure HTML
Deux <select>, chacun avec son <label> : le label est lié au champ par l'attribut for, ce qui rend le formulaire accessible et agrandit la zone cliquable. La liste des villes commence désactivée (disabled).
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Listes déroulantes liées</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<form class="form">
<label for="country">Pays</label>
<select id="country" name="country">
<option value="">Choisis un pays</option>
</select>
<label for="city">Ville</label>
<select id="city" name="city" disabled>
<option value="">Choisis d'abord un pays</option>
</select>
<p class="result" aria-live="polite"></p>
</form>
<script src="script.js"></script>
</body>
</html>
L'attribut aria-live="polite" sur le paragraphe de résultat demande aux lecteurs d'écran d'annoncer son contenu quand il change.
2. Un peu de style
.form {
display: grid;
gap: 0.5rem;
max-width: 20rem;
margin: 2rem auto;
font-family: system-ui, sans-serif;
}
select {
padding: 0.5rem;
font: inherit;
border: 1px solid #aaa;
border-radius: 0.375rem;
}
select:disabled {
opacity: 0.5;
}
.result {
min-height: 1.5em;
font-weight: 600;
}
3. Le JavaScript
// Les données : chaque pays et ses villes. Plus tard, elles pourront venir d'un fichier JSON.
const citiesByCountry = {
France: ['Paris', 'Marseille', 'Lyon', 'Toulouse'],
Belgique: ['Bruxelles', 'Liège', 'Namur'],
Canada: ['Montréal', 'Québec', 'Ottawa'],
};
const countrySelect = document.getElementById('country');
const citySelect = document.getElementById('city');
const result = document.querySelector('.result');
// Crée une balise <option> ; new Option(texte, valeur) évite d'écrire du HTML à la main.
function fillSelect(select, values, placeholder) {
select.replaceChildren(new Option(placeholder, ''));
values.forEach((value) => select.add(new Option(value, value)));
}
fillSelect(countrySelect, Object.keys(citiesByCountry), 'Choisis un pays');
countrySelect.addEventListener('change', () => {
const cities = citiesByCountry[countrySelect.value] ?? [];
fillSelect(citySelect, cities, cities.length ? 'Choisis une ville' : "Choisis d'abord un pays");
citySelect.disabled = cities.length === 0;
result.textContent = '';
});
citySelect.addEventListener('change', () => {
result.textContent = citySelect.value ? `Tu as choisi ${citySelect.value} (${countrySelect.value}).` : '';
});
Ce qu'il faut retenir :
- Les données sont séparées du code : l'objet
citiesByCountryassocie chaque pays à ses villes. Pour ajouter un pays, il suffit d'ajouter une ligne ; aucune autre partie du code ne change. new Option(texte, valeur)crée une option proprement, sans concaténer du HTML (source classique de bugs et de failles).replaceChildren()vide la liste en une seule instruction avant de la remplir à nouveau.?? [](l'opérateur de coalescence) renvoie une liste vide quand le pays choisi est l'option « Choisis un pays ».
4. Tester
- Au chargement, la liste des villes doit être grisée.
- Choisis « Belgique » : la liste propose Bruxelles, Liège et Namur.
- Reviens sur « Choisis un pays » : la liste des villes se désactive et le message s'efface.
Pour aller plus loin : charger les données depuis un fichier JSON
Dans un vrai projet, les données viennent d'un fichier ou d'une API. Place-les dans villes.json :
{
"France": ["Paris", "Marseille", "Lyon", "Toulouse"],
"Belgique": ["Bruxelles", "Liège", "Namur"]
}
Puis, au début du script, remplace l'objet écrit en dur par un appel à fetch :
const response = await fetch('villes.json');
const citiesByCountry = await response.json();
Attention : fetch ne fonctionne pas en ouvrant le fichier directement (file://). Lance un petit serveur local, par exemple avec l'extension Live Server de VS Code, ou la commande php -S localhost:8000 dans le dossier du projet.
Autres idées : un troisième niveau (pays, région, ville), ou un champ de recherche qui filtre les villes au fil de la frappe.