HTML Css Javascript
HTML Css Javascript
Objectifs 3
Environnement de travail 3
Notre page html exemple 3
Outils indispensables 3
Notion de client-serveur 3
C’est parti ! 4
Structure du projet 4
Squelette d’une page web 4
Html : qu’est-ce que c’est ? 5
Rappel rapide sur le Html 5
Flux et type de balises 5
Deux grands groupes de balises 6
Spécificités d'affichage 6
Niveau structurel et imbrications 6
Environnement de travail
Et afin de tous parler de la même chose, nous préconiserons l’utilisation de l’environnement de travail ci-
dessous à installer sur vos postes personnels :
● Google Chrome ou Firefox comme navigateur (ou tout autre navigateur moderne)
● L’éditeur de code Notepad++
Notre page html exemple
Outils indispensables
Les outils de développement de votre navigateur Chrome ou Firefox. Vous les obtiendrez en pressant la
touche F12 dans votre navigateur.
Ils vous permettront de parcourir le code de la page, de consulter les règles CSS, de débugger Javascript et
bien d’autres choses.
Notion de client-serveur
Dans une architecture web classique, nous sommes dans une relation client-serveur. Dans notre petit
projet, la notion de serveur n’a pas d’importance et nous pourrons consulter notre page web directement
dans le navigateur en faisant “fichier > ouvrir” ou en faisant glisser notre fichier html directement dans le
navigateur.
● Compte-nsi
○ index.html
○ images
○ css
■ app.css
○ js
■ app.js
<!DOCTYPE html>
<html lang="fr">
<head>
<!-- l'encodage de votre page : quel jeu de caractères utilisez-vous ? -->
<meta charset="UTF-8">
<title>Formulaire de saisie</title>
</head>
<body>
</body>
</html>
Squelette d’une page web
Le squelette d’une page HTML ressemblera toujours à cela :
</html>
Tim Berners-Lee développe le premier navigateur web (logiciel permettant de lire des pages contenant des
hypertextes), il l'appelle simplement "WorldWideWeb". Il faudra attendre 1993 et l'arrivée du navigateur
web "NCSA Mosaic" pour que le web commence à devenir populaire en dehors du petit monde de la
recherche.
Techniquement le web se base sur trois choses : le protocole HTTP (HyperText Transfert Protocol), les URL
(Uniform Resource Locator) et le langage de description HTML (HyperText Markup Language). Nous
aurons, très prochainement l'occasion de revenir sur ces trois éléments.
Une chose très importante à bien avoir à l'esprit : beaucoup de personnes confondent "web" et "internet".
Même si le "web" "s'appuie" sur internet, les deux choses n'ont rien à voir puisqu'"internet" est un "réseau
de réseaux" s'appuyant sur le protocole IP alors que, comme nous venons de le voir, le web est la
combinaison de trois technologies : HTTP, URL et HTML. D'ailleurs on trouve autre chose que le "web" sur
internet, par exemple, les emails avec le protocole SMTP (Simple Mail Transfert Protocol) et les transferts
de fichiers avec le protocole FTP (File Transfert Protocol).
Le flux correspond à l'écoulement des informations (ou données) dans le processus d'interprétation des
navigateurs. En toute logique, un navigateur commence par le haut de la page, place les éléments (balises)
qu'il rencontre les unes à la suite des autres, de gauche à droite puis de haut en bas.
● Les balises de type BLOC ("block") comme les balises <p>, <ul>, <li>, <div>, <form>, <blockquote>,
<h1>...<h6>, ...
● Les balises de type EN LIGNE ("inline") comme <a>, <strong>, <em>, <span>, <img>, ...
Spécificités d'affichage
● Les blocs se placent toujours l'un en-dessous de l'autre (saut de ligne). Par exemple : une suite de
paragraphes ou une liste. De même par défaut, les boîtes de type bloc seront affichées dans une
succession verticale.
● Les “en ligne” se placent toujours l'un à côté de l'autre. Par exemple : la mise en gras d'une partie de
texte à l'aide de la balise <strong>
● Une balise bloc peut contenir une (ou plusieurs) balise bloc et/ou inline, et peut avoir une
dimension (largeur, hauteur définies)
● Uane balise inline ne peut contenir QUE des balises inline, et n'a pas de dimension à proprement
parler (il n'occupe que la place minimum nécessaire à son contenu)
Une mise en page se fera donc à l'aide de balises blocs, la balise <div> étant la plus indiquée pour cet
usage.
La balise <div> est une balise neutre servant de conteneur, de zone (elle signifie Diviseur). Elle désigne une
boîte rectangulaire invisible que l'on peut configurer à souhait (position, couleurs, taille, ...).
Tout élément HTML possède une boîte rectangulaire qui délimite son contenu. Prenons l’exemple d’un
bloc. <div>Mon bloc</div>
Lorsque le navigateur dessine le bloc, il définit une boîte rectangulaire possédant plusieurs zones
distinctes.
Marge (margin)
Bordure (border)
Ajustement (padding)
Mon bloc
...
<body>
<div>
<div>
...
</div>
</div>
</body>
À l’intérieur nous allons pouvoir rentrer notre contenu : un titre général, un titre pour le formulaire et un
formulaire avec ses différents champs.
Imaginons que notre designer web ait préparé une maquette qui dispose d’un champ par ligne,
comprenant :
● Un libellé de champ.
● Le champ lui-même.
● Un petit texte explicatif.
On décide de délimiter chaque ligne avec une <div>. Comme il s’agit d’un bloc neutre mais de type bloc, il
sera facile de styliser les lignes.
Pour l’instant, entre le libellé, le champ et le texte explicatif, on va à la ligne avec la balise <br>.
<body>
<h1>Création de votre compte NSI</h1>
<form>
<div>
<div>
<h2>Fiche de renseignements</h2>
<div class="div-control">
<label for="nom">Votre nom</label><br>
<input type="text" id="nom" name="nom" aria-describedby="nom_aide"><br>
<small id="nom_aide">Entrez votre nom de famille</small>
</div>
...
Les autres champs
...
</div>
</div>
Vous remarquerez que nous avons pris le temps de positionner un attribut aria-describedby qui concerne
l’accessibilité.
Cette notion est très importante pour rendre votre site ou application accessible au plus grand nombre.
Aux déficients visuels, par exemple.
Un clavier braille pourra comprendre ces instructions et aider dans la navigation.
<body>
<h1>Création de votre compte NSI</h1>
<form>
<div>
<div>
<h2>Fiche de renseignements</h2>
<div>
<input type="radio" id="genre1" name="genre" value="f">
<label for="genre1">Femme</label>
</div>
<div>
<input type="radio" id="genre2" name="genre" value="h">
<label for="genre2">Homme</label>
<div>
<label for="nom">Votre nom</label><br>
<input type="text" id="nom" name="nom" aria-describedby="nom_aide"
class="champs_texte"><br>
<small id="nom_aide">Entrez votre nom de famille</small>
</div>
<div>
<label for="prenom">Votre prénom</label><br>
<input type="text" id="prenom" name="prenom" aria-describedby="prenom_aide"
class="champs_texte"><br>
<small id="prenom_aide">Entrez votre prénom</small>
</div>
<div>
<label for="email">Votre email</label><br>
<input type="text" id="email" name="email" aria-describedby="email_aide"
class="champs_texte"><br>
<small id="email_aide">Entrez votre adresse de messagerie principale</small>
</div>
<div>
<label for="daten">Votre date de naissance</label><br>
<input type="text" id="daten" name="daten" aria-describedby="daten_aide"
class="champs_texte"><br>
<small id="daten_aide">Entrez votre de date de naissance au format jj/mm/aaaa</small>
</div>
<div>
<label for="adresse">Votre adresse</label><br>
<input type="text" id="adresse" name="adresse" aria-describedby="adresse_aide"
class="champs_texte"><br>
<small id="adresse_aide">Entrez votre adresse (numéro + rue)</small>
</div>
<div>
<label for="cp">Votre code postal</label><br>
<input type="text" id="cp" name="cp" aria-describedby="cp_aide"
class="champs_texte"><br>
<small id="cp_aide">Entrez votre code postal (Ex : 44000)</small>
</div>
<div>
<label for="ville">Votre ville</label><br>
<select id="ville" name="ville" aria-describedby="ville_aide" class="champs_texte">
<option>Carquefou</option>
<option>La Chapelle-sur-Erdre</option>
<option>Nantes</option>
<option>Orvault</option>
<div>
<label for="tel">Votre numéro de téléphone</label><br>
<input type="text" id="tel" name="tel" aria-describedby="tel_aide"
class="champs_texte"><br>
<small id="tel_aide">Entrez votre numéro de téléphone</small>
</div>
</div>
</form>
</body>
Comportement du formulaire
Il est peut-être temps de déterminer le comportement de notre formulaire. Pour cela nous allons
positionner 2 attributs sur la balise <form> :
● method="..."
● action="..."
La première détermine la façon dont nos données vont être envoyées pour traitement. Nous avons le choix
entre la valeur get et la valeur post.
Méthode GET
Avec la première méthode, les données du formulaire seront encodées dans une URL. Celle-ci est
composée du nom de la page ou du script à charger avec les données de formulaire empaquetée dans une
chaîne.
Les données sont séparées de l'adresse de la page pas le point d'interrogation et entre elles par le « et
commercial » (&).
Elle est également limitée en taille : env. 2000 caractères.
Méthode POST
Elle envoie un en-tête et un corps de message au serveur. Le corps est généralement constitué des
données entrées dans le champ de formulaire par l'utilisateur.
Les données du formulaire n'apparaissent pas dans l'URL. En conséquence, il n'est pas possible de
récupérer directement les données en JavaScript, il faut ajouter un traitement côté serveur.
Action
L’action détermine à quel endroit (quelle adresse ou quelle url) doivent être expédiées les données. Il s’agit
d’une url pointant vers un autre fichier.
S’il s’agit d’un fichier placé au même endroit que le fichier en cours qui contient le formulaire, il n’est pas
nécessaire de mettre toute l’url mais il faut cependant bien comprendre qu’un aller - retour sera fait sur le
serveur.
Ajoutons donc :
Et créons le fichier traitement.html qui servira uniquement de réception mais nous ne traiterons pas les
données, il faudrait pour cela un langage serveur. Nous verrons cela plus tard.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Traitement des données</title>
</head>
<body>
<h1>Traitement des données</h1>
<div>
Nous avons bien reçu vos données, merci !
</div>
<p>
<a href="index.html">← Retour au formulaire</a>
</p>
</body>
</html>
Vous pouvez tester le formulaire et changer la méthode de ce dernier de POST à GET pour expérimenter la
façon dont sont passés les données.
Dans la barre d'adresse de votre navigateur web vous trouverez, quand vous visitez un site, des choses du
genre : "https://www.w3schools.com/tags/tag_html.asp".
Une URL (Uniform Resource Locator) permet d'identifier une ressource (par exemple un fichier) sur un
réseau.
L'URL indique « l'endroit » où se trouve une ressource sur un ordinateur. Un fichier peut se trouver dans un
dossier qui peut lui-même se trouver dans un autre dossier...
Ici, le chemin vers la ressource sur le serveur : le fichier tag_html.asp qui se trouve dans le dossier tags
lui-même dans le dossier public www.
Structure en arborescence
Comme vous pouvez le constater, la base de l'arbre s'appelle la racine de l'arborescence et se représente
par un /
Pour indiquer la position d'un fichier (ou d'un dossier) dans l'arborescence, il existe 2 méthodes : indiquer
un chemin absolu ou indiquer un chemin relatif. Le chemin absolu doit indiquer « le chemin » depuis la
racine. Par exemple l'URL du fichier fichier3.jpg sera : /dossier2/dossier3/fichier3.jpg
Remarquez que nous démarrons bien de la racine / (attention les symboles de séparation sont aussi des /)
Imaginons maintenant que le fichier fichier1.css fasse appel au fichier fichier3.jpg (comme un fichier HTML
peut faire appel à un fichier CSS). Il est possible d'indiquer le chemin non pas depuis la racine, mais depuis
le dossier (dossier2) qui accueille le fichier1.css, nous parlerons alors de chemin relatif :
dossier3/fichier3.jpg
Remarquez l’absence du / au début du chemin (c'est cela qui nous permettra de distinguer un chemin
relatif et un chemin absolu).
Imaginons maintenant que nous désirions indiquer le chemin relatif du fichier fichier5.svg depuis l'intérieur
du dossier dossier4.
Comment faire ?
Il faut "remonter" d'un "niveau" dans l'arborescence pour se retrouver dans le dossier dossier2 et ainsi
pouvoir repartir vers la bonne "branche" (vers le dossier3). Pour ce faire il faut utiliser 2 points : ..
../dossier3/fichier5.svg
Il est tout à fait possible de remonter de plusieurs "crans" : ../../ depuis le dossier dossier4 permet de
"retourner" à la racine.
À faire vous-même
Remarque : la façon d'écrire les chemins (avec des slash (/) comme séparateurs) est propre aux systèmes
dits « UNIX », par exemple GNU/Linux ou encore Mac OS. Sous Windows, ce n'est pas le slash qui est
utilisé, mais l'antislash (\). Pour ce qui nous concerne ici, les chemins réseau (et donc le web), pas de
problème, c'est le slash qui est utilisé.
Un peu de style
Comme vous avez pu le constater, le formulaire fonctionne mais en termes de navigation et d’expérience
utilisateur, ce n’est pas terrible !
Il va falloir améliorer notre interface en stylisant notre page et le formulaire à l’aide des feuilles de styles
en cascade, les fameuses CSS.
Si ce n’est pas déjà fait, créons un nouveau fichier dans le dossier css que nous nommerons app.css.
Il faut également le lier à la page pour que les instructions que nous allons rentrer dedans soient
interprétées.
<head>
<meta charset="UTF-8">
<title>Formulaire de saisie</title>
<link rel="stylesheet" href="css/app.css">
Les CSS (Cascading Style Sheets en anglais, ou « feuilles de style en cascade ») sont le code utilisé pour
mettre en forme une page web.
Le code
p {
color: yellow;
}
Comme le montre l’exemple ci-dessus, on écrit le CSS en désignant l’élément (ou les éléments) que l’on
veut styliser et on leur applique une déclaration. Cette dernière est composée d’un couple propriété /
valeur.
Ici :
● p est le sélecteur
● color: yellow; la déclaration
● color la propriété
● yellow la valeur
Ce petit bout de code signifie que toutes les balises html <p> auront la couleur jaune. Autrement dit que le
texte de tous les paragraphes sera jaune.
p {
color: yellow;
border: 1px solid red;
}
Il est également possible d’attribuer ces déclarations à plusieurs sélecteurs en les séparant par une virgule.
p, h1 {
color: yellow;
border: 1px solid red;
}
Ici, les balises <p> et <h1> auront le texte de couleur jaune et un bord de 1px et de couleur rouge.
<body>
<h1>Je suis un titre</h1>
Ordre
Le code CSS est lu par le navigateur dans l’ordre où il est écrit, c’est à dire de haut en bas et si une
déclaration vient en contredire une autre, la dernière a préséance.
L’ordre de vos déclarations est donc important.
Les propriétés
● color
● font -family / -size / -weight ...
● width / height
● background -color / -image / -position ...
● display
● position
● margin / padding
● border
● …
● px
● em
● %
● vw et vh
https://www.w3.org/Style/Examples/007/units.fr.html
Un framework CSS est une bibliothèque permettant une conception Web plus simple et plus conforme aux
normes à l'aide du langage Cascading Style Sheets. La plupart de ces cadres contiennent au moins une
grille.
Écrire du css peut vite devenir fastidieux. Sur de gros projets, il est compliqué de garder une organisation
claire du code tout en conservant l’ensemble des bonnes pratiques en termes de design (espacements,
cohérence des couleurs, des fonts, etc.).
De ce constat sont nés il y a un peu plus de 10 ans, des librairies permettant de s’affranchir de ces tâches.
● Bootstrap
● Foundation
● Bulma
● Tailwind
● ...
On veut mettre un bord bleu tout en haut de la page, avoir la même police de caractère sur toute la page
et le fond de la page doit être blanc.
/* Un bord bleu en haut de page */
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
border-top: 10px solid #0069D9;
background-color: white;
margin: 0;
}
Un peu d’espacement, une autre couleur de fond et une ombre portée pour le formulaire :
/* un peu d'espacement et une ombre portée */
form {
margin-top: 1em;
padding: 1em;
background-color: #f6fbff;
Les deux boutons radios sont dans une balise <div> de type block. On veut qu’ils soient sur la même ligne.
On va transformer une balise de type block en balise de type inline. On commence par créer les règles de la
classe « bouton_radio » dans le code CSS :
/* les boutons radios sur une même ligne */
.bouton_radio {
display:inline;
margin-right:2rem;
}
Et on n’oublie pas de définir la classe dans les balises qu’on désire voir hériter de cette classe :
<div class="bouton_radio">
<input type="radio" id="genre1" name="genre" value="f">
<label for="genre1">Femme</label>
</div>
<div class="bouton_radio">
<input type="radio" id="genre2" name="genre" value="h">
<label for="genre2">Homme</label>
</div><br><br>
Avec les deux balises <br> on a créé de l’espace entre les boutons radios et le champs nom.
On va atténuer la couleur des textes d’aide. Ils sont dans des balises <small> qui se trouvent dans la balise
<form>. D’autres balises <small> en dehors du formulaire ne seraient pas affectées par le code suivant :
/* atténuation de la couleur des textes d'aide et un peu d'espace */
form small {
color:grey;
line-height: 2em;
}
On va aussi mettre de l’espace entre tous les champs (attention au point qui indique qu’on a affaire à une
classe :
/* un peu d'espacement entre les lignes du formulaire */
.div-control {
margin-bottom: 1.5rem;
}
Et le bouton enregistrer :
.bouton_enregistrer{
height:35px;
background-color:#157ffb;
border:none;
border-radius: 8px;
font-size: 16px;
color:white;
margin-left:45%;
}
Les grilles
Le module CSS Grid layout (modèle de disposition en grille) est un module de la spécification CSS qui
permet de créer des mises en page en divisant l'espace d'affichage en régions utilisables par une
application ou en définissant des relations de taille, position et d'empilement entre les éléments HTML.
Comme les tableaux, la grille permet d'aligner des éléments sous forme de colonnes et de lignes mais à la
différence des tableaux, la grille n'a pas de structure de contenu. Ainsi, on peut créer de nombreuses mises
en page qui n'auraient pas été possibles avec les tableaux. Ainsi, les éléments fils d'un conteneur en grille
peuvent être positionnés afin qu'ils se chevauchent ou qu'ils se comportent comme des éléments
positionnés.
Source : https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Grid_Layout
Un exemple simple
Dans l'exemple qui suit, on montre comment utiliser une grille avec trois pistes en colonnes pour laquelle
les nouvelles lignes créées mesureront au moins 100 pixels et auront au plus la taille automatique (définie
par leur contenu). Les éléments sont placés sur la grille grâce aux numéros des lignes horizontales et
verticales.
HTML
<div class="wrapper">
CSS
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
.one {
grid-column: 1 / 3;
grid-row: 1;
}
.two {
grid-column: 2 / 4;
grid-row: 1 / 3;
}
.three {
grid-column: 1;
grid-row: 2 / 5;
}
.four {
grid-column: 3;
grid-row: 3;
}
.five {
grid-column: 2;
grid-row: 4;
}
.six {
grid-column: 3;
grid-row: 4;
}
<div class="div-control">
<label for="daten">Votre date de naissance</label><br>
<input type="text" id="daten" name="daten" aria-describedby="daten_aide"
class="champs_texte"><br>
<small id="daten_aide">Entrez votre de date de naissance au format jj/mm/aaaa</small>
</div>
</div>
<div class="two">
<img src="images/illustration.svg">
</div>
On place les classes dans les balises <div> des autres champs en lui affectant le nom de la classe qui lui
convient en fonction de sa position dans la grille.
On pensera à créer une blaise <div> pour le bouton enregistrer afin qu’il soit lui aussi à sa place dans la
grille.
On aimerait aussi que cette image soit alignée au centre horizontalement et verticalement. On place le
code suivant dans le fichier CSS, dans ce qui correspond à la case de la grille où se trouve l’image.
justify-self: center;
align-self: center;
https://fr.wikipedia.org/wiki/JavaScript
Les standards
https://apprendre-a-coder.com/es6/
Maintenant que notre formulaire a pris forme, nous allons ajouter du code pour nous permettre de
contrôler les données que l’utilisateur rentre.
Pour ce faire, nous allons avoir besoin d’intercepter des événements sur le formulaire, clic, focus sur un
champs, etc.
Il est possible d’ajouter ces événements directement dans le formulaire html, par exemple :
Cela fonctionne parfaitement mais nous allons essayer d’avoir de bonnes pratiques en séparant
distinctement :
● la structure : html
● le style : css
● le comportement : javascript (ou autre langage)
Ainsi une modification fondamentale de l’un n’a pas d’incidences sur les autres.
Javascript permet d’accéder au DOM (Document Object Model) et ainsi d’ajouter des gestionnaires
d’événement.
Le DOM (Document Object Model) est une interface pour vos pages web. C'est une API permettant aux
programmes de lire et de manipuler le contenu de la page, sa structure et ses styles.
Le cheminement d'un navigateur partant d'un document source HTML pour finalement afficher une page
stylée et interactive s'appelle le chemin critique du rendu (critical rendering path). Ce processus peut
comporter de nombreuses étapes mais celles-ci peuvent être regroupées en deux grandes étapes. La
première consiste en l'analyse du document par le navigateur pour déterminer ce qui sera finalement
rendu sur la page, et la seconde est le rendu par le navigateur.
La première étape permet de construire l'arbre de rendu (render tree), une représentation sous forme
d'arbre des éléments HTML qui seront rendus sur la page ainsi que leurs styles associés.
Le DOM est une représentation du document HTML source. Comme nous le verrons plus loin, il comporte
quelques différences, mais il s’agit pour l'essentiel d’une conversion de la structure et du contenu du
document HTML en un modèle objet utilisable par divers programmes.
La structure d'objet du DOM est représentée par ce qu'on appelle une "arborescence de noeuds" (node
tree). On l'appelle ainsi parce qu'il peut être considéré comme un arbre qui se ramifie en plusieurs
branches enfants, chacune pouvant avoir des feuilles. Le premier parent est l'élément racine <html> , les
"branches" enfants sont les éléments imbriqués et les "feuilles" sont le contenu des éléments.
Bien que créé à partir du document source HTML, le DOM n'en est pas toujours l'exact reflet. Il peut en
différer dans deux cas :
Le DOM est une interface pour les documents HTML valides. Pendant le processus de création du DOM, le
navigateur peut être amené à corriger des informations invalides.
</html>
En plus d'être une interface permettant de visualiser le contenu d'un document HTML, le DOM peut être
modifié, ce qui en fait une ressource vivante.
Nous pouvons par exemple créer des nœuds supplémentaires via JavaScript.
var newParagraph = document.createElement("p");
var paragraphContent = document.createTextNode("I'm new!");
newParagraph.appendChild(paragraphContent);
document.body.appendChild(newParagraph);
Le DOM sera mis à jour, mais bien entendu notre document source HTML restera inchangé.
À faire vous-même
Nous allons créer un dossier js dans le même dossier contenant le fichier index.html. Dans le dossier js, on
va créer un fichier app.js.
Il nous faut dire au fichier index.html qu’il y a un fichier js dont il faut tenir compte.
L’attribut async permet de charger et lancer l'interprétation de code JavaScript après avoir chargé le code
HTML, c’est à dire sans bloquer le rendu HTML (son affichage à l'écran).
Dans le fichier app.js, commençons par définir une constante qui ciblera un élément en particulier, le
champ email par exemple :
Attention : la méthode getElementById cherche un élément avec l’id spécifiée, cela implique que nous
ayons bien placé un attribut id=”email” dans le code html.
Puis ajoutons un écouteur d’événement en ciblant “la perte du focus” dans le champ. Cela signifie que
l'événement sera déclenché lorsque le champ email perdra le focus (qu’il aura donc reçu au préalable par
un clic ou une navigation clavier avec TAB).
email.addEventListener('blur', <listener>)
function (event) {alert("Vous avez quitté le champ")} : ici, on a créé une fonction qui n’a pas de nom.
Entre les parenthèses, on trouve l’argument de la fonction créée et entre les accolades, on trouve le corps
de la fonction.
Maintenant que nous savons utiliser le gestionnaire d’événements, nous allons manipuler les éléments du
DOM pour en changer l’apparence en fonction du résultat d’un événement.
Pour ce faire, il nous faut repasser par le CSS pour préparer deux classes qui représenteront
(graphiquement parlant) les états d’un champ :
Il y a des règles qui se répètent et que l’on peut écrire dans une seule règle qui s’appliquera aux deux
classes :
Maintenant, sur l’événement « perte du focus », testons la valeur du champ. Si ce dernier est vide
ajoutons-lui la classe CSS « invalid » sinon la classe « valid ».
Nous utiliserons la propriété classList de l’élément du DOM et sa méthode « add » :
Testons cela ! Est-ce que cela fonctionne ? N’y a-t-il pas un problème ?
Maintenant nous allons ajouter un message lorsque le champ passe en “invalid”. Il faut tout d’abord
insérer une nouvelle balise html de cette façon :
<div class="div-control">
<label for="email">Votre email</label><br>
<input type="text" id="email" name="email" aria-describedby="email_aide"
class="champs_texte"><br>
<small class="help" id="email_aide">Entrez votre adresse de messagerie
principale</small>
<small class="error">Votre adresse email est requise</small>
</div>
L’idée étant d’afficher seulement le premier <small> avec la classe “help” (à rajouter si vous ne l’avez pas
fait) lorsque tout est valide et seulement le deuxième si le champ est marqué “invalid”.
On pourra penser au combinateur " ~ " qui permet de sélectionner les nœuds qui suivent un élément et qui
ont le même parent.
Syntaxe : A ~ B
Exemple : p ~ span permettra de cibler les éléments <span> qui suivent (immédiatement ou non) un
élément <p> et qui ont le même élément parent.
On pensera aussi à la propriété display qui peut prendre pour valeur "none" pour ne pas afficher un
élément ou la valeur "block" qui permet à un élément d’être affiché et de type block.
Pour rappel en CSS, c’est la dernière déclaration qui a préséance et donc l’ordre est important.
input ~ .error {
display: none;
}
input.valid ~ .help {
display: block;
}
input.valid ~ .error {
display: none;
}
input.invalid ~ .help {
display: none;
}
input.invalid ~ .error {
display: block;
color: #dc3545;
}
• On rend invisible toutes les balises qui sont de la classe « error » et qui suivent une balise <input> dans
la même balise <div>.
• On rend visible toutes les balises qui ont la classe « help » et qui suivent une balise <input> de classe
« valid » à l’intérieur d’une même balise <div>.
Si tout est correct et comme nous savons mettre en place le gestionnaire d’événement sur un élément en
particulier nous pouvons répéter l’opération pour tous les champs.
Cependant, nous pouvons essayer de rendre notre validation plus générique en associant un événement
sur chaque champ avec une boucle :
On remplace notre constante par une requête qui cible tous les champs de type “text”, “email” :
const inputs = document.querySelectorAll('input[type="text"], input[type="email"]')
item.addEventListener('blur', function() {
if(item.value=='') {
item.classList.remove('valid')
item.classList.add('invalid')
} else {
item.classList.remove('invalid')
item.classList.add('valid')
}
})
})
Notez qu’en ES6, item => {} est la forme courte de function (item) {}
addEventListener() : La méthode addEventListener() installe une fonction à appeler chaque fois que
l'événement spécifié est envoyé à la cible.
Testez le formulaire. Tous les champs devraient être “invalides” s’ils sont vides à la perte du focus et
retrouver leur classe « valid » sinon.
Nous aimerions améliorer notre système de validation en prenant en compte le format attendu dans le
champ.
Nous allons faire appel à un concept nommé “les expressions régulières” dont voici la définition :
https://fr.wikipedia.org/wiki/Expression_r%C3%A9guli%C3%A8re
Les expressions régulières ne sont pas propres à Javascript, elles sont implantées dans la plupart des
langages de programmation et permettent un gain de temps dans la recherche et le remplacement d’une
chaîne de caractères en la décrivant plutôt qu’en l’écrivant.
Tous les bons outils de recherche les implémentent, c’est probablement le cas de votre éditeur de code.
^(0[1-9]|[1-2]\d|3[0-1])\/(0[1-9]|1[0-2])\/([1-2]\d{3})$
Mais une fois que l’on a compris la logique et appris les métacaractères les plus utilisés, il n’y a rien de bien
compliqué pour une puissance inégalée.
• ^ accent circonflexe : marque le début d'une • [ ] les crochets : indique une classe.
chaîne (voir classe aussi).
• - le tiret : indique l'intervalle dans une classe.
• $ dollar : marque la fin d'une chaîne.
Memo :
Memo :
[a-z] : reconnaît les lettres de a à z.
^chat$ : reconnaît chat seul. [Yy]ves : reconnaît un mot avec ou sans majuscule.
^$ : reconnaît chaîne vide. <h[1-6]> : reconnaît une balise de titre par
^ : début de chaîne. exemple.
$ : fin de chaîne.
• | barre verticale : marque l'alternative. • [^... ] au lieu de [...] : indique une classe
complémentée. Reconnaît tout caractère
Memo : qui n'est pas énuméré.
L[y|i]s : reconnaît Lys ou Lis.
Memo :
^(De|Sujet|Date):@ : reconnaît tout ce qui
commence par De:@ ou Sujet:@ ou Date:@. [^0-9] : reconnaît tout ce qui n'est pas des chiffres.
[^1-6] : reconnaît tout sauf les chiffres de 1 à 6.
• ? point d'interrogation : Facultatif, zéro ou • - indique un intervalle dans une classe [0-9].
une occurrence.
• * étoile : Facultatif, zéro, une ou plusieurs Rappel : Le tiret est un métacaractère à l'intérieur
occurrences. d'une classe à condition qu’il exprime bien un
• + signe plus : Obligatoire, une ou plusieurs intervalle.
occurrences. Pour utiliser le tiret en tant que littéral à l'intérieur
• {x} accolade + nombre : Obligatoire d'une classe, soit le placer au début, soit en fin de
restrictif doit apparaître exactement x fois. classe [-0-9] ou [0-9-].
• {x,} accolade + nombre : Obligatoire non À l'extérieur d'une classe le tiret est un caractère
restrictif, doit apparaître au moins x fois. normal.
• {x,y} accolade + nombre : Obligatoire
restrictif, doit apparaître exactement x fois
et maximum y fois
Memo :
a? reconnaît 0 ou 1 a
a* reconnaît 0 ou plusieurs a
a+ reconnaît 1 ou plusieurs a
Les types génériques Les parenthèses
Dans la boucle du gestionnaire d’événements on va isoler « daten » avec un « si » et écrire notre première
expression.
item.addEventListener('blur', function() {
...
if(item.id==='daten') {
let regex = /[0-9\/]+/
// le résultat du test s'écrit dans la console du navigateur
console.log(regex.test(item.value))
}
})
L’expression régulière ci-dessus cherche si la chaîne (item.value) contient un caractère dans l’intervalle 0-9
ou un slash (que l’on est obligé d’échapper avec l’antislash puisqu’il s’agit d’un métacaractère) et cela une
seule fois ou plusieurs, c’est la vocation du métacaractère +.
Si vous tapez une lettre par exemple, le test va échouer. On se rapproche de ce que l’on veut mais si vous
tapez 2 slashs de suite ou une année avec 6 chiffres, le test fonctionnera.
Pour info :
• L'égalité faible (==) effectue une conversion des deux éléments à comparer avant d'effectuer la
comparaison.
• L'égalité stricte (===) effectue la même comparaison mais sans conversion préalable (elle renverra
toujours false si les types des deux valeurs comparées sont différents).
Peut-on améliorer cette expression pour vérifier plus exactement le format de date attendu (jj/mm/aaaa) ?
Vous pouvez faire des essais ici : https://www.regextester.com/
if(item.value=="") {
item.classList.remove('valid')
item.classList.add('invalid')
} else if(!(/^([0-9]{2}\/){2}[0-9]{4}$/.test(item.value))) {
item.classList.remove('valid')
item.classList.add('invalid')
item.parentNode.querySelector('.error').innerHTML = 'Le format n\'est pas valide !'
} else {
item.classList.remove('invalid')
item.classList.add('valid')
}
})
On va donc utiliser la méthode querySelector() qui retourne le premier élément dans le document
correspondant au sélecteur spécifié. item.parentNode.querySelector('.error') indique que l’on va
chercher le premier sélecteur de classe « error » qui se trouve dans le nœud parent du champ où l’on se
trouve. Ici, le nœud parent sera la <div> dans laquelle se trouve l’<input> testé et il n’y a qu’un <small> de
classe « error » dans cette <div>.
La propriété innerHTML permet de remplacer le contenu de ce qui se trouve dans la balise <small> par ce
qui est entre quote.
Notre système semble fonctionner mais nous avons de multiples formats à tester comme le téléphone, le
code postal qui ne fonctionneront pas avec l’expression régulière de la date de naissance.
À faire vous-même :
Pour pallier à ce problème nous allons ajouter un attribut (nommé data-validation mais ceci est arbitraire,
ça pourrait être autre chose) aux éléments html pour dire à javascript quel format il devra tester :
• data-validation="email"
• data-validation="date"
• data-validation="telephone"
• data-validation="cp"
Exemple :
<input type="text" id="daten" name="daten" aria-describedby="daten_aide" class="champs_texte"
data-validation="date"><br>
Ensuite nous allons préparer nos différentes expressions régulières dans un objet, de cette façon :
const types = {
email: /^[0-9a-z._-]+@{1}[0-9a-z.-]{2,}[.]{1}[a-z]{2,5}$/,
date: /^([0-9]{2}\/){2}[0-9]{4}$/,
telephone: /^0[1-7]{1}[0-9]{8}$/,
cp: /^[0-9]{5}$/
}
inputs.forEach(item => {
item.addEventListener('blur', function() {
if(item.value=="") {
item.classList.remove('valid')
item.classList.add('invalid')
} else if(!regex.test(item.value)) {
item.classList.remove('valid')
item.classList.add('invalid')
item.parentNode.querySelector('.error').innerHTML = 'Le format n\'est pas valide !'
} else {
item.classList.remove('invalid')
item.classList.add('valid')
}
})
Il reste un cas de figure à traiter. Certains champs, tel que le nom ou le prénom n’ont pas de format à
tester (pas d’attribut data-validation), la ligne :
let regex = types[ item.getAttribute('data-validation') ]
va donc échouer pour eux, vous pouvez le vérifier dans la console.
Pour éviter cela, nous allons ajouter un test sur cette ligne avec l’opérateur ternaire :
let regex = item.hasAttribute('data-validation') ? types[ item.getAttribute('data-validation') ]
: /./
qui signifie que la variable regex aura toujours une valeur : soit l’expression récupérée dans l’objet types
soit l’expression /./ qui signifie n’importe quel caractère.
L'opérateur (ternaire) conditionnel est le seul opérateur JavaScript qui comporte trois opérandes. Cet
opérateur est fréquemment utilisé comme raccourci pour la déclaration de Instructions/if . . . else.
Il nous reste à gérer l’envoi du formulaire. En effet nous pouvons constater que malgré des erreurs nous
pouvons quand même envoyer le formulaire !
Il faudrait en quelque sorte intercepter ce dernier avant qu’il soit envoyé pour vérifier que toutes les
données ont été validées.
La solution de facilité est souvent de vouloir placer un événement sur le bouton « envoyer » mais ce n’est
pas une bonne pratique car le formulaire peut être validé d’une autre manière : la touche entrée sur un
champ texte par exemple.
En revanche un événement existe pour les éléments de type « form », il s’agit de « submit ».
Ajoutons également un bloc qui affichera un message d’erreur si le formulaire contient des erreurs. On
l’insère dans une ligne de la grille juste avant le bouton « enregistrer » :
<div class="seven">
<div class="alert" id="msg">
Le formulaire contient des erreurs !
</div>
</div>
<div class="eight">
<button type="submit" class="bouton_enregistrer">Enregistrer</button>
</div>
On pense à modifier la grille en conséquence (on ajoute un 5ème « auto » pour la 5ème ligne) :
/* Création de notre grille */
.grille {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: auto;
grid-auto-rows: auto, auto, auto, auto, auto;
}
Maintenant nous devons chercher si le formulaire contient des erreurs. Si oui, nous afficherons le message.
Pour ce faire, il suffit de chercher la classe CSS « invalid ». Si elle est présente au moins une fois c’est que le
formulaire contiendra des erreurs.
// on regarde si on a des erreurs
const error = document.querySelector('.invalid')
Le formulaire est visiblement envoyé quoiqu’il arrive. En effet l’événement « submit » est appelé juste
avant l’envoi mais ne gère aucunement l’envoi ou non.
En revanche, lorsqu’on écoute un événement, on a accès à ce dernier (event => {) et une méthode existe
pour stopper le cheminement habituel de celui-ci :
//on affiche le message d'erreur
if(error) {
msg.classList.add('show')
event.preventDefault()
}
Testons à nouveau.
Nous devrions effectuer la validation de chaque champ, ce que l’on fait dans chaque événement « blur » !
Puisque nous avons déjà effectué cela, il ne sert à rien de le réécrire, on va le réutiliser.
Pour ce faire, on déporte le contenu de l’événement « blur » dans une fonction que l’on pourra réutiliser :
function validItem(item) {
if(item.value=="") {
item.classList.remove('valid')
item.classList.add('invalid')
} else if(!regex.test(item.value)) {
item.classList.remove('valid')
item.classList.add('invalid')
item.parentNode.querySelector('.error').innerHTML = 'Le format n\'est pas valide !'
} else {
item.classList.remove('invalid')
item.classList.add('valid')
}
}
Et on termine par effectuer la validation de tous les champs dans l’événement “submit” :
// envoi du formulaire
const form = document.getElementById('formId')
// on effectue la validation
inputs.forEach(item => {
validItem(item)
})
...