Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
17 KiB
🛠️ Page Services — relecture & prochaine étape
Salut Guillaume ! 👋
Ta page services avance super bien : le tableau des prestations est clair, les boutons de navigation sont stylés, et tu as même ajouté une petite icône SVG sur le bouton « retour ». Bravo, c'est du bon travail ! 🎉
Ce guide a deux objectifs :
- Te montrer 3 bugs à corriger (avec l'explication, pas juste la solution).
- T'apprendre à définir et limiter une
<textarea>proprement. - Te lancer sur ta première mission JavaScript 🚀 (un vrai morceau de programmation, dans le thème FleetZen).
🐛 Bug 1 — Ton formulaire ne contient (presque) aucun champ
Regarde bien comment tu as écrit ton formulaire :
<form action="" method="post">Si vous avez des suggestions :</form> <!-- ❌ le </form> ferme ICI -->
<ul>
<li><input type="text" ... /></li> <!-- ces champs sont DEHORS du <form> ! -->
<li><textarea ...></textarea></li>
<button type="submit">Envoyer le message</button>
</ul>
Le problème : tu ouvres <form> puis tu le refermes (</form>) tout de suite, juste après la phrase d'intro. Du coup, tous tes champs (<input>, <textarea>, le bouton « Envoyer ») se retrouvent à l'extérieur du formulaire.
👉 Conséquence : le jour où le formulaire enverra vraiment les données, rien ne partira, parce que le navigateur n'envoie que ce qui est à l'intérieur des balises <form>…</form>.
✅ La correction : le <form> doit englober TOUS les champs
<form action="" method="post">
<p>Si vous avez des suggestions :</p>
<ul>
<li>
<label for="name">Nom :</label>
<input type="text" id="name" name="user_name" placeholder="Dupond" />
</li>
<li>
<label for="mail">E-mail :</label>
<input type="email" id="mail" name="user_mail" placeholder="dupond@gmail.com" />
</li>
<li>
<label for="msg">Message :</label>
<textarea id="msg" name="user_message" placeholder="Je pense qu'il faudrait..."></textarea>
</li>
</ul>
<div id="boutton_formulaire">
<button type="submit">Envoyer le message</button>
</div>
</form>
💡 À retenir : un
<form>est une boîte. Tout ce qu'on veut envoyer (champs + bouton « Envoyer ») doit être dedans, entre<form>et</form>.
🐛 Bug 2 — Un dégradé qui ne s'affiche pas (parenthèses oubliées)
Dans style_services.css, ton bouton de la page courante .x_to_x :
.x_to_x {
background-image:
linear-gradient to top left, /* ❌ il manque les parenthèses */
rgb(0 0 0 / 0.2),
rgb(0 0 0 / 0.2) 30%,
transparent;
}
linear-gradient est une fonction : comme en maths, ses arguments vont entre parenthèses. Sans elles, le navigateur ne comprend pas la ligne et ignore tout le dégradé. C'est pour ça que ce bouton n'a pas le même rendu que les autres.
✅ Correction (compare avec ta classe .styled qui, elle, est correcte)
.x_to_x {
background-image: linear-gradient(
to top left,
rgb(0 0 0 / 0.2),
rgb(0 0 0 / 0.2) 30%,
transparent
);
}
💡 À retenir :
linear-gradient( … ),rgb( … ),calc( … )… toutes les fonctions CSS prennent leurs arguments entre parenthèses. Une fonction sans parenthèses = ligne ignorée.
🎯 Bug 3 — L'icône SVG n'est pas centrée dans le bouton « retour »
Ton bouton mélange une image (le SVG) et du texte côte à côte :
<button class="retour"><svg ...></svg>retour</button>
Le souci : par défaut, une icône SVG se pose sur la ligne du texte (sur la « ligne de base », comme une lettre). Du coup elle paraît trop haute ou trop basse, et ton line-height: 2.5 accentue le décalage. L'icône et le mot « retour » ne sont pas alignés sur leur milieu.
✅ La solution : transformer le bouton en boîte flex
.retour {
display: inline-flex; /* le bouton range ses enfants (icône + texte) */
align-items: center; /* ↕ les centre verticalement, sur le même milieu */
justify-content: center; /* ↔ les centre horizontalement */
gap: 6px; /* un petit espace entre l'icône et le mot */
line-height: normal; /* on enlève le 2.5 qui déréglait l'alignement */
/* ...garde le reste de tes styles (couleur, border-radius, etc.) */
}
align-items: center, c'est exactement le flex que tu connais déjà (tu l'utilises sur #buttons et .align_x). Ici on l'applique au bouton lui-même, parce que c'est lui le parent qui contient l'icône et le texte.
💡 À retenir : dès qu'un bouton (ou n'importe quelle boîte) contient une icône + du texte à aligner, passe-le en
display: inline-flex; align-items: center;. C'est le réflexe pro pour centrer proprement une icône avec du texte.
⚠️ Ça vaut aussi pour ton nouveau bouton « Envoyer le message » : tu viens d'y ajouter l'icône SVG « avion en papier », il a donc exactement le même décalage. Applique-lui la même recette flex (par ex. une classe commune
.btn-iconeque tu mets sur les deux boutons, plutôt que de répéter le CSS).
📐 La leçon du jour : définir et limiter une <textarea>
Pour l'instant ta <textarea> est « brute » :
<textarea id="msg" name="user_message"></textarea>
Par défaut elle est petite, et surtout l'utilisateur peut écrire un roman de 10 000 mots sans aucune limite. On va apprendre à la cadrer.
1️⃣ Lui donner une taille de départ : rows et cols
<textarea id="msg" name="user_message"
rows="6" cols="40"></textarea>
rows= nombre de lignes visibles (la hauteur).cols= nombre de colonnes = caractères en largeur.
🔎
rows/colsne sont pas des pixels : c'est « combien de lignes/caractères je vois ». C'est la taille de départ.
2️⃣ Limiter le nombre de caractères : maxlength
<textarea id="msg" name="user_message"
rows="6" cols="40"
maxlength="500"></textarea>
maxlength="500" = l'utilisateur ne peut pas taper plus de 500 caractères. C'est le navigateur qui bloque tout seul, sans JavaScript. 👍
3️⃣ Empêcher (ou cadrer) le redimensionnement : resize en CSS
Tu as remarqué la petite poignée en bas à droite d'une textarea ? L'utilisateur peut l'étirer et casser ta mise en page. On contrôle ça en CSS :
#msg {
resize: vertical; /* il peut agrandir en hauteur seulement (recommandé) */
/* resize: none; pour interdire complètement le redimensionnement */
width: 100%; /* la largeur, on la pilote en CSS, pas avec cols */
min-height: 120px;
}
💡 À retenir :
rows/cols→ taille de départ (lignes / caractères).maxlength→ limite le nombre de caractères (le navigateur bloque tout seul).resize(CSS) → contrôle la poignée d'étirement (vertical,horizontal,none,both).- Astuce de pro : on règle souvent la largeur en CSS (
width) et on garderowspour la hauteur de départ.
4️⃣ 👉 Votre question : laisser l'utilisateur redimensionner, mais entre un min et un max
Tu as choisi pour l'instant :
#msg {
resize: none; /* la poignée est désactivée : l'utilisateur ne peut RIEN étirer */
width: 200px;
}
resize: none est un choix valable (la zone est verrouillée). Mais vous m'avez demandé l'inverse : autoriser l'agrandissement, tout en empêchant que ça parte trop grand ou trop petit. La recette tient en 2 idées qui se combinent :
resizedit dans quelle direction on peut étirer.min-*/max-*disent jusqu'où on peut aller.
#msg {
resize: vertical; /* on autorise l'étirement, mais en HAUTEUR seulement */
min-height: 80px; /* impossible de la réduire en dessous de 80px */
max-height: 300px; /* impossible de l'agrandir au-delà de 300px */
width: 100%; /* largeur pilotée en CSS, pas par l'utilisateur */
}
Avec ça : l'utilisateur attrape la poignée, tire vers le bas pour avoir plus de place… mais la zone refuse de dépasser 300px et de descendre sous 80px. Ta mise en page reste protégée. 🎉
Et si on veut autoriser les deux sens (vertical et horizontal) ?
Même principe, mais avec 4 bornes : 2 pour la hauteur, 2 pour la largeur. On passe resize à both :
#msg {
resize: both; /* la poignée étire en hauteur ET en largeur */
min-height: 80px; /* bornes verticales */
max-height: 300px;
min-width: 200px; /* bornes horizontales */
max-width: 100%; /* ne dépasse jamais la largeur du parent → pas de scroll */
}
Chaque direction a sa paire min/max :
- hauteur →
min-height/max-height - largeur →
min-width/max-width
L'utilisateur peut alors étirer dans tous les sens, mais jamais au-delà de ces 4 limites.
⚠️ Le piège classique en horizontal : mets
max-width: 100%(et pas une grande valeur enpx), sinon l'utilisateur peut élargir la zone plus que l'écran et faire réapparaître la barre de défilement horizontale (souviens-toi du bug du scroll 😉).
Récap des valeurs de resize :
resize |
Ce que l'utilisateur peut étirer |
|---|---|
none |
rien (verrouillé) — ton choix actuel |
vertical |
la hauteur seulement (le plus confortable pour un message) |
horizontal |
la largeur seulement |
both |
les deux |
💡 À retenir :
resize= la direction autorisée.min-height/max-height(etmin-width/max-width) = les bornes. Les deux ensemble = un redimensionnement libre mais encadré.
🚀 Ta première mission JavaScript : le simulateur de devis FleetZen
Jusqu'ici tu as fait du HTML (la structure) et du CSS (l'apparence). Le JavaScript, c'est la 3ᵉ couche : le comportement, ce qui réagit quand on clique. 🧠
On va le faire en mode tuto : tu construis petit bout par petit bout, et tu testes à chaque étape. Ne copie pas tout d'un coup — avance palier par palier, c'est comme ça qu'on comprend. 🙂
Objectif final : un petit simulateur où le client choisit combien de véhicules il a, et le prix total se met à jour tout seul, sans recharger la page.
🪜 Étape 0 — Découvrir la console (ton meilleur ami)
Avant tout : ouvre ta page dans le navigateur, puis appuie sur F12 → onglet Console. C'est là que le JS « parle ».
Crée un fichier css/devis.js avec une seule ligne :
console.log("Mon fichier JS est bien chargé !");
Et branche-le en bas du <body>, juste avant </body> :
<script src="css/devis.js"></script>
🔎 Pourquoi à la fin du body ? Parce que le JS s'exécute de haut en bas. S'il se lance avant que le HTML existe, il ne trouve rien à manipuler. À la fin, tout le HTML est déjà là. 👍
✅ Teste maintenant : recharge la page. Tu dois voir ton message dans la Console. Si oui, ton JS est branché. Sinon, vérifie le chemin du src.
🪜 Étape 1 — Ajouter le HTML du simulateur
Sous ton tableau, ajoute :
<section class="simulateur">
<h2>Estimez votre devis</h2>
<p>Nombre de véhicules dans votre flotte :</p>
<button id="moins" type="button">−</button>
<span id="nb_vehicules">1</span>
<button id="plus" type="button">+</button>
<p>Total géolocalisation : <strong id="total">30</strong> €/mois</p>
</section>
✅ Teste maintenant : tu dois voir les boutons − et + et le chiffre 1. Pour l'instant ils ne font rien, c'est normal : on n'a pas encore écrit le comportement.
🔎 Remarque les
id(moins,plus,nb_vehicules,total). Unidest une étiquette unique : c'est par là que le JS va « attraper » chaque élément à l'étape suivante.
🪜 Étape 2 — Attraper les éléments depuis le JS
Dans devis.js, remplace ton console.log de test par :
// "Attraper" un élément du HTML grâce à son id
const boutonPlus = document.getElementById("plus");
console.log(boutonPlus); // pour vérifier qu'on l'a bien trouvé
✅ Teste maintenant : dans la Console tu dois voir s'afficher ton <button id="plus">. Si tu vois null, c'est que l'id ne correspond pas → vérifie l'orthographe.
🧩 Idée clé n°1 — SÉLECTIONNER.
document.getElementById("plus")veut dire : « dans le document, va me chercher l'élément dont l'id est plus ». On le range dans une variable pour s'en servir après.
🪜 Étape 3 — Réagir à un clic
Ajoute en dessous :
boutonPlus.addEventListener("click", function () {
console.log("Clic sur + détecté !");
});
✅ Teste maintenant : clique sur le bouton +. À chaque clic, un message apparaît dans la Console. 🎉
🧩 Idée clé n°2 — RÉAGIR.
addEventListener("click", … )se lit : « écoute les clics sur ce bouton, et quand il y en a un, exécute cette fonction ». La fonction entre{ }est le « que faire quand ça arrive ».
🪜 Étape 4 — Faire vraiment changer le compteur
Maintenant on remplace le console.log par une vraie logique. Voici le fichier complet :
// 1. On attrape tous les éléments dont on a besoin
const affichageNb = document.getElementById("nb_vehicules");
const affichageTotal = document.getElementById("total");
const boutonPlus = document.getElementById("plus");
const boutonMoins = document.getElementById("moins");
const PRIX_PAR_VEHICULE = 30; // 30 €/véhicule/mois (cf. ton tableau)
let nbVehicules = 1; // "let" = une variable qui va CHANGER
// 2. Une fonction qui réécrit l'affichage à partir de nbVehicules
function mettreAJour() {
affichageNb.textContent = nbVehicules;
affichageTotal.textContent = nbVehicules * PRIX_PAR_VEHICULE;
}
// 3. Au clic sur "+", on augmente puis on réaffiche
boutonPlus.addEventListener("click", function () {
nbVehicules = nbVehicules + 1;
mettreAJour();
});
// 4. Au clic sur "−", on diminue (mais jamais en dessous de 1)
boutonMoins.addEventListener("click", function () {
if (nbVehicules > 1) {
nbVehicules = nbVehicules - 1;
}
mettreAJour();
});
🧩 Idée clé n°3 — MODIFIER.
element.textContent = …réécrit le texte affiché en direct, sans recharger la page. C'est ça, la magie du JS.🔎 Pourquoi une fonction
mettreAJour()? Pour ne pas se répéter : le+et le−ont tous les deux besoin de réafficher. On écrit le « comment afficher » une seule fois et on l'appelle aux deux endroits (c'est le même esprit que le composant réutilisable, juste après 👇).
✅ Teste maintenant : clique 3 fois sur + → tu dois lire 4 véhicules et 120 €. Clique sur − jusqu'en bas → ça doit bloquer à 1. Si oui : bravo, tu viens d'écrire ta première vraie logique en JavaScript ! 🎉
🎯 Défis bonus (dans l'ordre de difficulté) :
- Empêcher aussi de dépasser 100 véhicules.
- Afficher le total avec un séparateur de milliers.
- Ajouter un 2ᵉ service (ex. consommation à 15 €) et calculer un total combiné.
♻️ Bonus — La notion de composant réutilisable (la navbar)
Regarde : la barre du haut (#nav_barre) est recopiée à l'identique dans index.html, services.html et contact.html. Le jour où tu changes un lien, tu dois le changer dans 3 fichiers → source d'erreurs (d'ailleurs, vérifie tes liens entre pages 😉).
C'est le problème que résolvent les composants : on écrit la navbar une seule fois, et on la réutilise partout.
Première approche très simple en JavaScript : une fonction qui génère la navbar.
function creerNavbar() {
return `
<div id="nav_barre" class="shadow">
<h1>FleetZen</h1>
<div id="buttons">
<a href="contact.html"><button class="styled">contact</button></a>
<a href="services.html"><button class="styled">services</button></a>
<a href="../index.html"><button class="retour">retour</button></a>
</div>
</div>`;
}
// On l'injecte tout en haut du body :
document.body.insertAdjacentHTML("afterbegin", creerNavbar());
✅ Teste : enlève la navbar écrite « en dur » dans le HTML, garde ce JS, recharge → la navbar réapparaît, générée par la fonction. Maintenant le jour où tu changes un lien, tu le changes à un seul endroit pour toutes les pages.
💡 À retenir : un composant = un bout d'interface écrit une fois et réutilisé partout. C'est l'idée centrale derrière React, Vue, etc. — mais ça commence par une simple fonction comme celle-ci.
✅ Ta checklist
- Remettre tous les champs dans le
<form>(Bug 1) - Réparer le dégradé de
.x_to_xavec les parenthèses (Bug 2) - Centrer l'icône SVG des boutons « retour » et « Envoyer » avec
inline-flex(Bug 3) - Ajouter
rows,maxlength+ redimensionnement borné (resize: vertical+min/max-height) - Faire marcher le simulateur de devis en JavaScript
- (Bonus) Transformer la navbar en composant réutilisable
- Commits petits et clairs, puis
git push
Pose-moi toutes tes questions. Tu gères ! 🚗💨