COURS GRATUIT • FRANÇAIS
Apprendre JavaScript et le développement front-end en français
Une référence complète pour apprendre HTML, CSS, JavaScript et TypeScript, puis découvrir les principaux frameworks front-end : React, Vue.js et Angular.
Chaque notion est expliquée avec des exemples concrets, des fiches de référence, du code prêt à tester, un éditeur JavaScript et des exercices pour progresser par la pratique.
Une progression complète du HTML aux frameworks modernes
APPRENTISSAGECe site rassemble les connaissances essentielles pour comprendre le développement front-end moderne, depuis la structure d'une page HTML jusqu'à la création d'interfaces avec React, Vue.js ou Angular.
Si tu débutes, commence par
HTML,
puis découvre
CSS
avant de passer aux
bases de JavaScript.
Tu apprendras ensuite à manipuler le
DOM,
travailler avec les tableaux,
comprendre l'asynchrone,
utiliser
fetch(),
les Promises et
async/await,
puis écrire du JavaScript moderne.
Une fois les bases maîtrisées, la partie TypeScript permet d'ajouter un système de types à JavaScript. Les chapitres React, Vue.js et Angular montrent ensuite comment ces connaissances sont utilisées dans de véritables applications front-end.
L'objectif n'est pas seulement de mémoriser des propriétés ou des méthodes : chaque fiche cherche à expliquer à quoi sert la notion, comment elle fonctionne et quand l'utiliser.
Structure de base
HTMLLes éléments fondamentaux d'un document HTML : déclaration du document, élément racine, métadonnées, contenu visible, conteneurs génériques et attributs globaux.
<!doctype html>
doit apparaître au début
d'un document HTML.
Ce n'est pas un élément HTML.
Il s'agit d'une déclaration permettant notamment au navigateur d'utiliser son mode de rendu standard plutôt qu'un ancien mode de compatibilité appelé quirks mode.
Avec le HTML moderne, la déclaration est volontairement simple :
<!doctype html>.
Il n'est plus nécessaire d'indiquer un numéro de version HTML.
<html>
est l'élément racine
du document.
Il contient normalement :
<head>
et
<body>.
L'attribut
lang
indique la langue principale
du document.
Exemple :
lang="fr".
Cette information aide notamment les technologies d'assistance, les outils de traduction et le traitement linguistique.
L'attribut
dir
peut contrôler le sens d'écriture :
ltr,
rtl
ou
auto.
<head>
contient les métadonnées
et ressources associées au document.
On y trouve notamment :
<meta><title><link><style><script>
dans certains cas.
Le contenu du
<head>
ne constitue pas le contenu principal
visible de la page,
même si certaines informations
comme le
<title>
peuvent apparaître dans l'interface
du navigateur.
<body>
représente le contenu
du document HTML.
On y place notamment :
textes,
images,
formulaires,
tableaux,
navigation,
contenu principal,
médias.
Un document possède un seul élément
<body>.
Les scripts JavaScript modernes peuvent souvent être chargés avec
defer
ou
type="module"
afin de ne pas bloquer
inutilement l'analyse du HTML.
<div>
est un conteneur générique
sans signification sémantique
particulière.
Il est utile lorsque aucun élément HTML plus précis ne correspond au rôle du contenu.
Exemple :
wrapper de layout,
groupe destiné au CSS,
conteneur technique.
Les navigateurs affichent généralement un
<div>
comme une boîte de type block
par défaut,
mais ce comportement visuel
peut être modifié avec CSS.
Si le contenu possède une vraie signification, préférez par exemple :
<section>,
<article>,
<nav>,
<aside>.
<span>
est un conteneur générique
destiné au contenu de type
phrasing content.
Il est utile lorsqu'on souhaite cibler une portion de texte avec CSS ou JavaScript sans lui donner une signification particulière.
Il est affiché comme élément inline par défaut dans les navigateurs, mais CSS peut modifier son comportement visuel.
Si une balise sémantique plus précise correspond au contenu, utilisez-la plutôt que
<span>.
Ils ne sont normalement pas affichés dans la page.
Ils sont utiles pour :
identifier une grande section,
documenter une structure,
laisser une note technique.
Attention : un commentaire HTML reste visible dans le code source envoyé au navigateur.
N'y placez donc jamais de mot de passe, clé privée ou information secrète.
id : identifiant unique dans le document.
class : permet de regrouper plusieurs éléments.
data-* : stocke des données personnalisées destinées notamment à JavaScript.
hidden : indique qu'un élément n'est actuellement pas pertinent pour l'affichage.
lang : précise la langue du contenu.
title : fournit une information consultative, mais ne doit pas être utilisé comme seul moyen de transmettre une information essentielle.
<html>.
Le
<head>
contient les métadonnées.
Le
<body>
contient la structure
et le contenu de la page.
À l'intérieur du body, préférez les éléments sémantiques lorsqu'ils correspondent réellement au rôle du contenu.
Texte & Titres
HTMLLes éléments textuels HTML donnent une structure et une signification au contenu : titres, paragraphes, emphase, citations, code, abréviations, modifications et annotations.
<h1>
à
<h6>.
<h1>
représente le niveau le plus élevé.
En pratique, une page devrait généralement avoir un
<h1>
principal décrivant son sujet.
Utilisez ensuite
<h2>,
<h3>...
selon la hiérarchie réelle.
Évitez de sauter inutilement des niveaux.
Ne choisissez jamais un niveau de titre uniquement pour sa taille : utilisez CSS pour l'apparence.
<p>
représente un paragraphe.
Les navigateurs lui appliquent généralement des marges par défaut, mais ces espacements sont du style CSS navigateur et peuvent être modifiés.
Utilisez un nouveau
<p>
lorsqu'une nouvelle unité
de texte commence.
N'utilisez pas une succession de
<br>
simplement pour créer
de l'espace vertical :
utilisez CSS.
<strong>
indique qu'une partie du contenu
possède une importance particulière.
Il peut notamment signaler :
importance,
gravité,
urgence,
avertissement important.
Les navigateurs affichent généralement ce contenu en gras, mais ce n'est pas sa raison d'être.
Si vous souhaitez uniquement obtenir du gras visuellement, utilisez plutôt CSS.
<em>
représente une emphase
particulière dans la phrase.
Cette emphase peut modifier le sens ou l'intention de la phrase.
Les navigateurs l'affichent généralement en italique, mais son objectif est sémantique.
Pour un simple effet italique, utilisez CSS.
<b>
et
<i>
ne sont pas simplement
« gras » et « italique ».
<b> attire l'attention sur du contenu sans lui attribuer une importance supplémentaire.
Exemple : mot-clé, nom de produit, terme à repérer.
<i> représente du texte dans une voix ou un registre différent.
Exemples : terme technique, expression étrangère, nom scientifique, pensée, idiome.
Pour une simple apparence gras ou italique, utilisez CSS.
Exemple classique : signaler une faute ou une annotation particulière.
Évitez de souligner du texte ordinaire, car le soulignement est fortement associé aux liens.
<s> représente un contenu qui n'est plus exact ou n'est plus pertinent.
Exemple : ancien prix.
Pour représenter une modification du document, utilisez plutôt
<del>.
<mark>
représente un passage
particulièrement pertinent
dans le contexte actuel.
Exemple typique :
mettre en évidence un terme recherché dans un résultat.
Le navigateur utilise souvent un fond jaune par défaut, mais son apparence peut être personnalisée avec CSS.
<small>
représente des commentaires secondaires
ou du « petit texte ».
Exemples :
mentions légales,
copyright,
conditions,
avertissements secondaires.
Son sens ne signifie pas simplement « mets ce texte plus petit ».
Pour modifier uniquement la taille d'un texte, utilisez CSS.
<code>
représente un fragment
de code informatique.
Il peut être utilisé directement dans une phrase.
<pre>
représente du texte préformaté.
Les espaces et retours à la ligne présents dans le code source sont conservés.
Pour un bloc de code, on combine souvent :
<pre><code>.
La coloration syntaxique est une fonctionnalité supplémentaire, généralement apportée par CSS ou JavaScript.
Exemple : touche de clavier.
<samp> représente une sortie produite par un programme.
<var> représente une variable, notamment en programmation ou en mathématiques.
<blockquote>
représente une citation
relativement longue,
généralement présentée
comme un bloc.
<q>
représente une citation courte
intégrée dans une phrase.
L'attribut
cite
peut contenir l'URL
de la source.
Cette URL n'est cependant pas automatiquement présentée à l'utilisateur.
Si la source doit être visible, affichez-la explicitement dans le contenu.
<cite>
représente le titre
d'une œuvre créative.
Exemples :
livre,
article,
film,
chanson,
peinture,
site ou œuvre citée.
Il ne sert pas simplement à mettre un nom de personne en italique.
Pour afficher l'auteur, utilisez du texte normal ou une structure adaptée.
<abbr>
représente une abréviation
ou un acronyme.
L'attribut
title
peut contenir
son développement.
Cependant, ne comptez pas uniquement sur le tooltip de
title
pour transmettre une information essentielle.
Il est souvent préférable d'écrire la forme complète lors de la première occurrence.
<dfn>
indique le terme
actuellement défini.
<br>
force un retour à la ligne.
Il est adapté lorsque le retour lui-même fait partie du contenu :
adresse,
poème,
paroles ou texte formaté.
Il ne doit pas servir à créer des marges.
<hr>
représente une rupture thématique
entre deux parties du contenu.
Son apparence de ligne horizontale est seulement son rendu habituel et peut être modifiée avec CSS.
<sub>
représente un texte
en indice.
Exemple : formule chimique.
<sup>
représente un texte
en exposant.
Exemples :
puissances,
ordinaux,
références typographiques.
Ces éléments doivent être utilisés lorsque la position possède une signification, et non simplement pour déplacer visuellement un texte.
<ins>
représente du contenu
ajouté au document.
<del>
représente du contenu
supprimé.
Ils sont utiles lorsqu'on veut indiquer explicitement une modification.
Les attributs
datetime
et
cite
peuvent fournir
des informations supplémentaires
sur cette modification.
<bdi>
signifie
Bidirectional Isolation.
Il isole une portion de texte dont la direction n'est pas connue à l'avance.
C'est utile par exemple pour des noms d'utilisateurs provenant de langues différentes.
<bdo>
permet au contraire
de forcer explicitement
une direction avec
dir="ltr"
ou
dir="rtl".
<wbr>
signifie
Word Break Opportunity.
Il indique un emplacement où le navigateur peut effectuer un retour à la ligne si nécessaire.
Contrairement à
<br>,
le saut n'est pas forcé.
C'est utile pour certains identifiants, chemins ou longues chaînes difficiles à couper naturellement.
Utilisez généralement un titre principal
<h1>,
puis des niveaux cohérents.
Utilisez :
<strong>
pour l'importance,
<em>
pour l'emphase,
<b>
pour attirer l'attention
sans importance supplémentaire,
<i>
pour une voix ou un registre différent.
Ne choisissez pas une balise uniquement parce que son style par défaut vous plaît.
HTML décrit le sens. CSS contrôle l'apparence.
Listes
HTMLLes listes HTML permettent d'organiser des éléments liés entre eux : listes sans ordre particulier, étapes ordonnées, sous-listes et associations terme / description.
<ul>
représente une liste dont l'ordre
des éléments n'est pas important.
Chaque élément est représenté par un
<li>.
Exemples :
• fonctionnalités
• ingrédients
• technologies utilisées
• liens de navigation
• caractéristiques
La forme des puces est un choix visuel et peut être modifiée avec CSS.
<ol>
représente une liste ordonnée.
Elle convient lorsque la position des éléments possède une signification.
Exemples :
• étapes d'un tutoriel
• classement
• procédure
• chronologie
Attributs utiles :
start
définit la valeur de départ.
reversed
inverse la numérotation.
type
contrôle le type de marqueur :
1,
A,
a,
I,
i.
<li>
représente un élément
d'une liste.
Il peut contenir :
texte,
liens,
paragraphes,
images,
boutons,
autres listes.
Dans une liste ordonnée, l'attribut
value
permet de définir
la valeur numérique
d'un item particulier.
La sous-liste doit être placée à l'intérieur du
<li>
correspondant.
Cette structure est utile pour :
• menus hiérarchiques
• plans de cours
• catégories
• arbres de contenu
<dl>
représente une liste
de groupes nom / valeur
ou terme / description.
<dt>
représente le terme
ou le nom.
<dd>
représente sa description
ou sa valeur.
Usages :
• glossaire
• métadonnées
• caractéristiques techniques
• questions / réponses simples
• informations produit
Un terme peut avoir plusieurs descriptions, et plusieurs termes peuvent partager une même description.
Propriétés :
list-style-typelist-style-positionlist-style-image
Le raccourci
list-style
peut regrouper plusieurs valeurs.
Pour des interfaces personnalisées, on utilise également souvent
list-style: none,
par exemple pour une navigation,
à condition de conserver
une structure HTML logique.
::marker
cible directement
le marqueur d'un élément de liste.
Il peut notamment modifier :
couleur,
taille de police,
graisse,
certains contenus.
Il est généralement préférable à une fausse puce créée uniquement avec
::before
lorsque l'objectif est simplement
de styliser le marqueur natif.
<ul>
lorsque l'ordre n'a pas de sens.
Utilisez
<ol>
lorsque la position des éléments
est importante.
Utilisez
<dl>
pour des associations
terme / description
ou nom / valeur.
Ne remplacez pas une véritable liste par une série de
<div>
uniquement pour faciliter le CSS.
La structure HTML doit décrire le contenu ; CSS gère son apparence.
Liens & Médias
HTMLHTML permet de créer des liens, afficher des images responsives, intégrer des vidéos et fichiers audio, embarquer du contenu externe et créer des graphiques vectoriels ou dessinés avec JavaScript.
<a>
crée un lien.
L'attribut
href
indique sa destination.
Exemples :
URL relative,
URL absolue,
ancre interne,
email,
numéro de téléphone.
Si l'élément doit déclencher une action JavaScript plutôt que naviguer, utilisez généralement un véritable
<button>.
target="_blank"
demande généralement
l'ouverture du lien
dans un nouvel onglet
ou une nouvelle fenêtre.
rel="noopener"
empêche la nouvelle page
d'accéder à la page d'origine
via window.opener.
Les navigateurs modernes appliquent généralement ce comportement automatiquement avec
_blank,
mais l'écrire explicitement
reste lisible.
noreferrer
empêche également l'envoi
des informations de référent.
Ne l'ajoutez donc pas systématiquement si vous souhaitez conserver ces informations.
mailto:
ouvre généralement
l'application email configurée.
tel:
permet notamment
de lancer un appel
depuis les appareils compatibles.
L'attribut
download
suggère au navigateur
de télécharger la ressource
plutôt que de la naviguer.
Le comportement final peut dépendre du navigateur, du type de ressource et de son origine.
<img>
permet d'afficher une image.
L'attribut
src
indique la ressource.
L'attribut
alt
fournit une alternative textuelle.
Pour une image informative, décrivez son rôle ou son contenu utile.
Pour une image purement décorative, utilisez généralement :
alt="".
Les attributs
width
et
height
permettent au navigateur
de réserver l'espace nécessaire
avant le chargement.
loading="lazy"
demande au navigateur
de différer le chargement
d'une image hors écran.
C'est utile pour les images situées plus bas dans la page.
Évitez cependant de mettre toutes les images en lazy loading, notamment l'image principale visible dès l'arrivée.
fetchpriority="high"
peut indiquer qu'une image
est particulièrement importante.
Utilisez-le avec modération, par exemple pour une image principale réellement prioritaire.
srcset
permet de proposer
plusieurs variantes d'une image.
Le navigateur peut choisir une ressource adaptée à la taille d'affichage et à la densité de l'écran.
sizes
décrit la largeur approximative
que l'image occupera
dans le layout.
Cela peut éviter de télécharger inutilement une image énorme sur un petit écran.
<picture>
permet de fournir
plusieurs sources d'image.
Il est particulièrement utile pour :
• formats différents
• art direction
• cadrage mobile spécifique
• changement d'image selon un media query
Il contient un ou plusieurs
<source>,
puis un
<img>
qui constitue l'image réelle
et le fallback.
<figure>
représente un contenu autonome
auquel le document peut faire référence.
Il peut contenir :
image,
diagramme,
graphique,
capture d'écran,
code.
<figcaption>
fournit une légende.
La légende peut être placée au début ou à la fin de la figure.
<audio>
intègre un lecteur audio natif.
Attributs fréquents :
controlsautoplayloopmutedpreload
Plusieurs
<source>
peuvent être proposés.
preload="metadata"
demande généralement
de récupérer principalement
les métadonnées du média.
<video>
permet de lire une vidéo
sans plugin externe.
Attributs importants :
controls
→ commandes utilisateur.
poster
→ image affichée avant la lecture.
preload
→ indication de préchargement.
playsinline
→ demande une lecture intégrée
sur certains appareils mobiles.
L'autoplay peut être limité par le navigateur ; les vidéos silencieuses ont généralement plus de chances d'être autorisées.
<track>
associe une piste textuelle
à un média audio ou vidéo.
Le format courant est WebVTT :
.vtt.
Valeurs de
kind :
captionssubtitlesdescriptionschaptersmetadata
Les sous-titres et captions sont importants pour rendre les vidéos accessibles.
<iframe>
intègre un autre document HTML
dans la page.
Utilisations :
vidéos externes,
cartes,
widgets,
contenus tiers.
Ajoutez un
title
décrivant le contenu.
loading="lazy"
peut différer le chargement
d'une iframe hors écran.
allow
configure certaines fonctionnalités.
referrerpolicy
contrôle les informations
de référent transmises.
sandbox
applique des restrictions
au document intégré.
Sans valeur supplémentaire, plusieurs capacités sont limitées.
Des permissions peuvent ensuite être réactivées avec des tokens comme :
allow-scriptsallow-formsallow-popupsallow-same-origin
La configuration dépend du contenu embarqué.
N'accordez que les capacités réellement nécessaires.
Contrairement à une image bitmap, les formes sont décrites mathématiquement.
SVG est particulièrement adapté à :
icônes,
logos,
diagrammes,
illustrations simples.
viewBox
définit le système
de coordonnées interne.
currentColor
permet à l'icône
de reprendre la couleur CSS
du texte.
On peut alors utiliser :
aria-hidden="true".
Un SVG réellement informatif doit au contraire disposer d'un nom accessible.
On peut notamment utiliser
<title>
avec les attributs ARIA appropriés.
<canvas>
fournit une surface bitmap
manipulable avec JavaScript.
Contextes courants :
2d
pour le dessin 2D.
webgl
et
webgl2
pour des graphiques
accélérés utilisant WebGL.
Contrairement à un SVG, les objets dessinés ne deviennent pas automatiquement des éléments DOM individuels.
Un canvas complexe peut donc nécessiter une alternative accessible décrivant les informations importantes.
<source>
permet de proposer
plusieurs ressources possibles.
Il est utilisé notamment avec :
<picture><audio><video>
Selon le contexte, on peut préciser :
src,srcset,type,media.
Le navigateur sélectionne la ressource adaptée parmi celles qu'il peut utiliser.
<a>
pour naviguer
et
<button>
pour déclencher une action.
Donnez aux images une alternative textuelle adaptée à leur rôle.
Ajoutez
width
et
height
lorsque vous connaissez
les dimensions intrinsèques.
Utilisez
loading="lazy"
pour les médias hors écran
lorsque cela est pertinent.
Utilisez
srcset,
sizes
ou
<picture>
afin d'éviter des images
inutilement lourdes.
Ajoutez des captions ou sous-titres aux vidéos lorsque nécessaire.
Donnez un
title
descriptif aux iframes.
Limitez les permissions accordées au contenu tiers.
Tableaux
HTMLLes tableaux HTML servent à présenter des données organisées en lignes et colonnes. Une bonne structure avec caption, th, scope, thead, tbody et tfoot améliore la compréhension et l'accessibilité.
<table>
représente des données ayant une relation
logique entre lignes et colonnes.
Utilisez-le pour :
statistiques,
résultats,
tarifs comparatifs,
horaires,
données financières.
Un tableau HTML ne doit pas servir à construire la mise en page générale.
Pour le layout, utilisez plutôt CSS Grid ou Flexbox.
<caption>
fournit un titre ou une description
au tableau.
Il doit être placé comme premier enfant de
<table>.
Un caption clair aide l'utilisateur à comprendre immédiatement la nature des données.
Il est particulièrement utile pour l'accessibilité.
<tbody> contient les lignes principales de données.
<tfoot> contient généralement les totaux, moyennes ou informations de synthèse.
Ces éléments améliorent la structure du document.
Pour rendre un grand tableau scrollable, il est souvent préférable de placer le tableau dans un conteneur avec
overflow
plutôt que de transformer directement
<tbody>.
<tr>
signifie
table row.
Il représente une ligne du tableau.
Chaque ligne contient ensuite des cellules :
<th>
pour les en-têtes,
ou
<td>
pour les données.
<th>
représente une cellule d'en-tête.
<td>
représente une cellule de données.
L'apparence visuelle par défaut de
<th>
n'est pas sa fonction principale :
sa valeur est surtout sémantique.
L'attribut
scope
permet de préciser
ce que décrit l'en-tête.
scope
indique les cellules auxquelles
un <th>
est associé.
Valeurs utiles :
col
→ en-tête de colonne.
row
→ en-tête de ligne.
colgroup
et
rowgroup
existent pour des tableaux
plus complexes.
colspan
permet à une cellule
d'occuper plusieurs colonnes.
rowspan
permet d'occuper plusieurs lignes.
Ces attributs sont utiles pour des tableaux structurés, mais une utilisation excessive peut rendre les relations entre données plus difficiles à comprendre.
<colgroup>
permet de regrouper
les colonnes d'un tableau.
<col>
représente une ou plusieurs colonnes.
L'attribut
span
peut appliquer un même groupe
à plusieurs colonnes.
Les propriétés CSS applicables directement aux colonnes sont cependant plus limitées que sur des éléments HTML classiques.
Une solution robuste consiste à conserver la structure native du tableau et à placer celui-ci dans un conteneur défilable horizontalement.
Cela évite de casser inutilement la sémantique du tableau.
table-layout
contrôle la méthode utilisée
pour calculer les largeurs
des colonnes.
auto : le contenu influence les dimensions.
fixed : les dimensions peuvent être calculées plus directement à partir de la largeur du tableau et des colonnes définies.
fixed
peut être pratique pour obtenir
des colonnes plus prévisibles.
Une interface de tri nécessite généralement JavaScript.
Le contrôle de tri peut être un véritable
<button>
placé dans le
<th>.
aria-sort
peut indiquer l'état de tri
de la colonne concernée.
Ajoutez un
<caption>
lorsque le contexte n'est pas évident.
Utilisez
<th>
et scope
pour les en-têtes.
Évitez les fusions de cellules inutilement complexes.
Gardez un ordre de lecture logique.
Sur mobile, préférez généralement un wrapper scrollable plutôt que de transformer complètement la structure du tableau.
Formulaires
HTMLLes formulaires permettent de collecter des données utilisateur. HTML fournit les champs, labels, groupes, contraintes de validation et comportements natifs nécessaires à une interface accessible.
<form>
regroupe des contrôles
permettant de saisir puis d'envoyer
des données.
action : URL recevant les données.
method="get" : encode généralement les données dans l'URL.
method="post" : transmet les données dans le corps de la requête.
POST ne rend pas les données automatiquement secrètes ou sécurisées : HTTPS et la sécurité côté serveur restent indispensables.
enctype="multipart/form-data" : nécessaire pour l'envoi classique de fichiers avec un formulaire POST.
name
est essentiel lors de la soumission.
Il devient la clé associée à la valeur saisie.
Un champ sans
name
n'est généralement pas inclus
dans les données du formulaire.
Pour certains contrôles comme radio ou checkbox, l'attribut
value
définit la valeur envoyée.
<input>
possède de nombreux types.
Parmi les plus courants :
textemailpasswordnumbertelurlsearchdatetimedatetime-localfilecheckboxradiorangecolorhidden.
Utiliser le type approprié améliore souvent clavier mobile, validation native et ergonomie.
<label>
fournit le libellé d'un champ.
Le clic sur le label peut également activer le contrôle associé.
L'association classique utilise :
for
sur le label
et le même
id
sur le champ.
Un
placeholder
ne doit pas remplacer
un vrai label :
il disparaît lorsque l'utilisateur saisit.
autocomplete
aide le navigateur
à comprendre la nature
d'une information.
Exemples :
nameemailusernamecurrent-passwordnew-passwordpostal-code.
inputmode
donne une indication
sur le clavier virtuel
souhaité.
Exemples :
numeric,
decimal,
tel,
email.
Attributs courants :
requiredminmaxstepminlengthmaxlengthpattern.
Le type de champ peut également participer à la validation.
Cette validation améliore l'expérience utilisateur mais ne remplace jamais la validation côté serveur.
<textarea>
permet la saisie
de texte sur plusieurs lignes.
Contrairement à
<input>,
il possède une balise fermante.
Une valeur initiale peut être placée entre les deux balises.
Attention : les espaces et retours placés entre les balises peuvent faire partie de la valeur initiale.
Le redimensionnement visuel peut être contrôlé avec CSS.
<select>
présente une liste d'options.
Chaque
<option>
possède un texte visible
et éventuellement une
value
différente.
<optgroup>
permet de regrouper
les options.
multiple
autorise plusieurs sélections,
mais l'interaction exacte
dépend de l'appareil
et de l'interface du navigateur.
radio
permet généralement
de choisir une seule valeur.
Les boutons radio du même groupe utilisent le même
name.
Une
checkbox
représente généralement
une option indépendante
pouvant être activée ou non.
Pour un groupe logique, utilisez idéalement
<fieldset>
et
<legend>.
<button>
crée un bouton interactif.
Types :
submit
→ soumet le formulaire.
button
→ bouton générique,
souvent utilisé avec JavaScript.
reset
→ réinitialise les contrôles
à leurs valeurs initiales.
Dans un formulaire, spécifiez explicitement
type="button"
pour un bouton qui ne doit pas
soumettre le formulaire.
<fieldset>
regroupe des contrôles
ayant une relation logique.
<legend>
fournit le nom du groupe.
Ce pattern est particulièrement utile pour :
radios,
checkboxes,
informations personnelles,
groupes d'options.
La bordure visible par défaut est seulement un style navigateur : ce n'est pas la raison sémantique d'utiliser fieldset.
input type="file"
permet de sélectionner
un fichier local.
multiple
autorise plusieurs fichiers.
accept
fournit une indication
sur les types de fichiers attendus.
Il ne s'agit pas d'une protection de sécurité : le serveur doit toujours vérifier réellement le fichier reçu.
Pour une soumission HTML classique contenant des fichiers, utilisez généralement
multipart/form-data.
<datalist>
fournit des suggestions
à un champ.
Le lien se fait avec :
list="..."
sur l'input,
et un
id
correspondant sur le datalist.
Contrairement à un
<select>,
l'utilisateur peut généralement
saisir une valeur différente
de celles proposées.
<output>
représente le résultat
d'un calcul ou d'une action.
Il peut être associé à d'autres contrôles grâce à l'attribut
for.
Il est particulièrement adapté aux formulaires comportant des valeurs calculées.
<progress>
représente la progression
d'une tâche.
Exemple : téléchargement à 70 %.
Sans attribut
value,
la progression est indéterminée.
<meter>
représente une valeur
à l'intérieur d'un intervalle connu.
Exemple : espace disque, niveau, score.
Son apparence exacte dépend du navigateur et du système : ne dépendez pas uniquement de sa couleur pour transmettre une information importante.
disabled
désactive un contrôle.
Un contrôle désactivé n'est généralement pas modifiable, ne reçoit pas le focus et n'est pas envoyé avec le formulaire.
readonly
rend certains contrôles textuels
non modifiables
tout en conservant
leur valeur dans le formulaire.
Les deux attributs ne sont donc pas interchangeables.
form.
Le contrôle n'a alors pas besoin d'être physiquement imbriqué dans le
<form>.
La valeur correspond à l'
id
du formulaire.
aria-describedby.
Lorsqu'une erreur est détectée,
aria-invalid="true"
peut indiquer que la valeur
est considérée comme invalide.
Un message d'erreur doit être compréhensible et expliquer comment corriger le problème.
Évitez de signaler une erreur uniquement par la couleur.
Elles peuvent être contournées.
Le serveur doit toujours :
valider les données,
vérifier les autorisations,
traiter les fichiers avec prudence,
protéger les données sensibles,
gérer les erreurs.
Utilisez HTTPS pour protéger les données pendant leur transport.
N'envoyez pas des mots de passe ou autres données sensibles dans une URL GET.
labels explicites,
attributs
name,types de champs adaptés,
autocomplete,
contraintes natives,
groupes fieldset/legend,
bouton avec type explicite.
Le placeholder reste facultatif et ne remplace pas le label.
Utilisez le bon
type
de champ.
Ajoutez
autocomplete
lorsque cela facilite la saisie.
Groupez les choix liés avec
fieldset
et
legend.
Indiquez clairement les erreurs.
Ne dépendez jamais uniquement de la couleur.
Et surtout : toute donnée reçue doit être considérée comme non fiable jusqu'à validation côté serveur.
Éléments Sémantiques
HTMLLes éléments sémantiques décrivent le rôle du contenu plutôt que son apparence. Ils rendent la structure du document plus claire pour les navigateurs, les technologies d'assistance et les moteurs de recherche.
Par exemple :
<nav>
indique une navigation.
<article>
indique un contenu autonome.
<main>
indique le contenu principal.
<div>
reste utile lorsqu'aucune signification
particulière n'est nécessaire,
notamment pour un groupement purement visuel.
<header>
représente du contenu introductif.
Il peut contenir :
logo,
titre,
navigation,
recherche,
informations d'auteur.
Une page peut contenir plusieurs
<header>.
Un
<article>
peut par exemple posséder
son propre header.
<nav>
représente une zone dont le rôle principal
est la navigation.
Exemples :
menu principal,
sommaire,
pagination,
fil d'Ariane important.
Il n'est pas nécessaire d'utiliser
<nav>
autour de chaque petit groupe de liens.
Lorsque plusieurs navigations existent, un
aria-label
clair permet de les distinguer.
<main>
représente le contenu dominant
du document.
Son contenu doit être directement lié au sujet principal ou à la fonctionnalité principale de la page.
Dans une page classique, utilisez un seul
<main> visible.
Techniquement, un document ne doit pas avoir plus d'un
<main>
sans attribut hidden.
Les éléments répétés entre pages comme le footer global ou la navigation principale ne font généralement pas partie de
<main>.
<section>
représente une section générique
mais autonome du document.
Elle est utile lorsqu'aucun élément plus spécifique comme
<article>,
<nav>
ou <aside>
ne convient.
Une section devrait généralement posséder un titre.
Pour un simple conteneur visuel sans signification particulière, préférez
<div>.
<article>
représente une composition autonome.
Exemples fréquents :
article de blog,
actualité,
commentaire,
publication de forum,
fiche éditoriale autonome.
Une bonne question à se poser :
« Ce contenu conserve-t-il son sens lorsqu'il est présenté indépendamment du reste de la page ? »
<aside>
représente un contenu indirectement
lié au contenu principal.
Il peut s'agir de :
ressources liées,
biographie d'auteur,
encadré complémentaire,
publicité,
sidebar.
L'élément n'est donc pas réservé aux colonnes latérales visuelles.
<footer>
représente le pied
de la section ou du document
auquel il appartient.
Il peut contenir :
auteur,
copyright,
liens légaux,
liens connexes,
coordonnées.
Comme
<header>,
il peut être utilisé plusieurs fois
dans un document.
<h1>
à
<h6>
représentent six niveaux de titres.
<h1>
est le niveau le plus élevé.
Les niveaux doivent refléter la structure logique du contenu, et non simplement la taille visuelle désirée.
Utilisez CSS pour changer l'apparence des titres.
<figure>
représente un contenu autonome
référencé depuis le texte.
Il peut contenir :
image,
diagramme,
capture d'écran,
exemple de code,
illustration.
<figcaption>
fournit la légende associée.
<time>
représente une date,
une heure ou une durée.
L'attribut
datetime
fournit une valeur normalisée.
Le texte visible peut rester naturel pour l'utilisateur.
<details>
crée une zone qui peut être
ouverte ou fermée.
<summary>
fournit son libellé interactif.
Il doit être le premier enfant de
<details>
pour servir de résumé.
L'attribut
open
permet d'afficher le contenu
dès le chargement.
<search>
représente une partie du document
consacrée à une recherche
ou à une opération de filtrage.
Il peut contenir :
formulaire de recherche,
filtres,
contrôles associés.
Il décrit directement la fonction de cette zone.
<address>
représente les informations de contact
liées à la personne ou organisation
responsable du contenu concerné.
Il peut contenir :
nom,
email,
téléphone,
lien vers un profil,
adresse pertinente.
Il ne doit pas être utilisé simplement parce qu'un texte contient une adresse postale quelconque.
Utilisez CSS pour le design.
Préférez une balise native lorsque celle-ci exprime déjà correctement le rôle du contenu.
N'ajoutez pas des rôles ARIA inutiles à des éléments possédant déjà la bonne sémantique.
Gardez également une hiérarchie de titres logique et compréhensible.
Head & Meta
HTMLLe <head> contient les métadonnées du document : encodage, viewport, titre, description, ressources, informations SEO, partage social et données structurées.
<head>
contient les informations
qui décrivent ou configurent
le document.
On y trouve généralement :
encodage,
viewport,
titre,
description,
CSS,
favicon,
métadonnées sociales,
données structurées.
La majorité de ces informations n'est pas affichée directement dans le contenu de la page.
<meta charset="UTF-8">
déclare l'encodage du document.
UTF-8 permet de représenter les caractères utilisés dans pratiquement toutes les langues, ainsi que de nombreux symboles.
Placez cette déclaration très tôt dans le
<head>
afin que le navigateur connaisse
rapidement l'encodage.
La configuration classique est :
width=device-width
avec :
initial-scale=1.
Évitez de désactiver le zoom utilisateur avec
user-scalable=no
ou des limites de zoom trop strictes :
le zoom est important
pour l'accessibilité.
<title>
définit le titre du document.
Il peut notamment apparaître :
dans l'onglet,
dans les favoris,
dans l'historique,
comme source possible d'un titre de résultat de recherche.
Chaque page devrait avoir un titre descriptif et distinct.
Il n'existe pas de nombre de caractères garantissant exactement l'affichage d'un titre : les moteurs peuvent le tronquer ou générer une présentation différente.
meta name="description"
fournit un résumé de la page.
Un moteur de recherche peut utiliser cette description pour un snippet, mais ce n'est pas garanti.
Le contenu visible de la page peut également être utilisé pour générer l'extrait.
Rédigez donc une description :
spécifique,
naturelle,
utile,
cohérente avec le contenu réel.
On utilise :
rel="canonical".
Utilisez généralement une URL absolue correcte.
Le canonical constitue un signal important, mais un moteur de recherche peut sélectionner une autre URL si d'autres signaux sont contradictoires.
Valeurs fréquentes :
index
→ autorise l'indexation.
noindex
→ demande de ne pas indexer.
follow
→ permet de suivre les liens.
noindex
doit être utilisé avec précaution :
une erreur sur cette balise
peut retirer une page
des résultats de recherche.
Les propriétés principales sont :
og:titleog:descriptionog:imageog:urlog:type
Utilisez des URL absolues pour les ressources importantes comme l'image.
Une valeur fréquente est :
summary_large_image.
Open Graph reste également utile pour mutualiser une partie des informations de partage.
<link>
associe le document
à une ressource externe
ou à une relation particulière.
Valeurs courantes de
rel :
stylesheeticonpreloadpreconnectcanonicalmanifestalternate
Ne préchargez pas inutilement de nombreuses ressources :
preload
doit rester ciblé.
hreflang
permet d'indiquer les relations
entre ces pages.
Chaque version doit pointer vers les variantes appropriées.
x-default
peut représenter une page
par défaut lorsqu'aucune variante
linguistique ne convient.
theme-color
permet à certains navigateurs
d'adapter des parties de leur interface
à la couleur du site.
Il est possible d'utiliser des variantes associées aux préférences clair/sombre.
color-scheme
peut également indiquer
les thèmes pris en charge
par le document.
On le relie au document avec :
rel="manifest".
Le fichier peut contenir :
nom,
icônes,
couleurs,
URL de départ,
mode d'affichage.
JSON-LD est couramment utilisé à l'intérieur de :
<script type="application/ld+json">.
Types possibles selon la page :
Article,
BreadcrumbList,
Organization,
Product,
Recipe,
Event...
Le balisage doit correspondre au contenu réellement présent.
L'ajout de données structurées ne garantit pas l'affichage d'un résultat enrichi.
Une page publique classique peut néanmoins disposer d'une base comprenant :
charset,
viewport,
title,
description,
canonical,
favicon,
CSS,
Open Graph.
Ajoutez ensuite uniquement les métadonnées pertinentes pour le projet.
<title>
réellement descriptif
pour chaque page.
Utilisez une description spécifique à son contenu.
Vérifiez que le canonical pointe vers la bonne URL.
N'utilisez jamais
noindex
par erreur sur une page
destinée à apparaître
dans les moteurs de recherche.
Les métadonnées sociales doivent correspondre au contenu réel.
Les données structurées ne remplacent pas le contenu visible de qualité.
Enfin, n'ajoutez pas des dizaines de métadonnées simplement parce qu'elles existent : gardez celles qui ont un rôle réel dans le projet.
Box Model
CSSChaque élément CSS est représenté comme une boîte composée du contenu, du padding, de la bordure et des marges. Comprendre ce modèle est indispensable pour maîtriser les dimensions, les espacements et les débordements.
Content : contenu réel de l'élément.
Padding : espace entre le contenu et la bordure.
Border : bordure autour du padding et du contenu.
Margin : espace extérieur séparant la boîte des éléments voisins.
La taille finale dépend ensuite de la valeur de
box-sizing.
box-sizing: content-box.
Dans ce mode,
width et height
représentent uniquement la zone de contenu.
Padding et bordure s'ajoutent ensuite.
Avec :
box-sizing: border-box
la largeur et la hauteur déclarées incluent le contenu, le padding et la bordure.
C'est souvent plus simple pour construire des interfaces prévisibles.
width
définit la largeur.
height
définit la hauteur.
Elles peuvent utiliser :
px%remvw / vhdvhauto
Pour beaucoup de contenus, il est préférable de laisser la hauteur en
auto
plutôt que de forcer une valeur fixe.
min-width,
max-width,
min-height
et
max-height
permettent de poser des limites.
Elles sont particulièrement utiles pour créer des interfaces fluides.
Un conteneur peut ainsi prendre toute la place disponible sans devenir excessivement large.
min-content : taille minimale intrinsèque.
max-content : taille préférée sans retour à la ligne lorsque cela est possible.
fit-content : taille adaptée au contenu tout en respectant les contraintes disponibles.
Ces valeurs sont particulièrement utiles avec Grid et les composants réutilisables.
inline-size
correspond généralement à la largeur
dans un texte occidental horizontal.
block-size
correspond généralement à la hauteur.
On trouve également :
min-inline-sizemax-inline-sizemin-block-sizemax-block-size.
padding
crée de l'espace à l'intérieur
de la boîte.
Il sépare le contenu de la bordure.
Le padding n'a pas sa propre couleur : le background de l'élément est généralement peint derrière cette zone.
Raccourcis :
padding: 16px
→ quatre côtés.
padding: 12px 24px
→ vertical / horizontal.
Les propriétés logiques
padding-inline
et padding-block
sont souvent très pratiques.
margin
crée de l'espace à l'extérieur
de la bordure.
Contrairement au padding, la marge ne reçoit pas le background de l'élément.
margin-inline: auto
est très utile pour centrer
un bloc dont la largeur
est inférieure à l'espace disponible.
Les marges automatiques ont également des comportements très utiles avec Flexbox.
Deux marges adjacentes ne sont donc pas forcément additionnées.
Ce comportement concerne surtout les blocs dans le flux normal.
Les marges des items Flexbox ou Grid ne fusionnent pas de la même manière.
Lorsque l'objectif est simplement d'espacer une série d'éléments dans Flexbox ou Grid,
gap est souvent préférable.
border
est un raccourci pour :
border-widthborder-styleborder-color
Styles fréquents :
soliddasheddotteddoublenone
Une bordure participe aux dimensions de la boîte selon le modèle
box-sizing choisi.
border-radius
arrondit les coins de la boîte.
Une valeur élevée peut créer un bouton en forme de pilule.
50%
permet notamment de produire
une forme circulaire
lorsque largeur et hauteur sont égales.
On peut aussi définir chaque coin séparément.
outline
ressemble visuellement à une bordure,
mais ne participe généralement pas
aux dimensions de la boîte.
Il est particulièrement utile pour les indicateurs de focus.
outline-offset
permet d'éloigner le contour
de l'élément.
Évitez de supprimer un focus visible sans fournir une alternative accessible.
x y blur spread color.
inset
crée une ombre intérieure.
Plusieurs ombres peuvent être séparées par des virgules.
Les ombres trop fortes ou trop nombreuses peuvent rendre l'interface visuellement chargée.
overflow
contrôle le contenu dépassant
les dimensions d'une boîte.
visible
→ débordement visible.
hidden
→ contenu coupé.
auto
→ défilement lorsque nécessaire.
scroll
→ conteneur défilable.
clip
→ contenu coupé sans créer
le même comportement de scrolling
qu'un overflow classique.
Les variantes
overflow-x
et overflow-y
contrôlent chaque axe.
scrollbar-gutter
permet de réserver un espace
destiné à une scrollbar classique.
Cela peut éviter certains déplacements de contenu lorsqu'une scrollbar apparaît ou disparaît.
Une valeur courante est :
stable.
border-box
simplifie le calcul des dimensions.
Évitez les hauteurs fixes lorsqu'un contenu peut évoluer.
Préférez
max-width,
min(),
max()
et clamp()
pour les dimensions adaptatives.
Utilisez
gap
pour espacer les éléments
de Flexbox et Grid.
Enfin, évitez
overflow: hidden
uniquement pour masquer
un problème de layout :
cherchez d'abord la cause du débordement.
Typographie CSS
CSSCSS contrôle la famille de police, la taille, la graisse, la hauteur de ligne, l'espacement, l'alignement, les retours à la ligne et de nombreux détails typographiques.
font-family
définit la police utilisée.
Il est recommandé de fournir plusieurs polices de secours.
Si la première police n'est pas disponible, le navigateur teste la suivante.
Familles génériques :
serifsans-serifmonospacecursivefantasysystem-ui
@font-face
permet d'enregistrer une police
utilisable dans le CSS.
font-family
lui donne un nom local.
src
indique le fichier de police.
Le format
woff2
est couramment utilisé sur le web.
font-display
contrôle le comportement d'affichage
pendant le chargement.
font-size
définit la taille du texte.
rem : relatif à la taille de police de l'élément racine.
Sur de nombreux navigateurs, la valeur initiale habituelle correspond à environ 16 px, mais elle peut dépendre des réglages utilisateur.
em : valeur relative au contexte typographique.
clamp()
permet de créer une taille fluide
avec minimum et maximum.
font-weight
contrôle l'épaisseur des caractères.
Valeurs courantes :
400 → normal500 → medium600 → semi-bold700 → bold
Les polices variables peuvent proposer des valeurs intermédiaires beaucoup plus fines qu'une simple série de poids fixes.
La valeur réellement disponible dépend de la police chargée.
font-style
permet notamment d'activer
une variante italique.
Valeurs courantes :
normalitalicoblique
Lorsqu'une véritable fonte italique existe, le navigateur peut utiliser les glyphes spécifiquement dessinés pour cette variante.
line-height
détermine la hauteur
de chaque ligne de texte.
Une valeur sans unité agit comme un multiplicateur de la taille de police.
Exemple :
line-height: 1.6.
Cette forme est souvent intéressante car elle s'adapte naturellement lorsque la taille du texte change.
letter-spacing
modifie l'espacement
entre les caractères.
word-spacing
modifie l'espacement
entre les mots.
Une valeur positive espace davantage, tandis qu'une valeur négative resserre.
Les modifications importantes doivent être utilisées avec prudence afin de préserver la lisibilité.
text-align
contrôle l'alignement
du contenu inline.
Valeurs courantes :
startcenterendjustify
start
et end
sont souvent préférables
à gauche/droite lorsque plusieurs
directions d'écriture doivent être supportées.
text-decoration
peut contrôler plusieurs aspects
d'une décoration.
Propriétés utiles :
text-decoration-linetext-decoration-colortext-decoration-styletext-decoration-thicknesstext-underline-offset
Ces propriétés sont notamment pratiques pour personnaliser les liens.
text-transform
modifie la présentation
de la casse sans changer
le texte source dans le HTML.
Valeurs courantes :
uppercaselowercasecapitalizenone.
text-wrap
contrôle la manière
dont le navigateur répartit
le texte sur plusieurs lignes.
wrap : comportement normal.
nowrap : empêche les retours automatiques.
balance : tente d'équilibrer les lignes. Très intéressant pour les titres.
pretty : privilégie une composition typographique plus agréable, notamment pour des blocs de texte plus longs.
Le navigateur peut limiter le nombre de lignes traité par
balance pour des raisons de coût.
white-space
contrôle la façon dont
les espaces et retours à la ligne
du texte source sont traités.
Valeurs utiles :
normalnowrapprepre-wrappre-line
pre-wrap
est notamment pratique
lorsqu'on veut conserver
les retours du texte source
tout en autorisant le wrapping.
overflow-wrap
autorise le navigateur
à casser un mot lorsque cela
devient nécessaire.
word-break
contrôle plus directement
les possibilités de rupture.
Pour beaucoup de textes classiques :
overflow-wrap: anywhere
est une solution pratique
contre certains débordements.
hyphens
contrôle l'utilisation
de traits d'union lors
des retours à la ligne.
Valeurs :
nonemanualauto
La langue du document, définie notamment avec l'attribut HTML
lang,
peut être importante pour permettre
au navigateur d'appliquer
une césure correcte.
overflow: hidden
cache le surplus.
white-space: nowrap
conserve une seule ligne.
text-overflow: ellipsis
affiche les points de suspension.
line-clamp
permet de limiter un bloc
à un nombre donné de lignes.
La propriété standard existe, mais sa compatibilité n'est pas encore uniforme sur tous les navigateurs.
Le pattern historique utilisant :
-webkit-line-clamp-webkit-box-webkit-box-orient
reste donc utile lorsque la compatibilité est importante.
font-variant-numeric
contrôle certaines variantes
typographiques des chiffres.
tabular-nums
donne aux chiffres
une largeur régulière,
lorsque la police le supporte.
C'est particulièrement pratique pour :
tableaux,
prix,
compteurs,
statistiques.
Les axes fréquents comprennent :
poids,
largeur,
inclinaison,
taille optique.
Pour les axes courants, utilisez de préférence les propriétés CSS habituelles comme
font-weight.
font-variation-settings
permet un contrôle plus bas niveau
lorsque cela est nécessaire.
font-optical-sizing: auto
permet alors au navigateur
d'utiliser une variante adaptée
à la taille d'affichage.
Le résultat dépend entièrement des capacités de la police choisie.
text-shadow
ajoute une ou plusieurs ombres
au texte.
Syntaxe :
x y blur color.
Contrairement à
box-shadow,
il n'existe pas de spread
ou d'ombre inset.
Plusieurs ombres peuvent être séparées par des virgules.
Gardez une longueur de ligne confortable pour les textes longs.
Utilisez une hauteur de ligne suffisante pour le corps du texte.
Évitez les tailles fixes excessivement petites.
Limitez les effets décoratifs qui réduisent la lisibilité.
Pour les grands titres,
text-wrap: balance
peut améliorer la composition.
Pour le corps du texte, une largeur exprimée en
ch
peut être utile pour limiter
la longueur des lignes.
Layout & Position
CSSLes propriétés de layout et de positionnement contrôlent la façon dont les éléments participent au flux du document, se superposent, débordent ou restent attachés à une zone de l'écran.
display
détermine la façon dont un élément participe
à la mise en page.
block : crée généralement une boîte occupant la largeur disponible.
inline : reste dans le flux du texte.
inline-block : reste en ligne tout en acceptant largeur et hauteur.
flex : active Flexbox.
grid : active CSS Grid.
none : retire l'élément du rendu et de la mise en page.
display: none : l'élément ne participe plus à la mise en page.
visibility: hidden : l'élément devient invisible, mais son espace est conservé.
opacity: 0 : l'élément devient transparent, mais reste présent dans le layout et peut encore participer aux interactions.
Ne choisissez donc pas l'une de ces propriétés uniquement parce qu'elles semblent toutes « cacher » quelque chose.
relative : reste dans le flux, mais peut être décalé. Il peut aussi servir de repère à un descendant positionné.
absolute : est retiré du flux normal et positionné relativement à son containing block approprié.
fixed : reste généralement attaché au viewport pendant le défilement.
sticky : se comporte normalement jusqu'à atteindre un seuil, puis reste collé dans sa zone de défilement.
inset
est un raccourci pour :
top,
right,
bottom,
left.
On trouve également les propriétés logiques :
inset-inlineinset-blockinset-inline-startinset-inline-end
Elles sont pratiques pour créer des interfaces indépendantes du sens d'écriture.
sticky
a généralement besoin d'un seuil comme :
top: 0.
Son comportement dépend aussi du conteneur de défilement.
Un parent avec certaines valeurs de
overflow
peut modifier le comportement attendu.
L'élément sticky reste également limité par les limites de son conteneur.
z-index
contrôle l'ordre d'empilement
dans le contexte approprié.
Une valeur plus grande ne garantit pas toujours qu'un élément passera devant tout le reste.
La raison : les éléments peuvent appartenir à différents stacking contexts.
Un nouveau contexte d'empilement peut notamment être créé par certaines propriétés telles que :
position + z-index,
opacity,
transform,
filter,
isolation...
Les items Flex et Grid peuvent également utiliser
z-index
sans nécessairement être positionnés.
isolation: isolate
crée explicitement un nouveau
contexte d'empilement.
Cela peut être utile pour un composant comportant plusieurs couches visuelles.
Ses enfants peuvent gérer leur
z-index
sans interférer avec les couches
extérieures au composant.
overflow
définit ce qui se passe lorsqu'un contenu
dépasse les dimensions de son conteneur.
visible : débordement visible.
hidden : contenu visuellement coupé.
auto : permet le défilement lorsque nécessaire.
scroll : crée un conteneur de défilement.
clip : coupe le contenu sans fournir le même comportement de scrolling que
hidden.
On peut aussi utiliser :
overflow-x
et
overflow-y.
object-fit
contrôle la façon dont un contenu remplacé,
comme une image ou une vidéo,
remplit sa boîte.
cover : remplit la zone, quitte à recadrer.
contain : affiche tout le média sans le recadrer.
fill : remplit les dimensions même si cela déforme le média.
object-position
détermine la zone privilégiée
lors du recadrage.
float
était souvent détourné pour créer
des layouts complets.
Aujourd'hui, Grid et Flexbox sont généralement préférables pour cela.
float
reste néanmoins utile pour faire
circuler du texte autour d'une image.
display: flow-root
crée un nouveau contexte de formatage
permettant notamment de contenir proprement
des éléments flottants.
C'est notamment le cas de certains :
<dialog>popovers
éléments en plein écran.
Cette couche se situe au-dessus des contextes d'empilement classiques.
Il ne sert donc à rien de tenter de battre un véritable top layer avec un
z-index: 999999.
Utilisez Flexbox et Grid pour organiser les composants.
Réservez
absolute
aux éléments qui doivent réellement
être superposés ou détachés du flux.
Évitez les valeurs de
z-index
arbitrairement énormes.
Vérifiez également les débordements avant de masquer systématiquement le problème avec
overflow: hidden.
Flexbox
CSSFlexbox organise et aligne des éléments principalement sur un axe. Il est particulièrement utile pour les navigations, barres d'outils, boutons, cartes, centrages et composants responsives.
display: flex
transforme un élément
en flex container.
Ses enfants directs deviennent des flex items.
Par défaut :
flex-direction: row
Les éléments sont donc placés le long de l'axe principal, qui suit alors l'axe inline.
flex-direction
définit l'axe principal.
row : axe principal horizontal dans un document occidental classique.
row-reverse : inverse cet axe.
column : axe principal vertical.
column-reverse : axe vertical inversé.
Lorsque la direction change, le rôle de
justify-content
change avec l'axe principal.
justify-content
distribue les flex items
sur l'axe principal.
Valeurs courantes :
flex-startcenterflex-endspace-betweenspace-aroundspace-evenly
Son orientation dépend de
flex-direction.
align-items
aligne les items sur l'axe croisé.
Valeurs fréquentes :
stretchflex-startcenterflex-endbaseline
Avec
flex-direction: row,
cet axe est généralement vertical.
Avec
column,
il devient généralement horizontal.
flex-wrap: wrap
autorise plusieurs lignes.
Valeurs :
nowrapwrapwrap-reverse
flex-flow
est un raccourci combinant
flex-direction
et
flex-wrap.
align-content
ne doit pas être confondu
avec align-items.
Il distribue les lignes flex elles-mêmes sur l'axe croisé.
Il est donc surtout utile lorsque :
le conteneur utilise
flex-wrap: wrap,
plusieurs lignes existent,
et le conteneur possède de l'espace disponible sur l'axe croisé.
gap
crée de l'espace entre
les flex items.
Il ne crée pas de marge sur les bords extérieurs.
On peut aussi utiliser :
row-gap
et
column-gap.
C'est généralement plus simple que d'ajouter des marges individuelles à chaque item.
flex-shrink : contrôle sa capacité à rétrécir lorsqu'il manque de place.
flex-basis : définit sa taille de base sur l'axe principal, avant la distribution de l'espace disponible.
Ces trois propriétés expliquent réellement le comportement de Flexbox.
flex
est un raccourci pour :
flex-grow,
flex-shrink,
flex-basis.
Exemple :
flex: 1 1 300px
signifie qu'un item : peut grandir, peut rétrécir, et part d'une base de 300 px.
flex: 1
signifie généralement
qu'il participe fortement
au partage de l'espace disponible,
avec une base calculée très petite,
plutôt que simplement
« prendre toute la largeur ».
align-items
agit sur tous les items.
align-self
permet à un seul item
d'utiliser un alignement différent.
Valeurs courantes :
autostretchflex-startcenterflex-end
Il agit sur l'axe croisé.
Une marge automatique peut absorber l'espace libre.
C'est très pratique pour pousser un bouton ou un groupe vers l'autre extrémité du conteneur.
Avec les propriétés logiques,
margin-inline-start: auto
évite de dépendre explicitement
de gauche ou droite.
order
peut changer l'ordre visuel
des flex items.
Par défaut :
order: 0.
Une valeur plus petite est placée plus tôt.
Attention : cela ne réécrit pas nécessairement l'ordre logique du document.
Pour l'accessibilité, l'ordre HTML doit rester cohérent avec la lecture et les interactions.
Une longue URL, un bloc de code, ou du texte non cassable peut alors forcer le conteneur à déborder.
min-width: 0
permet souvent au flex item
de réellement rétrécir.
C'est un correctif important dans de nombreux layouts Flexbox.
flex-wrap: wrap
avec :
flex: 1 1 280px
permet de créer rapidement des composants flexibles.
Chaque carte possède une taille de base, peut grandir, peut rétrécir et passe sur une nouvelle ligne lorsqu'il n'y a plus assez d'espace.
Exemples :
navigation, barre d'actions, groupe de boutons, contenu d'une carte, alignement d'icônes.
Grid est souvent préférable lorsqu'il faut contrôler simultanément lignes et colonnes.
Les deux systèmes peuvent parfaitement être imbriqués.
Utilisez
gap
pour gérer les espacements.
Utilisez les marges automatiques pour repousser un item lorsque c'est plus simple qu'une distribution globale.
Comprenez
grow,
shrink
et basis
plutôt que d'utiliser
flex: 1
partout sans raison.
Gardez également l'ordre HTML logique, même si Flexbox peut changer l'ordre visuel.
CSS Grid
CSSCSS Grid est un système de mise en page bidimensionnel permettant d'organiser les éléments en lignes et colonnes. Il est particulièrement adapté aux layouts de pages, dashboards, galeries et grilles de cartes.
display: grid
transforme un élément en
conteneur Grid.
Ses enfants directs deviennent des grid items.
Une grille est composée de :
• lignes de grille
• colonnes
• rangées
• cellules
• pistes (tracks)
• zones (areas)
Contrairement à Flexbox, principalement orienté sur un axe, Grid permet de travailler simultanément sur les lignes et les colonnes.
grid-template-columns
définit les colonnes de la grille.
grid-template-rows
définit les lignes.
Les pistes peuvent utiliser :
px%frautomin-contentmax-contentfit-content()
L'unité
fr
représente une fraction de l'espace disponible
dans la grille.
repeat(3, 1fr)
crée trois colonnes égales.
minmax() définit une taille minimale et une taille maximale pour une piste.
Exemple :
minmax(250px, 1fr)
signifie : la colonne ne doit pas devenir plus petite que 250 px mais peut ensuite s'étendre.
auto-fit
et auto-fill
permettent de créer automatiquement
autant de colonnes que l'espace disponible le permet.
auto-fit : les pistes vides peuvent se replier, laissant les éléments existants occuper l'espace disponible.
auto-fill : conserve les pistes potentielles, même si certaines sont vides.
Pour une grille de cartes responsive,
auto-fit
est très souvent pratique.
gap
définit l'espace entre les lignes
et les colonnes.
On peut également utiliser :
row-gap
pour les lignes.
column-gap
pour les colonnes.
Contrairement aux marges,
gap
ajoute uniquement l'espace entre les éléments,
sans créer d'espace supplémentaire
sur les bords externes de la grille.
Ainsi :
grid-column: 1 / 3
signifie :
commencer sur la ligne 1
et terminer sur la ligne 3.
L'élément occupe donc deux colonnes.
span 2
signifie :
occuper deux pistes.
1 / -1
permet souvent d'occuper toute la grille explicite.
justify-items : alignement sur l'axe inline.
align-items : alignement sur l'axe block.
place-items : raccourci pour les deux.
Pour déplacer l'ensemble des pistes dans le conteneur :
justify-contentalign-contentplace-content
Pour un seul item :
justify-self,
align-self
et place-self.
grid-template-*
constituent la
grille explicite.
Si davantage d'éléments doivent être placés, CSS peut créer automatiquement de nouvelles pistes : c'est la grille implicite.
On peut contrôler leur taille avec :
grid-auto-rows
et
grid-auto-columns.
grid-auto-flow
contrôle la façon dont Grid place automatiquement
les éléments.
Valeurs fréquentes :
row
→ remplit par lignes.
column
→ remplit par colonnes.
dense
tente de remplir les trous disponibles.
Attention :
dense
peut produire un ordre visuel différent
de l'ordre du document.
Il faut donc éviter de l'utiliser lorsque l'ordre de lecture est important.
grid-template-areas
permet de représenter visuellement
la structure d'une page.
Chaque chaîne représente une ligne.
Le même nom répété représente une zone occupant plusieurs cellules.
Un point
.
représente une cellule non nommée.
Les éléments sont ensuite associés aux zones avec
grid-area.
On peut définir une structure simple sur mobile, puis changer uniquement
grid-template-areas
et les colonnes sur un écran plus large.
Le HTML reste identique.
Les noms sont placés entre crochets :
[sidebar-start]
Ils rendent les placements complexes plus faciles à comprendre qu'une série de numéros comme
2 / 5.
Avec
subgrid,
une grille enfant peut reprendre
les pistes définies par sa grille parente.
Cela permet par exemple d'aligner parfaitement le titre, le contenu et le bouton de plusieurs cartes, même lorsque leur contenu possède des hauteurs différentes.
subgrid
peut être utilisé sur les colonnes,
les lignes ou les deux.
Une longue URL, un bloc de code ou un contenu non cassable peut alors provoquer un débordement.
Deux solutions fréquentes :
min-width: 0
sur le grid item.
ou :
minmax(0, 1fr)
dans la définition de la piste.
Cette technique est particulièrement utile pour les layouts avec sidebar.
Grid est particulièrement efficace lorsque la structure doit être organisée sur deux dimensions.
Exemples :
dashboards,
layouts de pages,
galeries,
grilles de cartes.
Flexbox est souvent pratique pour organiser les éléments d'un composant sur un axe.
Exemple classique : Grid pour disposer les cartes, Flexbox à l'intérieur de chaque carte.
gap
pour les espacements entre items.
2. Utiliser
auto-fit
et minmax()
lorsque la grille peut être naturellement responsive.
3. Préserver autant que possible un ordre HTML logique.
4. Ne pas réorganiser visuellement des informations importantes d'une façon différente de l'ordre de lecture.
5. Utiliser les zones nommées pour les grandes structures lisibles.
6. Utiliser
subgrid
lorsque plusieurs composants imbriqués
doivent partager les mêmes pistes.
7. Ne pas ajouter des breakpoints lorsqu'une grille flexible suffit déjà.
Effets visuels & Animations
CSSCSS permet de déplacer, transformer, filtrer et animer les éléments sans modifier leur structure HTML. Transitions, transformations, keyframes, filtres, masques et animations liées au scroll permettent de créer des interfaces dynamiques tout en respectant l'accessibilité.
transform
modifie visuellement la géométrie d'un élément.
Fonctions courantes :
translate()translateX()translateY()rotate()scale()skew()
Une transformation ne provoque généralement pas le déplacement des autres éléments dans le layout.
Les transformations sont donc particulièrement adaptées aux interactions et animations visuelles.
translaterotatescale
Elles évitent d'avoir à réécrire toute la propriété
transform pour modifier une seule transformation.
Elles sont très pratiques pour les transitions et les composants interactifs.
transform-origin
définit le point autour duquel une transformation
est effectuée.
Par défaut :
50% 50%
c'est-à-dire le centre.
On peut utiliser :
centertop leftbottom centerdes pourcentages ou des longueurs.
Fonctions utiles :
rotateX()rotateY()rotateZ()translateZ()
perspective
donne une sensation de profondeur.
transform-style: preserve-3d
permet aux descendants de conserver
leur positionnement tridimensionnel.
transition
anime automatiquement le passage entre
deux valeurs d'une propriété CSS.
Elle est particulièrement adaptée aux changements :
:hover:focus:activeajout ou retrait de classe
Syntaxe :
propriété durée timing délai.
Il est généralement préférable de préciser les propriétés à animer plutôt que d'utiliser systématiquement
transition: all.
Valeurs courantes :
lineareaseease-inease-outease-in-out
cubic-bezier()
permet de créer une courbe personnalisée.
steps()
permet au contraire une animation
par étapes discrètes.
@keyframes
décrit les étapes d'une animation.
On peut utiliser :
from / to
ou des pourcentages :
0%,
50%,
100%.
La propriété
animation
relie ensuite les keyframes à l'élément.
animation-nameanimation-durationanimation-delayanimation-iteration-countanimation-directionanimation-fill-modeanimation-play-state
Le raccourci
animation
permet de regrouper ces valeurs.
forwards
conserve notamment les valeurs finales
définies par les keyframes.
filter
applique un traitement visuel
à l'élément lui-même.
Fonctions courantes :
blur()brightness()contrast()grayscale()saturate()hue-rotate()invert()drop-shadow()
Plusieurs filtres peuvent être combinés.
backdrop-filter
applique un filtre à ce qui se trouve
derrière l'élément.
Il est souvent combiné à un fond semi-transparent.
C'est la base de nombreux effets de type glassmorphism.
Évitez cependant les valeurs de flou excessives sur de grandes zones si l'effet n'apporte rien à l'interface.
box-shadow
ajoute une ombre autour de la boîte d'un élément.
text-shadow
ajoute une ombre au texte.
Une ombre peut être :
externe,
interne avec
inset,douce,
colorée,
utilisée comme effet lumineux.
Plusieurs ombres peuvent être séparées par des virgules.
clip-path
découpe une partie de l'élément.
Formes fréquentes :
circle()ellipse()inset()polygon()
Les propriétés de masque permettent des effets plus avancés avec des images ou gradients servant de masque.
mix-blend-mode
contrôle la façon dont un élément
se mélange avec ce qui se trouve derrière lui.
Exemples :
multiplyscreenoverlaydifference
Ces effets peuvent être intéressants pour des créations graphiques, mais doivent rester lisibles.
@starting-style
permet de définir les valeurs initiales
utilisées lorsqu'un élément apparaît
pour la première fois.
C'est particulièrement utile pour :
popovers,
dialogs,
éléments nouvellement affichés.
Il permet d'obtenir certaines transitions d'entrée sans devoir créer une animation
@keyframes complète.
animation-timeline: view()
peut faire progresser l'animation
selon la position de l'élément
dans la zone visible.
scroll()
permet de baser la progression
sur le défilement d'un conteneur.
Cette partie est plus avancée : vérifiez la compatibilité navigateur avant de l'utiliser comme fonctionnalité indispensable.
CSS peut détecter cette préférence avec :
prefers-reduced-motion: reduce.
Les grands déplacements, rotations ou zooms peuvent alors être réduits ou remplacés.
Une transition légère de couleur peut parfois être conservée.
Pour beaucoup d'animations d'interface,
transform
et opacity
sont de bons candidats.
Évitez d'animer inutilement des propriétés influençant fortement la mise en page.
will-change
peut indiquer au navigateur qu'une propriété
va bientôt changer.
Mais il ne doit pas être ajouté partout : utilisez-le uniquement lorsque cela répond à un véritable besoin.
1. Utiliser des durées relativement courtes pour les interactions simples.
2. Animer uniquement les propriétés nécessaires.
3. Éviter les mouvements excessifs.
4. Respecter
prefers-reduced-motion.
5. Éviter de multiplier les gros flous, filtres et effets graphiques sans nécessité.
6. Une animation doit idéalement expliquer un changement d'état, attirer l'attention sur une information utile ou améliorer la continuité visuelle.
Arrière-plans & Couleurs
CSSCSS permet de créer des couleurs, images de fond, dégradés, superpositions et effets visuels complexes. Les propriétés background contrôlent la taille, la position, la répétition, les couches et la zone d'affichage du fond.
HEX :
#2563eb
RGB :
rgb(37 99 235)
HSL :
hsl(221 83% 53%)
Une transparence peut être ajoutée avec une barre oblique :
rgb(37 99 235 / 50%)
CSS propose aussi des espaces colorimétriques modernes comme
oklch(), utiles notamment pour
construire des palettes plus régulières.
background-color
définit la couleur de fond d'un élément.
La valeur par défaut est
transparent.
Une couleur de fond peut rester visible derrière une image possédant des zones transparentes.
Il est souvent préférable d'utiliser une couleur avec alpha plutôt que de modifier
opacity,
car opacity affecte aussi
le contenu et les enfants.
background-image
permet d'afficher une image derrière le contenu.
Une image de fond est généralement décorative.
Pour une image importante pour le contenu, une véritable balise
<img>
est souvent préférable.
background-image
accepte également les gradients CSS.
background-size
contrôle la taille de l'image de fond.
cover : remplit entièrement la zone. Une partie de l'image peut être coupée.
contain : affiche toute l'image, mais peut laisser de l'espace libre.
On peut également définir une taille précise ou utiliser des pourcentages.
background-position
détermine la position de l'image dans son conteneur.
Valeurs courantes :
centercenter topright bottom50% 50%
Avec
background-size: cover,
cette propriété devient importante,
car certaines zones de l'image peuvent être coupées.
background-repeat
contrôle la répétition d'une image.
repeat
répète dans les deux directions.
no-repeat
empêche toute répétition.
repeat-x
répète horizontalement.
repeat-y
répète verticalement.
CSS propose aussi
space
et
round
pour répartir les motifs plus proprement.
linear-gradient() : dégradé suivant une direction.
radial-gradient() : dégradé depuis un point central.
conic-gradient() : dégradé tournant autour d'un centre.
Plusieurs étapes de couleur peuvent être définies.
Les différentes couches sont séparées par des virgules.
La première couche déclarée se trouve visuellement au-dessus des suivantes.
Cette technique permet notamment de créer un overlay sombre par-dessus une photographie.
background
permet de définir plusieurs propriétés
en une seule déclaration.
Elle peut inclure notamment :
couleur,
image,
position,
taille,
répétition,
attachement.
Dans la syntaxe raccourcie, la taille est placée après la position avec une barre oblique :
center / cover.
background-attachment
détermine comment le fond réagit au défilement.
scroll : comportement normal.
fixed : le fond reste lié au viewport.
local : le fond suit le défilement interne de l'élément.
Les effets avec
fixed
doivent être utilisés avec prudence,
notamment sur mobile.
background-clip détermine jusqu'où le background est peint.
Valeurs courantes :
border-boxpadding-boxcontent-box
Ces propriétés sont particulièrement utiles lorsque l'élément possède bordures et padding.
background-clip: text
permet d'utiliser la forme du texte
comme masque du background.
La couleur du texte doit devenir transparente pour laisser apparaître le background.
Cette technique est souvent utilisée pour créer des titres avec dégradé.
background-blend-mode
contrôle la façon dont plusieurs couches
de background se mélangent.
Quelques modes :
multiplyscreenoverlaydarkenlighten
Cette propriété peut créer des effets graphiques sans modifier directement l'image source.
currentColor
représente la valeur actuelle
de la propriété color.
Elle peut être utilisée dans :
bordures,
ombres,
SVG,
backgrounds,
décorations.
Cela permet de garder plusieurs éléments synchronisés avec une seule couleur principale.
color-mix()
permet de mélanger deux couleurs directement en CSS.
C'est utile pour générer :
• variantes plus claires
• variantes plus sombres
• transparences
• états hover
Cette fonction fonctionne très bien avec les variables CSS.
1. Utiliser
background-image
surtout pour les images décoratives.
2. Pour une image importante pour le contenu, préférer une balise
<img>
avec un texte alternatif adapté.
3. Vérifier le contraste entre texte et arrière-plan.
4. Optimiser les images lourdes.
5. Éviter d'utiliser
opacity
uniquement pour rendre un background transparent,
car les enfants deviennent eux aussi transparents.
6. Préférer une couleur avec alpha lorsque seul le fond doit être translucide.
Pseudo-classes & Pseudo-éléments
CSSLes pseudo-classes ciblent un élément selon son état, sa position ou sa relation avec d'autres éléments. Les pseudo-éléments permettent de styliser une partie particulière d'un élément ou de générer du contenu décoratif sans ajouter de balise HTML.
:hover:focus:first-child
Elle sélectionne un élément selon un état ou une condition.
Un pseudo-élément utilise généralement deux deux-points :
::before::after::first-letter
Il représente une partie virtuelle ou spécifique d'un élément.
:active cible l'élément pendant son activation, par exemple pendant un clic.
:focus cible un élément ayant reçu le focus.
:focus-visible permet d'afficher un indicateur de focus lorsque le navigateur estime qu'il est utile, notamment lors de la navigation au clavier.
Il est important de conserver un focus visible pour l'accessibilité.
:checked
→ checkbox ou radio sélectionné.
:disabled
→ champ désactivé.
:enabled
→ champ actif.
:required
→ champ obligatoire.
:optional
→ champ facultatif.
:valid
et
:invalid
permettent de réagir à la validation native du navigateur.
:last-child cible le dernier.
:only-child cible un élément qui est l'unique enfant de son parent.
:empty cible un élément ne contenant aucun enfant ni texte.
Ces pseudo-classes permettent souvent d'éviter des classes HTML supplémentaires.
:nth-child()
sélectionne un élément selon sa position parmi
tous les enfants du parent.
Exemples :
:nth-child(3)
→ troisième enfant.
:nth-child(odd)
→ positions impaires.
:nth-child(even)
→ positions paires.
:nth-child(3n)
→ tous les trois éléments.
:nth-child(n + 4)
→ à partir du quatrième.
:nth-of-type()
ressemble à :nth-child(),
mais il tient compte du type d'élément.
Par exemple :
p:nth-of-type(2)
cible le deuxième paragraphe parmi les paragraphes frères.
On trouve également :
:first-of-type:last-of-type:only-of-type.
:is() permet de regrouper plusieurs sélecteurs dans une expression plus concise.
:where() fonctionne de manière similaire à
:is(),
mais n'ajoute aucune spécificité propre.
Cela le rend particulièrement intéressant pour les styles de base facilement surchargeables.
Il peut notamment cibler un parent selon son contenu.
Exemple :
.card:has(img)
sélectionne une carte contenant une image.
Il peut aussi réagir à l'état d'un enfant, ce qui permet certains comportements autrefois difficiles à obtenir sans JavaScript.
::before
et
::after
créent des pseudo-éléments associés à un élément.
Ils sont très utilisés pour :
• décorations
• lignes
• badges
• overlays
• icônes visuelles
La propriété
content
doit généralement être définie,
même avec une chaîne vide.
Évitez d'y placer du contenu essentiel à la compréhension de la page : le HTML reste préférable pour le contenu important.
::first-line cible la première ligne affichée.
La première ligne dépend de la largeur disponible : son contenu peut donc changer avec le responsive.
::placeholder cible le texte indicatif affiché dans certains champs de formulaire.
Pour un placeholder, veillez à conserver un contraste suffisant.
Un placeholder ne doit pas remplacer un véritable label de formulaire.
::marker
cible le marqueur d'un élément de liste.
Il peut être utilisé avec les listes
<ul> et <ol>.
Cela permet de personnaliser la couleur, la taille ou certains aspects des puces et numéros sans recréer toute la liste manuellement.
::file-selector-button
permet de styliser le bouton natif
d'un champ :
<input type="file">.
Il évite certaines techniques anciennes consistant à masquer complètement le contrôle natif.
::backdrop
représente la couche située derrière certains éléments
affichés au-dessus de la page.
Il est notamment utilisé avec :
<dialog>
affiché avec
showModal().
Il permet de créer facilement un voile sombre ou un effet de flou.
Quelques principes :
1. Ne pas dépendre uniquement de
:hover,
car les écrans tactiles n'ont pas toujours
de véritable état de survol.
2. Conserver un focus clavier visible.
3. Ne pas placer des informations essentielles uniquement dans
::before ou ::after.
4. Utiliser
:has(),
:is() et :where()
pour simplifier les sélecteurs plutôt que
les rendre excessivement complexes.
5. Préférer le HTML sémantique lorsque l'information possède un véritable sens dans le document.
Responsive & Media Queries
CSSLe responsive design permet d'adapter une interface à l'espace disponible, aux capacités de l'appareil et aux préférences de l'utilisateur. Media queries, tailles fluides, Grid, Container Queries et unités modernes permettent aujourd'hui de créer des interfaces beaucoup plus adaptatives.
Elles peuvent tester :
• la largeur
• la hauteur
• l'orientation
• le type de pointeur
• la présence du survol
• les préférences utilisateur
Le responsive ne se limite donc pas à « mobile / tablette / ordinateur ».
On utilise généralement :
min-width
Les breakpoints ne devraient pas être choisis uniquement parce qu'ils correspondent à un modèle d'appareil.
Il est préférable de les ajouter lorsque le contenu ou la mise en page en a réellement besoin.
Au lieu de :
(min-width: 768px)
on peut écrire :
(width >= 768px).
On peut également définir une plage :
600px <= width < 1000px.
width,
max-width
et les marges automatiques suffisent souvent
pour créer un conteneur fluide.
Une technique moderne consiste à utiliser
min()
afin de limiter automatiquement la largeur.
Syntaxe :
clamp(minimum, valeur idéale, maximum).
min() choisit la plus petite valeur.
max() choisit la plus grande.
Ces fonctions permettent de créer des tailles naturellement fluides.
minmax() : définit une taille minimale et maximale.
auto-fit : crée autant de colonnes que possible et étire les colonnes existantes.
Cela permet parfois de supprimer complètement plusieurs media queries.
Une Container Query permet au contraire à un composant de réagir à la taille de son propre conteneur.
C'est particulièrement utile pour les composants réutilisables placés dans différentes zones : sidebar, contenu principal, dashboard...
Le parent doit généralement déclarer :
container-type: inline-size.
cqw
correspond à 1 % de la largeur du conteneur.
cqh
correspond à 1 % de sa hauteur.
On trouve également :
cqi,
cqb,
cqmin,
cqmax.
Elles permettent de rendre un composant fluide selon son espace disponible plutôt que selon le viewport.
aspect-ratio
définit une proportion entre largeur et hauteur.
Il est très utile pour :
• vidéos
• images
• cartes
• galeries
• placeholders
Associé à
object-fit,
il permet de conserver un cadrage propre des images.
vw et vh
représentent un pourcentage du viewport.
Sur mobile, les barres du navigateur peuvent modifier l'espace réellement disponible.
CSS propose donc plusieurs unités modernes :
svh : petit viewport.
lvh : grand viewport.
dvh : viewport dynamique.
dvh
est particulièrement pratique pour les interfaces
devant suivre la hauteur réellement disponible.
Valeurs :
portraitlandscape
Cette condition peut être utile pour certaines interfaces multimédias ou mises en page très visuelles.
Elle ne doit cependant pas remplacer une mise en page naturellement flexible.
hover : indique si le périphérique principal permet facilement le survol.
pointer : indique la précision du périphérique de pointage.
Valeurs courantes :
fine → pointeur préciscoarse → pointeur moins précis, souvent tactile
C'est plus fiable que de supposer « petit écran = tactile ».
La media query
prefers-reduced-motion
permet de respecter cette préférence.
Cela améliore l'accessibilité, notamment pour certaines personnes sensibles aux mouvements importants.
Il n'est pas toujours nécessaire de supprimer absolument toutes les animations : on peut simplement réduire celles qui sont décoratives ou très prononcées.
Exemples :
margin-inlinemargin-blockpadding-inlinepadding-blockinline-sizeblock-size
Elles facilitent les interfaces adaptatives et les langues utilisant d'autres directions d'écriture.
1. Commencer par une mise en page naturellement fluide.
2. Utiliser Flexbox et Grid avant d'ajouter de nombreuses media queries.
3. Ajouter un breakpoint lorsque le contenu en a réellement besoin.
4. Utiliser
clamp(),
min() et max()
pour les valeurs fluides.
5. Utiliser les Container Queries pour les composants autonomes.
6. Tester les petites et grandes tailles intermédiaires, pas seulement quelques appareils connus.
7. Respecter les préférences d'accessibilité et les capacités réelles du périphérique.
Variables CSS
CSSLes variables CSS, aussi appelées custom properties, permettent de centraliser des couleurs, espacements, tailles, thèmes et valeurs réutilisables tout en profitant de la cascade CSS.
Son nom commence obligatoirement par :
--
Par exemple :
--color-primary--space-md--radius-card
Les variables peuvent contenir de nombreuses sortes de valeurs : couleurs, tailles, ombres, espacements, transitions, gradients ou même plusieurs valeurs CSS.
var()
permet d'utiliser une custom property.
Syntaxe :
var(--nom)
Lorsque la variable change, tous les éléments qui l'utilisent sont automatiquement recalculés par le navigateur.
Cela permet de centraliser le design au lieu de répéter les mêmes valeurs partout.
var()
accepte une valeur de secours.
Syntaxe :
var(--variable, fallback)
Le fallback est utilisé si la custom property demandée n'est pas définie ou n'est pas exploitable.
Il est également possible d'imbriquer plusieurs variables de secours.
Une variable déclarée dans
:root
est généralement disponible dans toute la page.
Une variable déclarée sur un composant est disponible sur cet élément et peut être héritée par ses descendants.
Un descendant peut également redéfinir la même variable.
Cela permet de modifier localement le comportement ou l'apparence d'un composant.
Cela permet de créer un composant générique configurable avec quelques variables.
On évite ainsi de recopier tout le CSS simplement pour changer une couleur ou une taille.
calc().
Cela permet de construire un système cohérent d'espacements ou de tailles.
Une variable peut également contenir uniquement un nombre et être combinée avec une unité dans certains calculs.
Les fonctions modernes comme
min(),
max()
et clamp()
peuvent elles aussi utiliser des variables.
Plutôt que de modifier de nombreuses propriétés, on peut simplement changer quelques variables.
Le reste du CSS continue d'utiliser
var()
sans changement.
On définit une palette par défaut, puis on surcharge seulement les variables nécessaires.
Un attribut comme :
data-theme="dark"
permet de séparer clairement le thème du reste des classes CSS.
prefers-color-scheme
permet de connaître la préférence clair/sombre
configurée dans le système de l'utilisateur.
Les variables CSS permettent ensuite de remplacer toute la palette avec quelques lignes.
La propriété
color-scheme
peut également informer le navigateur
des thèmes pris en charge.
setProperty() : définit une custom property.
getPropertyValue() : permet de lire sa valeur.
Cette technique est utile pour : thèmes, sliders, couleurs personnalisables, tailles dynamiques ou composants interactifs.
On peut définir :
syntax
→ type de valeur attendu.
inherits
→ indique si la propriété doit être héritée.
initial-value
→ valeur initiale.
Cela est notamment utile pour créer des animations plus fiables avec des variables CSS.
Il est préférable d'utiliser des noms décrivant le rôle de la valeur.
Exemples :
--color-primary--color-danger--space-sm--space-md--radius-card--font-size-title
Une organisation cohérente facilite les changements globaux et la maintenance.
Évitez de créer une variable pour chaque propriété simplement pour tout transformer en variable.
Préférez des noms décrivant le rôle :
--color-primary
plutôt que
--blue.
Utilisez des variables globales pour les valeurs communes au design.
Utilisez des variables locales pour rendre les composants configurables.
N'oubliez pas que les custom properties suivent la cascade et l'héritage CSS : cela fait partie de leur puissance.
Sélecteurs CSS
CSSLes sélecteurs CSS permettent de cibler précisément les éléments HTML auxquels appliquer des styles : éléments, classes, identifiants, relations, attributs, états, positions et sélecteurs modernes.
p
cible tous les paragraphes.
Classe : commence par un point.
.card
cible tous les éléments ayant la classe
card.
ID : commence par
#.
#hero
cible l'élément ayant l'identifiant
hero.
Pour les styles réutilisables, les classes sont généralement préférables aux IDs.
Il est souvent utilisé dans les règles globales comme le réglage de
box-sizing.
, : permet de grouper plusieurs sélecteurs partageant les mêmes règles CSS.
Cela évite de répéter le même bloc de propriétés.
« un élément situé quelque part à l'intérieur d'un autre ».
Par exemple :
.card p
cible tous les paragraphes contenus dans
.card.
Il n'est pas nécessaire que le paragraphe soit un enfant direct.
+ : sélectionne le premier frère situé immédiatement après.
~ : sélectionne les frères suivants correspondant au sélecteur.
Ces opérateurs sont appelés combinateurs CSS.
[disabled]
→ attribut présent.
[type="email"]
→ valeur exacte.
[href^="https"]
→ commence par.
[href$=".pdf"]
→ se termine par.
[class*="card"]
→ contient une chaîne.
[class~="active"]
→ contient le mot complet.
Ils sont très utiles avec les formulaires, les liens et les attributs
data-*.
:.
Elle permet de cibler un élément selon son état.
Exemples courants :
:hover → survol souris:focus → élément ayant le focus:focus-visible → focus visuellement pertinent:active → activation:checked → checkbox/radio sélectionné:disabled → champ désactivé:required → champ obligatoire:valid / :invalid → validation formulaire
:first-child
→ premier enfant.
:last-child
→ dernier enfant.
:only-child
→ seul enfant.
:nth-child()
permet des sélections avancées.
Exemples :
:nth-child(2):nth-child(odd):nth-child(even):nth-child(3n)
:nth-of-type()
fonctionne selon le type de balise.
:is() : permet de regrouper plusieurs alternatives dans un sélecteur plus compact.
:where() : fonctionne de manière similaire à
:is(),
mais sa propre spécificité est toujours nulle.
:where()
est donc très pratique pour définir des styles
faciles à surcharger.
Par exemple :
.card:has(img)
cible une carte contenant une image.
form:has(input:invalid)
cible un formulaire contenant un champ invalide.
Cela permet de résoudre certains cas qui nécessitaient autrefois JavaScript.
::.
Ils permettent de cibler une partie particulière d'un élément ou de créer un élément visuel virtuel.
Exemples :
::before::after::first-letter::first-line::selection::placeholder
Avec
::before et ::after,
la propriété content est généralement nécessaire.
.card.active
signifie :
« un élément ayant à la fois les classes
card et active ».
On peut combiner :
élément + classe,
plusieurs classes,
attribut + pseudo-classe,
etc.
De manière simplifiée :
ID est plus spécifique qu'une classe.
Une classe, pseudo-classe ou attribut est plus spécifique qu'un sélecteur d'élément.
Exemple :
#profile
est plus spécifique que
.profile,
qui est plus spécifique que
section.
Si deux règles ont la même spécificité, celle déclarée plus tard gagne généralement.
Il vaut mieux éviter d'augmenter inutilement la spécificité des sélecteurs.
1. Préférer les classes pour les composants réutilisables.
2. Éviter les sélecteurs excessivement longs.
3. Éviter de dépendre trop fortement de la structure HTML.
4. Garder une spécificité raisonnable.
5. Utiliser des noms de classes décrivant le rôle plutôt que l'apparence.
Par exemple :
.user-card
est souvent préférable à
.blue-box.
Bases & Syntaxe
JAVASCRIPTLes fondamentaux de JavaScript : syntaxe, variables, portée, opérateurs, expressions, chaînes modernes, commentaires et outils de débogage.
Instruction : demande à JavaScript d'effectuer une action.
Expression : produit une valeur.
Les accolades
{ }
permettent de créer des blocs de code.
Les points-virgules
;
sont souvent facultatifs grâce à l'insertion automatique
de points-virgules de JavaScript,
mais utiliser une convention cohérente évite les ambiguïtés.
JavaScript est sensible à la casse :
user et User
sont deux identifiants différents.
// crée un commentaire sur une seule ligne.
/* ... */ permet d'écrire un commentaire sur plusieurs lignes.
Les commentaires sont utiles pour expliquer pourquoi une décision a été prise.
Évitez cependant de commenter chaque ligne lorsque le code est déjà évident.
const : choix recommandé lorsqu'une variable ne doit pas être réassignée.
let : utilisé lorsque la variable doit changer.
var : ancien système de déclaration. Il possède une portée de fonction plutôt qu'une portée de bloc et son comportement de hoisting peut être source de confusion.
Dans du JavaScript moderne :
utilisez const par défaut, puis
let lorsque la réassignation est nécessaire.
Attention :
const empêche la réassignation de la variable,
mais ne rend pas automatiquement les objets ou tableaux immuables.
Portée globale : variable accessible depuis une grande partie du programme.
Portée de fonction : variable définie dans une fonction.
Portée de bloc : variable définie entre accolades avec
let ou const.
var ne respecte pas la portée de bloc.
Hoisting : certaines déclarations sont traitées avant l'exécution du code.
let et const
existent avant leur ligne de déclaration,
mais ne peuvent pas être utilisées avant celle-ci :
cette zone est appelée
Temporal Dead Zone.
+ addition- soustraction* multiplication/ division% reste d'une division** puissance
Les parenthèses permettent de contrôler l'ordre des opérations.
Attention :
+ sert également à concaténer
des chaînes de caractères.
=
affecte une valeur à une variable.
Les opérateurs composés permettent de modifier une variable plus rapidement.
+=-=*=/=%=**=
Par exemple :
x += 5
équivaut à :
x = x + 5.
1.
-- : retire
1.
Il existe une différence entre :
counter++
et
++counter.
En utilisation seule, le résultat final est identique.
Dans une expression :
le postfixe retourne d'abord l'ancienne valeur, alors que le préfixe retourne la nouvelle valeur.
` `.
Ils permettent d'insérer une variable ou une expression directement avec :
${expression}
Ils peuvent également contenir plusieurs lignes.
Ils sont généralement plus lisibles que la concaténation de nombreuses chaînes avec l'opérateur
+.
Elle est généralement accessible depuis les outils de développement avec F12.
Méthodes utiles :
console.log()console.warn()console.error()console.table()console.group()console.time()console.timeEnd()
console.table()
est particulièrement pratique pour inspecter
des tableaux d'objets.
En JavaScript, la convention la plus utilisée pour les variables et fonctions est :
camelCase
Exemple :
userName,
totalPrice,
isLoggedIn.
Les classes utilisent généralement : PascalCase.
Exemple :
UserService.
Les constantes réellement fixes et globales peuvent utiliser UPPER_SNAKE_CASE.
Exemple :
MAX_USERS.
Un nom ne peut pas commencer par un chiffre et ne peut pas utiliser un mot réservé comme
class ou return.
Types de Données
JAVASCRIPTJavaScript possède 8 grandes catégories de types : 7 types primitifs (string, number, bigint, boolean, undefined, symbol et null) ainsi que le type object. Comprendre les types est essentiel pour manipuler correctement les valeurs, les objets, les comparaisons et les conversions.
Une chaîne peut être écrite avec :
"double quotes"'simple quotes'`template literals`
Les chaînes JavaScript sont immuables : une méthode ne modifie pas directement la chaîne originale.
Méthodes fréquentes :
toUpperCase(),
toLowerCase(),
trim(),
includes(),
startsWith(),
endsWith(),
slice(),
split(),
replace(),
replaceAll().
JavaScript n'a pas de type séparé
int ou float.
Valeurs numériques particulières :
Infinity-InfinityNaN
Méthodes utiles :
Number.isNaN()Number.isFinite()Number.isInteger()Number.parseInt()Number.parseFloat()
Pour les très grands entiers, utilisez
BigInt.
On ajoute la lettre
n
après le nombre.
Number
possède une limite de précision pour les grands entiers.
Cette limite est disponible avec :
Number.MAX_SAFE_INTEGER.
Attention : un
BigInt et un Number
ne peuvent pas être mélangés directement
dans une opération arithmétique.
true ou false.
Les booléens sont utilisés dans :
conditions, comparaisons, validations, états d'interface, permissions...
Boolean()
permet de convertir explicitement une valeur.
Les principales valeurs falsy sont :
false,
0,
-0,
0n,
"",
null,
undefined
et NaN.
Exemples :
variable non initialisée, propriété inexistante, fonction sans
return.
null : représente généralement une absence de valeur définie volontairement par le développeur.
null et undefined
sont deux types distincts.
null === undefined
retourne false.
Même deux Symbols créés avec la même description sont différents.
Ils peuvent être utilisés comme clés de propriétés d'un objet sans risque de collision avec une clé texte.
JavaScript possède également des Symbols intégrés, comme
Symbol.iterator.
L'index commence à
0.
Les tableaux JavaScript sont des objets spécialisés.
Ainsi :
typeof []
retourne "object".
Pour savoir si une valeur est réellement un tableau, utilisez :
Array.isArray().
Un tableau peut techniquement contenir différents types, même si des données homogènes sont souvent plus simples à manipuler.
Les clés de propriétés sont des chaînes ou des Symbols.
Les valeurs peuvent contenir n'importe quel type : nombres, textes, tableaux, objets ou fonctions.
Une fonction stockée dans un objet est souvent appelée méthode.
Les objets sont des valeurs mutables. Une affectation d'objet vers une autre variable copie la référence vers le même objet, pas l'objet lui-même.
Exemples :
"string""number""bigint""boolean""undefined""symbol""object""function"
Attention à deux particularités historiques :
typeof null
retourne "object".
typeof []
retourne également "object".
Pour un tableau, utilisez donc
Array.isArray().
String() : convertit vers une chaîne.
Number() : convertit vers un nombre.
Boolean() : convertit vers un booléen.
Les conversions implicites peuvent parfois produire des résultats surprenants.
Pour les comparaisons, utilisez généralement
=== plutôt que ==.
string,
number,
bigint,
boolean,
undefined,
symbol
et null.
Lorsqu'une valeur primitive est affectée à une autre variable, sa valeur est copiée.
Pour les objets, la valeur copiée est une référence vers le même objet.
Deux variables peuvent donc pointer vers le même objet.
Une copie superficielle peut être créée avec le spread
{ ...objet }.
Pour certaines structures compatibles,
structuredClone()
permet d'effectuer une copie profonde.
Conditions
JAVASCRIPTLes conditions permettent de contrôler le flux d'exécution d'un programme. JavaScript propose if/else, l'opérateur ternaire, switch et les opérateurs logiques pour décider quel code doit être exécuté.
else if : teste une nouvelle condition lorsque les précédentes sont fausses.
else : s'exécute lorsqu'aucune des conditions précédentes n'est vraie.
Une condition n'est pas obligée de retourner explicitement
true ou false :
JavaScript convertit la valeur en booléen.
Les principales valeurs falsy sont :
false0-00n""nullundefinedNaN
La plupart des autres valeurs sont truthy.
Attention : un tableau vide
[]
et un objet vide {}
sont truthy.
Boolean()
permet de voir explicitement la conversion.
=== : égalité stricte. Compare la valeur et le type.
!== : différence stricte.
>, <, >=, <= comparent des valeurs.
Il est généralement préférable d'utiliser
=== et !==
plutôt que == et !=,
car ces derniers effectuent des conversions implicites.
|| — OR : au moins une condition doit être truthy.
! — NOT : inverse la valeur booléenne.
Ces opérateurs utilisent également le court-circuit : JavaScript arrête l'évaluation dès que le résultat est connu.
Ils ne retournent pas forcément
true ou false :
ils peuvent retourner directement une des valeurs testées.
if.
Avec
&&,
la deuxième expression n'est évaluée que si
la première est truthy.
Avec
||,
la deuxième expression n'est évaluée que si
la première est falsy.
Cela permet parfois d'écrire des expressions concises, mais il ne faut pas sacrifier la lisibilité.
Pour une valeur par défaut qui doit uniquement remplacer
null ou undefined,
?? est souvent préférable à ||.
Syntaxe :
condition ? valeurSiVrai : valeurSiFaux
Contrairement à un bloc
if,
le ternaire produit directement une valeur.
Il est idéal pour une condition simple.
Évitez d'empiler plusieurs ternaires imbriqués lorsque cela réduit la lisibilité.
case.
Il est pratique lorsqu'une même variable peut prendre plusieurs valeurs précises.
break : arrête l'exécution du switch. Sans
break,
JavaScript continue dans le case suivant :
c'est le fall-through.
default : est utilisé lorsqu'aucun case ne correspond.
Les comparaisons de
switch
suivent une égalité stricte.
break
peut parfois être utilisée volontairement.
Plusieurs
case
peuvent ainsi partager le même bloc.
C'est pratique lorsque plusieurs valeurs doivent produire exactement le même comportement.
En revanche, un fall-through accidentel peut provoquer des bugs : il est donc préférable que l'intention soit claire.
Si ce n'est pas le cas, la fonction se termine immédiatement avec
return.
Cela permet souvent d'éviter de nombreux niveaux de
if imbriqués.
Le code principal devient généralement plus lisible, car les cas d'erreur sont traités en premier.
Il est souvent préférable de donner un nom aux différentes parties de la condition.
Des variables booléennes bien nommées rendent le code beaucoup plus explicite.
Utilisez des parenthèses lorsque plusieurs opérateurs
&& et ||
sont combinés.
Pour des règles métier complexes, préférez plusieurs étapes lisibles plutôt qu'une seule expression gigantesque.
JavaScript Asynchrone
JAVASCRIPTJavaScript peut gérer des opérations qui prennent du temps sans bloquer inutilement l'application : timers, requêtes HTTP, Promises, async/await, concurrence et annulation.
Call Stack : contient le code JavaScript actuellement exécuté.
Task Queue : contient notamment les événements et les callbacks de
setTimeout().
Microtask Queue : contient notamment les callbacks des Promises.
Les microtasks sont exécutées avant de passer à la tâche suivante.
setInterval() : répète une fonction régulièrement.
clearTimeout() et clearInterval() permettent d'annuler les timers.
Le délai indiqué n'est pas garanti exactement : si JavaScript est occupé, l'exécution peut avoir lieu plus tard.
Elle possède trois états :
pending → en attentefulfilled → réussierejected → échouée
then() : traite un succès.
catch() : traite une erreur.
finally() : s'exécute dans tous les cas.
await : attend le résultat d'une Promise à l'intérieur d'une fonction asynchrone.
Cela ne bloque pas l'ensemble du navigateur.
try/catch
permet de gérer les erreurs de manière lisible.
Elle retourne une Promise contenant un objet
Response.
Important : une réponse HTTP 404 ou 500 ne rejette pas automatiquement la Promise.
Il faut donc vérifier :
response.ok.
Certaines API comme
fetch()
acceptent cependant un
AbortSignal.
AbortController permet alors d'interrompre la requête.
Cette technique est utile lors d'une navigation, d'une recherche instantanée ou lorsqu'une requête précédente n'est plus nécessaire.
Lorsque plusieurs opérations sont indépendantes, elles peuvent être lancées ensemble puis attendues avec
Promise.all().
Cela réduit souvent le temps total d'attente.
Il s'agit de concurrence asynchrone : cela ne signifie pas automatiquement que plusieurs morceaux de JavaScript sont exécutés simultanément sur plusieurs CPU.
Promise.allSettled() : attend toutes les Promises, même celles qui échouent.
Promise.any() : retourne la première Promise qui réussit.
Promise.race() : retourne la première Promise terminée, qu'elle réussisse ou échoue.
promiseresolvereject
Cette API est surtout utile lorsque la résolution de la Promise doit être déclenchée ailleurs dans le programme.
Elle peut notamment servir à adapter des événements ou des API basées sur des callbacks.
for await...of
permet de parcourir ces valeurs avec une syntaxe simple.
Cette technique est notamment utile pour : streams, générateurs asynchrones, données paginées ou résultats arrivant progressivement.
2. Toujours vérifier
response.ok
avec fetch().
3. Éviter les
await séquentiels
lorsque les opérations sont indépendantes.
4. Annuler les requêtes devenues inutiles quand c'est possible.
5. Attention :
forEach() n'attend pas automatiquement
les callbacks async.
ES6+ JavaScript Moderne
JAVASCRIPTES6, publié en 2015, a profondément modernisé JavaScript. Depuis, ECMAScript continue d'évoluer chaque année. Cette section présente les fonctionnalités modernes essentielles : portée des variables, fonctions fléchées, template literals, déstructuration, spread/rest, optional chaining, objets modernes, Map, Set, classes, modules et fonctionnalités ECMAScript récentes.
let : permet de réassigner la variable.
Contrairement à
var,
let et const
possèdent une
portée de bloc.
Un objet déclaré avec
const
peut toujours être modifié :
c'est la variable elle-même qui ne peut pas
être réassignée.
Elles sont particulièrement utilisées dans : callbacks, méthodes de tableaux, événements, Promises et frameworks.
Si une seule expression est retournée, le mot-clé
return
peut être omis.
Attention : une fonction fléchée ne possède pas son propre
this.
Elle utilise le this
de son contexte extérieur.
`.
Ils permettent d'insérer directement des expressions avec :
${expression}
Ils peuvent également contenir plusieurs lignes.
Les paramètres de fonctions peuvent recevoir une valeur par défaut utilisée lorsque l'argument vaut
undefined.
Elle permet également :
de renommer une variable, définir une valeur par défaut, ignorer certains éléments, récupérer les valeurs restantes.
Elle est très utilisée dans les paramètres de fonctions ainsi que dans React, Vue et Node.js.
...
mais leur rôle est opposé.
Spread : étale des valeurs.
Il permet notamment de copier ou fusionner tableaux et objets.
Rest : collecte plusieurs valeurs restantes dans un tableau ou un objet.
Attention : les copies réalisées avec spread sont superficielles. Les objets imbriqués restent partagés.
null ou undefined.
Le résultat devient alors
undefined
plutôt que de provoquer une erreur.
?? : utilise la valeur de droite uniquement si celle de gauche vaut
null ou undefined.
C'est différent de
||,
qui considère également
0,
""
et false
comme des valeurs falsy.
??= : affecte seulement si la valeur vaut
null ou undefined.
||= : affecte si la valeur est falsy.
&&= : affecte si la valeur est truthy.
Object.keys() : retourne les clés.
Object.values() : retourne les valeurs.
Object.entries() : retourne des couples
[clé, valeur].
Object.fromEntries() : effectue l'opération inverse.
Object.hasOwn() : vérifie qu'une propriété appartient directement à l'objet.
Object.groupBy() : permet de regrouper des valeurs selon une fonction.
Contrairement aux propriétés ordinaires d'un objet, les clés peuvent être de n'importe quel type : objet, fonction, nombre, chaîne...
Set : collection de valeurs uniques.
Il est particulièrement pratique pour supprimer les doublons.
JavaScript moderne fournit également des opérations d'ensembles comme :
union(),
intersection()
et difference().
class
permet de créer des objets à partir d'un modèle.
constructor : initialise l'instance.
extends : permet l'héritage.
super : appelle le constructeur parent.
#propriete : crée un champ privé réellement inaccessible depuis l'extérieur.
static : crée une propriété ou méthode appartenant à la classe plutôt qu'aux instances.
export nommé : plusieurs exports sont possibles dans un fichier.
export default : un seul export par défaut par module.
import() : permet de charger dynamiquement un module, seulement lorsqu'il devient nécessaire.
Dans le navigateur, un module peut être chargé avec :
<script type="module">.
Le top-level await permet également d'utiliser
await
directement au niveau supérieur d'un module.
Number.
Celui-ci ne peut représenter exactement que les entiers situés dans une certaine plage.
BigInt : permet de travailler avec des entiers arbitrairement grands.
Un BigInt se termine par
n.
Séparateurs numériques : le caractère
_
peut améliorer la lisibilité des grands nombres.
Generator : fonction spéciale utilisant
function*.
Elle peut suspendre son exécution avec
yield
puis reprendre plus tard.
Les générateurs permettent notamment de créer des séquences de valeurs à la demande.
Iterator Helpers : permettent d'utiliser des méthodes comme
map(),
filter()
ou take()
directement sur des itérateurs.
RegExp.escape() : échappe automatiquement une chaîne avant de l'intégrer dans une expression régulière.
Array.fromAsync() : construit un tableau à partir d'une source asynchrone.
Math.sumPrecise() : additionne une collection de nombres en réduisant les erreurs de précision liées aux nombres flottants.
Error.isError() : permet de vérifier proprement qu'une valeur représente une erreur.
Pour ces fonctionnalités très récentes, il est recommandé de vérifier la compatibilité de l'environnement ciblé avant utilisation en production.
Méthodes des Tableaux
JAVASCRIPTLes tableaux sont omniprésents en JavaScript. Leurs méthodes permettent de parcourir, transformer, filtrer, rechercher, trier, regrouper et modifier des collections de données. Une distinction essentielle existe entre les méthodes qui modifient le tableau original et celles qui retournent un nouveau tableau.
Contrairement à
map(),
forEach() ne construit pas un nouveau tableau.
Sa valeur de retour est undefined.
Il est adapté lorsque l'on veut produire un effet : afficher une valeur, modifier le DOM, appeler une fonction, etc.
Le callback peut recevoir :
(valeur, index, tableau)
Le tableau retourné possède la même longueur que le tableau original.
map() ne modifie pas directement
le tableau source.
Très utilisé pour transformer des données provenant d'une API ou préparer des données pour une interface.
true.
Le tableau original n'est pas modifié.
Le chaînage :
filter().map()
est extrêmement courant : on sélectionne d'abord les données souhaitées, puis on les transforme.
undefined.
findIndex() : retourne l'index du premier élément correspondant. Sinon, retourne
-1.
findLast() : recherche depuis la fin et retourne la dernière valeur correspondante.
findLastIndex() : retourne l'index de la dernière correspondance.
includes() : vérifie si une valeur existe dans le tableau.
indexOf() : retourne la position d'une valeur ou
-1.
true si
au moins un élément
respecte la condition.
every() : retourne
true uniquement si
tous les éléments
respectent la condition.
Ces méthodes s'arrêtent dès que le résultat peut être déterminé.
Elles sont très utiles pour les validations, permissions, paniers ou formulaires.
Cet accumulateur peut être :
• un nombre
• une chaîne
• un objet
• un tableau
• n'importe quelle autre valeur
Syntaxe :
array.reduce(
(accumulateur, valeur) => résultat,
valeurInitiale
)
Il est conseillé de fournir explicitement une valeur initiale.
push() : ajoute un ou plusieurs éléments à la fin.
pop() : retire et retourne le dernier élément.
unshift() : ajoute au début.
shift() : retire et retourne le premier élément.
Pour créer un nouveau tableau sans modifier l'ancien, on peut utiliser le spread :
[...tableau, valeur].
L'index de fin n'est pas inclus.
splice(index, quantité, ...éléments) : peut supprimer, remplacer ou insérer des éléments.
Attention :
splice()
modifie le tableau original.
toSpliced() : fonctionne sur le même principe mais retourne un nouveau tableau sans modifier l'ancien.
Sans fonction de comparaison, les valeurs sont comparées comme des chaînes.
Pour des nombres :
(a, b) => a - b.
toSorted() : trie mais retourne un nouveau tableau. L'original reste intact.
reverse() : inverse le tableau original.
toReversed() : retourne une copie inversée sans mutation.
Pour du texte destiné aux utilisateurs,
localeCompare()
est souvent préférable.
Son avantage est de permettre les indices négatifs.
array.at(-1)
retourne donc le dernier élément.
with(index, valeur) : crée une copie du tableau dans laquelle un élément est remplacé.
Contrairement à :
array[index] = valeur,
la méthode with()
ne modifie pas le tableau original.
Par défaut, un seul niveau est aplati.
flat(2)
aplatit deux niveaux.
flat(Infinity)
aplatit tous les niveaux.
flatMap() : correspond approximativement à :
map().flat(1)
Il permet donc de transformer puis d'aplatir en une seule opération.
Il ne modifie pas les tableaux originaux.
L'opérateur spread
...
permet également de copier ou fusionner des tableaux.
Attention : ces copies sont superficielles. Les objets présents à l'intérieur restent partagés.
Le séparateur est fourni en argument.
Sans argument, une virgule est utilisée.
Cette méthode est très pratique pour construire des textes, listes, URLs ou affichages.
Exemple : NodeList, chaîne de caractères, Set...
Il accepte également une fonction de transformation.
Array.isArray() : vérifie si une valeur est réellement un tableau.
C'est préférable à :
typeof value,
car un tableau possède également le type
"object" avec typeof.
Modifient l'original :
push()pop()shift()unshift()splice()sort()reverse()
Retournent une nouvelle valeur ou un nouveau tableau :
map()filter()slice()concat()flat()toSorted()toReversed()toSpliced()with()
Les méthodes non mutantes sont particulièrement utiles dans des bibliothèques et frameworks comme React, Vue ou Angular lorsque l'on veut garder des transformations prévisibles.
Manipulation du DOM
JAVASCRIPTManipuler une page HTML avec JavaScript : sélectionner et parcourir les éléments, modifier leur contenu, créer ou supprimer des nœuds, gérer les attributs et les classes, écouter les événements et utiliser la délégation d'événements.
querySelectorAll() : retourne tous les éléments correspondants sous forme de
NodeList.
getElementById() : récupère directement un élément grâce à son identifiant.
Les sélecteurs utilisés sont les mêmes qu'en CSS :
#id,
.classe,
tag,
[attribut],
etc.
Une NodeList retournée par
querySelectorAll()
peut directement être parcourue avec
forEach().
parentElement : retourne l'élément parent.
children : retourne les éléments enfants.
firstElementChild / lastElementChild : premier et dernier enfant.
nextElementSibling : élément HTML suivant au même niveau.
previousElementSibling : élément précédent.
closest() : remonte dans le DOM jusqu'au premier élément correspondant au sélecteur.
matches() : vérifie si un élément correspond à un sélecteur CSS.
C'est généralement le meilleur choix lorsque le contenu provient d'un utilisateur.
innerHTML : lit ou remplace le HTML interne d'un élément.
Il ne faut pas injecter directement dans
innerHTML
des données provenant d'un utilisateur sans les contrôler,
car cela peut provoquer une faille
XSS.
insertAdjacentHTML() : permet d'insérer du HTML sans remplacer tout le contenu.
Positions possibles :
beforebegin,
afterbegin,
beforeend,
afterend.
Il faut ensuite l'insérer dans le DOM.
append() : ajoute à la fin.
prepend() : ajoute au début.
before() / after() : insèrent un élément avant ou après un autre.
remove() : supprime directement un élément.
replaceWith() : remplace un élément par un autre.
cloneNode(true) : crée une copie d'un élément et de ses descendants.
setAttribute() : ajoute ou modifie un attribut.
removeAttribute() : supprime un attribut.
hasAttribute() : vérifie sa présence.
Beaucoup d'éléments possèdent également des propriétés JavaScript accessibles directement :
input.value,
checkbox.checked,
button.disabled.
dataset : permet d'accéder aux attributs HTML
data-*.
Par exemple :
data-user-id="42"
devient
element.dataset.userId.
•
add() : ajouter•
remove() : supprimer•
toggle() : basculer•
contains() : vérifier•
replace() : remplacer
element.style : permet de modifier directement un style inline.
Pour la majorité des changements visuels, il est généralement préférable d'ajouter ou supprimer une classe CSS plutôt que d'écrire de nombreux styles directement en JavaScript.
getComputedStyle() : permet de lire le style final calculé par le navigateur.
Événements fréquents :
click,
input,
change,
submit,
keydown,
keyup,
focus,
blur,
scroll,
resize.
event : objet contenant des informations sur l'événement.
preventDefault() : empêche le comportement par défaut du navigateur.
removeEventListener() : retire un écouteur. Pour cela, il faut conserver une référence vers la fonction utilisée.
Le comportement le plus courant est le bubbling : l'événement part de l'élément cible puis remonte vers ses parents.
event.target : élément qui a réellement déclenché l'événement.
event.currentTarget : élément sur lequel l'écouteur
addEventListener()
est installé.
stopPropagation() : empêche l'événement de continuer sa propagation.
Il faut cependant éviter de l'utiliser sans raison : le bubbling est justement très utile pour la délégation d'événements.
Grâce au bubbling, le parent reçoit également les événements déclenchés par ses descendants.
Cette technique est particulièrement utile pour :
• les grandes listes
• les tableaux
• les menus
• les éléments ajoutés dynamiquement
closest()
permet de déterminer quel bouton ou élément a été cliqué.
DocumentFragment : conteneur temporaire léger permettant de regrouper plusieurs éléments.
Une fois terminé, le fragment est ajouté au DOM en une seule opération.
replaceChildren() : permet de remplacer tous les enfants d'un élément.
Bonnes pratiques :
• éviter les manipulations DOM inutiles
• regrouper les modifications
• conserver les éléments fréquemment utilisés dans des variables
• préférer les classes CSS pour les changements visuels
Closures & Scope avancé
JS AvancéComprendre les closures, le lexical scope, le hoisting et le mot-clé this — fondamentaux pour écrire du JavaScript professionnel.
Comment ça marche : Quand une fonction est créée, elle capture une référence vers son environnement lexical (les variables disponibles au moment de sa création).
Cas d'usage :
• Encapsulation de données (modules)
• Fonctions factory (créer des variantes)
• Callbacks et event handlers
• Mémoïsation / cache
Piège courant : Les closures capturent la référence à la variable, pas sa valeur au moment de la capture.
// Closure basique — compteur privé
function createCounter(initial = 0) {
let count = initial; // Variable "enfermée"
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count,
reset: () => { count = initial; }
};
}
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getValue(); // 12
// count est inaccessible de l'extérieur ✅
// Factory function — créer des variantes
function createMultiplier(factor) {
return (number) => number * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5); // 10
triple(5); // 15
// ⚠️ Piège classique avec var dans une boucle
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 3, 3, 3 (pas 0, 1, 2)
// ✅ Solution avec let (block scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 0, 1, 2Hoisting : JS "remonte" les déclarations en haut de leur portée lors de la compilation :
•
var → déclaration remontée, valeur = undefined•
let/const → remontées mais dans la Temporal Dead Zone (erreur si accédées avant déclaration)•
function déclaration → entièrement remontée (corps inclus)•
function expression → comme var/letPortées : Globale → Fonction → Bloc (let/const)
// ─── Lexical Scope (scope chain) ───
const app = 'NovaDash';
function outer() {
const version = '2.0';
function inner() {
const module = 'crypto';
// Accès : module ✅, version ✅, app ✅
console.log(`${app} v${version} — ${module}`);
}
inner();
// Accès : version ✅, app ✅, module ❌
}
// ─── Hoisting — var vs let ───
console.log(a); // undefined (var est remonté)
console.log(b); // ❌ ReferenceError (TDZ)
var a = 1;
let b = 2;
// ─── Hoisting — fonctions ───
// Déclaration → entièrement remontée
greet(); // ✅ "Hello" — fonctionne !
function greet() { console.log('Hello'); }
// Expression → pas remontée
sayBye(); // ❌ TypeError
const sayBye = () => console.log('Bye');
// ─── Block scope ───
if (true) {
var x = 1; // Fuit hors du bloc
let y = 2; // Reste dans le bloc
const z = 3; // Reste dans le bloc
}
console.log(x); // 1
console.log(y); // ❌ ReferenceErrorRègles de résolution :
1.
new → this = nouvel objet2.
call/apply/bind → this = objet passé3. Méthode d'objet → this = l'objet
4. Fonction libre → this = window (ou undefined en strict)
5. Arrow function → this = celui du scope parent (lexical)
call vs apply vs bind :
•
call(obj, arg1, arg2) → appel immédiat, args séparés•
apply(obj, [args]) → appel immédiat, args en tableau•
bind(obj) → retourne une nouvelle fonction (pas d'appel)
// ─── this selon le contexte ───
const user = {
name: 'Julien',
greet() {
console.log(this.name); // 'Julien'
},
greetLater() {
// ❌ this perdu dans setTimeout
setTimeout(function() {
console.log(this.name); // undefined
}, 100);
// ✅ Arrow function → this lexical
setTimeout(() => {
console.log(this.name); // 'Julien'
}, 100);
}
};
// ─── call / apply / bind ───
function introduce(greeting, emoji) {
console.log(`${greeting} ${this.name} ${emoji}`);
}
const dev = { name: 'Julien' };
// call — args séparés
introduce.call(dev, 'Salut', '👋');
// apply — args en tableau
introduce.apply(dev, ['Salut', '👋']);
// bind — retourne une nouvelle fonction
const boundIntro = introduce.bind(dev);
boundIntro('Hello', '🚀'); // Hello Julien 🚀
// ─── Cas pratique : emprunt de méthode ───
const logger = {
prefix: '[LOG]',
log(msg) { console.log(`${this.prefix} ${msg}`); }
};
const errorLogger = { prefix: '[ERROR]' };
logger.log.call(errorLogger, 'Crash !');
// [ERROR] Crash !Module Pattern : Combine IIFE + closures pour créer des modules avec état privé et API publique. C'était LE pattern avant ES6 modules.
Revealing Module Pattern : Variante plus lisible qui déclare tout en privé puis "révèle" les méthodes publiques dans le return.
Aujourd'hui : Préférer les ES Modules (
import/export), mais le module pattern reste utile pour comprendre les closures et encapsuler du code dans un seul fichier.
// ─── IIFE — Portée isolée ───
(function() {
const secret = 'abc123';
console.log('Initialisé');
// secret n'existe pas dehors
})();
// Avec paramètres
(function(win, doc) {
// Alias locaux pour window et document
})(window, document);
// ─── Module Pattern ───
const CryptoWallet = (function() {
// 🔒 État privé
let balance = 0;
const transactions = [];
// Fonction privée
function log(type, amount) {
transactions.push({ type, amount, date: new Date() });
}
// 🔓 API publique
return {
deposit(amount) {
balance += amount;
log('deposit', amount);
},
withdraw(amount) {
if (amount > balance) throw new Error('Fonds insuffisants');
balance -= amount;
log('withdraw', amount);
},
getBalance: () => balance,
getHistory: () => [...transactions] // copie
};
})();
CryptoWallet.deposit(1000);
CryptoWallet.withdraw(250);
CryptoWallet.getBalance(); // 750
CryptoWallet.getHistory(); // [{...}, {...}]
// CryptoWallet.balance // undefined 🔒
// CryptoWallet.transactions // undefined 🔒Prototypes & Classes avancées
JS AvancéLe système d'héritage prototype de JavaScript et les classes ES6+ qui l'encapsulent. Comprendre les prototypes est essentiel pour maîtriser le langage.
[[Prototype]] vers un autre objet. Quand on accède à une propriété absente, JS remonte la chaîne de prototypes jusqu'à null.__proto__ vs prototype :
•
__proto__ → le prototype de l'instance (utiliser Object.getPrototypeOf())•
.prototype → propriété des fonctions constructeurs, assignée aux instances créées avec newChaîne :
instance → Constructor.prototype → Object.prototype → nullPerformance : Les méthodes sur le prototype sont partagées entre toutes les instances (économie mémoire), contrairement aux méthodes définies dans le constructeur.
// ─── Constructeur + prototype ───
function Crypto(name, symbol) {
this.name = name; // Propre à chaque instance
this.symbol = symbol;
}
// Méthode partagée via prototype
Crypto.prototype.display = function() {
return `${this.name} (${this.symbol})`;
};
const btc = new Crypto('Bitcoin', 'BTC');
const eth = new Crypto('Ethereum', 'ETH');
btc.display(); // "Bitcoin (BTC)"
eth.display(); // "Ethereum (ETH)"
// Même fonction en mémoire
btc.display === eth.display; // true ✅
// ─── Chaîne de prototypes ───
console.log(
Object.getPrototypeOf(btc) === Crypto.prototype, // true
Object.getPrototypeOf(Crypto.prototype) === Object.prototype, // true
Object.getPrototypeOf(Object.prototype) === null // true — fin
);
// ─── Vérifications ───
btc instanceof Crypto; // true
btc.hasOwnProperty('name'); // true (propre)
btc.hasOwnProperty('display'); // false (hérité)
'display' in btc; // true (remonte la chaîne)
// ─── Object.create — héritage sans new ───
const baseConfig = { theme: 'dark', lang: 'fr' };
const userConfig = Object.create(baseConfig);
userConfig.fontSize = 16;
userConfig.theme; // 'dark' (hérité)
userConfig.fontSize; // 16 (propre)Éléments :
•
constructor() → initialisation• Méthodes → automatiquement sur le prototype
•
static → méthode de classe (pas d'instance)•
get/set → propriétés calculées•
extends → héritage•
super() → appel du parentChamps privés (ES2022) : Préfixe
# pour les propriétés/méthodes vraiment privées (pas juste une convention).
class Portfolio {
// Champ privé (ES2022)
#holdings = new Map();
#owner;
constructor(owner) {
this.#owner = owner;
}
// Méthode publique
buy(symbol, amount) {
const current = this.#holdings.get(symbol) || 0;
this.#holdings.set(symbol, current + amount);
}
// Getter
get totalAssets() {
return this.#holdings.size;
}
// Méthode statique
static compare(a, b) {
return a.totalAssets - b.totalAssets;
}
// Méthode privée
#validate(amount) {
if (amount <= 0) throw new Error('Montant invalide');
}
}
// ─── Héritage avec extends ───
class PremiumPortfolio extends Portfolio {
#leverage;
constructor(owner, leverage = 2) {
super(owner); // Appel du constructeur parent
this.#leverage = leverage;
}
// Override de méthode
buy(symbol, amount) {
super.buy(symbol, amount * this.#leverage);
}
}
const p = new PremiumPortfolio('Julien', 3);
p.buy('BTC', 1); // Achète 3 BTC (levier x3)
p.totalAssets; // 1
// p.#holdings // ❌ SyntaxError — privéIterator Protocol : Un objet est itérable s'il implémente
[Symbol.iterator]() qui retourne un objet avec une méthode next(). Permet l'utilisation de for...of, spread, destructuring.Generators : Fonctions avec
function* qui peuvent suspendre leur exécution avec yield. Retournent un iterator. Parfait pour les séquences paresseuses, la pagination, et les flux asynchrones.
// ─── Symbol ───
const id = Symbol('userId');
const user = { [id]: 42, name: 'Julien' };
user[id]; // 42
Object.keys(user); // ['name'] — Symbol non listé
// Symbols globaux partagés
const KEY = Symbol.for('app.key');
Symbol.for('app.key') === KEY; // true
// ─── Iterator personnalisé ───
class CryptoList {
#coins;
constructor(...coins) { this.#coins = coins; }
[Symbol.iterator]() {
let i = 0;
const coins = this.#coins;
return {
next() {
return i < coins.length
? { value: coins[i++], done: false }
: { done: true };
}
};
}
}
const list = new CryptoList('BTC', 'ETH', 'SOL');
for (const coin of list) console.log(coin);
const arr = [...list]; // ['BTC', 'ETH', 'SOL']
// ─── Generator ───
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
fib.next().value; // 0
fib.next().value; // 1
fib.next().value; // 1
fib.next().value; // 2
// Generator pour pagination API
async function* fetchPages(url) {
let page = 1;
while (true) {
const res = await fetch(`${url}?page=${page}`);
const data = await res.json();
if (data.length === 0) return;
yield data;
page++;
}
}Traps courants :
•
get(target, prop) → lecture de propriété•
set(target, prop, value) → écriture•
has(target, prop) → opérateur in•
deleteProperty(target, prop) → delete•
apply(target, thisArg, args) → appel de fonctionReflect : API miroir qui fournit les comportements par défaut des traps. Utilisé dans les handlers pour déléguer au comportement original.
// ─── Proxy — Validation automatique ───
function createValidated(schema) {
return new Proxy({}, {
set(target, prop, value) {
if (schema[prop]) {
const { type, min, max } = schema[prop];
if (typeof value !== type)
throw new TypeError(`${prop} doit être ${type}`);
if (min !== undefined && value < min)
throw new RangeError(`${prop} min: ${min}`);
if (max !== undefined && value > max)
throw new RangeError(`${prop} max: ${max}`);
}
return Reflect.set(target, prop, value);
},
get(target, prop) {
if (!(prop in target))
console.warn(`Propriété "${prop}" non définie`);
return Reflect.get(target, prop);
}
});
}
const trade = createValidated({
amount: { type: 'number', min: 0 },
symbol: { type: 'string' },
leverage: { type: 'number', min: 1, max: 100 }
});
trade.symbol = 'BTC'; // ✅
trade.amount = 1000; // ✅
trade.leverage = 50; // ✅
// trade.amount = -5; // ❌ RangeError
// trade.symbol = 123; // ❌ TypeError
// ─── Proxy — Observable (style Vue.js) ───
function reactive(obj, onChange) {
return new Proxy(obj, {
set(target, prop, value) {
const old = target[prop];
const result = Reflect.set(target, prop, value);
if (old !== value) onChange(prop, value, old);
return result;
}
});
}
const state = reactive({ count: 0 }, (prop, val) => {
console.log(`${prop} changed to ${val}`);
});
state.count = 5; // "count changed to 5"Event Loop & Concurrence
JS AvancéComment JavaScript gère l'asynchrone avec un seul thread. Comprendre l'event loop, les microtasks et macrotasks, et les patterns de concurrence avancés.
Ordre d'exécution :
1. Call Stack — code synchrone en cours
2. Microtask Queue — Promises (.then), queueMicrotask, MutationObserver
3. Macrotask Queue — setTimeout, setInterval, I/O, events DOM
Règle clé : Les microtasks sont TOUJOURS exécutées avant les macrotasks. Après chaque macrotask, TOUTES les microtasks en attente sont vidées.
requestAnimationFrame : Exécuté avant le prochain repaint du navigateur (~60fps). Idéal pour les animations.
// ─── Quiz : quel ordre d'affichage ? ───
console.log('1 — Sync');
setTimeout(() => console.log('2 — Timeout'), 0);
Promise.resolve().then(() => console.log('3 — Promise'));
queueMicrotask(() => console.log('4 — Microtask'));
console.log('5 — Sync');
// Résultat :
// 1 — Sync (call stack)
// 5 — Sync (call stack)
// 3 — Promise (microtask)
// 4 — Microtask (microtask)
// 2 — Timeout (macrotask)
// ─── Pourquoi c'est important ───
// Les microtasks peuvent bloquer le rendu !
Promise.resolve().then(function loop() {
// ⚠️ Boucle infinie de microtasks
// Le navigateur ne peut JAMAIS peindre
Promise.resolve().then(loop);
});
// ─── requestAnimationFrame ───
function animate(element) {
let start = null;
function step(timestamp) {
if (!start) start = timestamp;
const progress = (timestamp - start) / 1000;
element.style.transform =
`translateX(${progress * 200}px)`;
if (progress < 2) {
requestAnimationFrame(step); // ~60fps
}
}
requestAnimationFrame(step);
}•
Promise.all([...]) → attend TOUTES. Échoue si UNE échoue•
Promise.allSettled([...]) → attend TOUTES, jamais d'échec. Retourne le statut de chaque promise•
Promise.race([...]) → retourne la PREMIÈRE (résolue OU rejetée)•
Promise.any([...]) → retourne la PREMIÈRE résolue. Échoue seulement si TOUTES échouentCas d'usage SaaS :
•
all → charger plusieurs API en parallèle•
allSettled → dashboard multi-sources (certaines peuvent échouer)•
race → timeout sur une requête•
any → fallback entre plusieurs serveurs
// ─── Promise.all — tout ou rien ───
const [user, trades, prices] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/trades').then(r => r.json()),
fetch('/api/prices').then(r => r.json())
]);
// Si UNE échoue → tout échoue
// ─── Promise.allSettled — résultats partiels ───
const results = await Promise.allSettled([
fetch('/api/binance'),
fetch('/api/kraken'),
fetch('/api/coinbase')
]);
results.forEach(r => {
if (r.status === 'fulfilled')
console.log('OK:', r.value);
else
console.log('Erreur:', r.reason);
});
// ─── Promise.race — timeout pattern ───
function fetchWithTimeout(url, ms = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
// ─── Promise.any — premier succès ───
const fastest = await Promise.any([
fetch('https://api1.example.com/price'),
fetch('https://api2.example.com/price'),
fetch('https://api3.example.com/price')
]);
// Retourne la première réponse réussie
// ─── Chaînage avancé ───
const portfolio = await fetch('/api/portfolio')
.then(r => { if (!r.ok) throw r; return r.json(); })
.then(data => data.filter(c => c.value > 100))
.then(filtered => filtered.sort((a, b) => b.value - a.value))
.catch(err => { console.error(err); return []; })
.finally(() => console.log('Requête terminée'));Web Workers : Exécutent du JS dans un thread séparé. Communication par messages (
postMessage / onmessage). Pas d'accès au DOM.Types :
• Dedicated Worker — un seul script propriétaire
• Shared Worker — partagé entre plusieurs onglets
• Service Worker — proxy réseau (cache, offline, push notifications)
Transferables : Pour les gros tableaux, utilisez
postMessage(data, [buffer]) pour transférer la mémoire (pas copier) — beaucoup plus rapide.
// ─── Main thread (app.js) ───
const worker = new Worker('analyzer.js');
// Envoyer des données au worker
worker.postMessage({
type: 'ANALYZE',
prices: [95000, 95200, 94800, 95500, 96000]
});
// Recevoir les résultats
worker.onmessage = (event) => {
const { sma, rsi, signal } = event.data;
document.getElementById('signal').textContent = signal;
};
worker.onerror = (err) => console.error('Worker:', err);
// Terminer le worker
// worker.terminate();
// ─── Worker (analyzer.js) ───
self.onmessage = function(event) {
const { type, prices } = event.data;
if (type === 'ANALYZE') {
// Calculs lourds sans bloquer l'UI
const sma = prices.reduce((a, b) => a + b)
/ prices.length;
const rsi = calculateRSI(prices);
const signal = rsi > 70 ? 'SELL' :
rsi < 30 ? 'BUY' : 'HOLD';
// Retourner les résultats
self.postMessage({ sma, rsi, signal });
}
};
function calculateRSI(prices) {
// ... calcul intensif du RSI
return 65;
}
// ─── Inline Worker (sans fichier séparé) ───
const blob = new Blob([`
self.onmessage = (e) => {
const result = e.data * 2;
self.postMessage(result);
};
`], { type: 'application/javascript' });
const inlineWorker = new Worker(URL.createObjectURL(blob));Debounce : Attend que l'utilisateur arrête de taper pendant X ms avant d'exécuter. Idéal pour les recherches.
Throttle : Exécute au maximum une fois toutes les X ms. Idéal pour le scroll, le resize.
Retry avec backoff : Relance une requête échouée avec un délai croissant (exponentiel). Standard pour les API instables.
// ─── AbortController — annuler des requêtes ───
let controller = null;
async function searchCrypto(query) {
// Annuler la requête précédente
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${query}`, {
signal: controller.signal
});
return await res.json();
} catch (err) {
if (err.name === 'AbortError') return; // Ignoré
throw err;
}
}
// ─── Debounce ───
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const debouncedSearch = debounce(searchCrypto, 400);
input.addEventListener('input', e => {
debouncedSearch(e.target.value);
});
// ─── Throttle ───
function throttle(fn, limit = 100) {
let waiting = false;
return (...args) => {
if (waiting) return;
fn(...args);
waiting = true;
setTimeout(() => { waiting = false; }, limit);
};
}
// ─── Retry avec exponential backoff ───
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url);
if (res.ok) return await res.json();
throw new Error(`HTTP ${res.status}`);
} catch (err) {
if (i === retries - 1) throw err;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(r => setTimeout(r, delay));
}
}
}Design Patterns
JS AvancéPatterns architecturaux essentiels pour structurer des applications JavaScript maintenables et scalables.
Factory : Crée des objets sans exposer la logique de création. Le code appelant ne sait pas quelle classe concrète est instanciée — il demande un type et reçoit l'objet.
Quand utiliser :
• Singleton → config, logger, connexion API, cache
• Factory → création d'éléments UI, parsers, notifications
// ─── Singleton avec classe ───
class ApiClient {
static #instance = null;
#baseUrl;
#token;
constructor(baseUrl) {
if (ApiClient.#instance) return ApiClient.#instance;
this.#baseUrl = baseUrl;
this.#token = null;
ApiClient.#instance = this;
}
setToken(token) { this.#token = token; }
async get(path) {
return fetch(`${this.#baseUrl}${path}`, {
headers: { Authorization: `Bearer ${this.#token}` }
}).then(r => r.json());
}
}
const api1 = new ApiClient('https://api.novadash.fr');
const api2 = new ApiClient('https://other.url');
api1 === api2; // true — même instance
// ─── Singleton avec module ES6 (plus simple) ───
// api-client.js
// export default new ApiClient('https://...');
// → Même instance partout grâce au cache du module
// ─── Factory Pattern ───
class Notification {
static create(type, message) {
switch (type) {
case 'success': return new SuccessNotif(message);
case 'error': return new ErrorNotif(message);
case 'warning': return new WarningNotif(message);
default: throw new Error(`Type inconnu: ${type}`);
}
}
}
class SuccessNotif {
constructor(msg) { this.msg = msg; this.color = '#0f0'; }
show() { /* ... */ }
}
class ErrorNotif {
constructor(msg) { this.msg = msg; this.color = '#f00'; }
show() { /* ... */ }
}
const n = Notification.create('success', 'Trade exécuté');
n.show();Pub/Sub (EventEmitter) : Version découplée — les publishers émettent des événements sur un bus central, les subscribers écoutent les événements qui les intéressent. Aucune connaissance directe entre émetteur et récepteur.
Cas d'usage :
• Observer → binding UI, formulaires réactifs
• Pub/Sub → communication entre modules indépendants, microservices frontend, logging centralisé
// ─── Observer Pattern ───
class Observable {
#observers = new Set();
subscribe(fn) {
this.#observers.add(fn);
return () => this.#observers.delete(fn); // unsubscribe
}
notify(data) {
this.#observers.forEach(fn => fn(data));
}
}
// Utilisation
const priceStream = new Observable();
const unsub = priceStream.subscribe(price => {
document.getElementById('btc').textContent = price;
});
priceStream.subscribe(price => {
if (price > 100000) alert('BTC > 100K !');
});
priceStream.notify(95000); // Tous les observers notifiés
unsub(); // Désabonner le premier observer
// ─── EventEmitter (Pub/Sub) ───
class EventBus {
#events = new Map();
on(event, handler) {
if (!this.#events.has(event))
this.#events.set(event, new Set());
this.#events.get(event).add(handler);
return () => this.#events.get(event).delete(handler);
}
emit(event, ...args) {
this.#events.get(event)?.forEach(fn => fn(...args));
}
once(event, handler) {
const unsub = this.on(event, (...args) => {
handler(...args);
unsub();
});
}
}
// Bus global de l'app
const bus = new EventBus();
// Module A — émet
bus.emit('trade:executed', { symbol: 'BTC', amount: 0.5 });
// Module B — écoute (ne connaît pas A)
bus.on('trade:executed', (trade) => {
console.log(`Trade: ${trade.symbol}`);
});if/else ou switch.Command : Encapsule une action en objet, permettant de la stocker, passer en paramètre, annuler (
undo), ou rejouer (redo).En JS idiomatique : Ces patterns se simplifient souvent en objets littéraux + fonctions de premier ordre (pas besoin de classes complexes comme en Java).
// ─── Strategy — Algorithmes interchangeables ───
const sortStrategies = {
price: (a, b) => b.price - a.price,
name: (a, b) => a.name.localeCompare(b.name),
volume: (a, b) => b.volume - a.volume,
change: (a, b) => b.change24h - a.change24h
};
function sortCryptos(cryptos, strategy = 'price') {
return [...cryptos].sort(sortStrategies[strategy]);
}
const coins = [
{ name: 'Bitcoin', price: 95000, volume: 50e9, change24h: 2.1 },
{ name: 'Ethereum', price: 3200, volume: 20e9, change24h: -1.3 },
];
sortCryptos(coins, 'price'); // Par prix
sortCryptos(coins, 'name'); // Alphabétique
sortCryptos(coins, 'change'); // Par variation
// ─── Command — Undo/Redo ───
class CommandManager {
#history = [];
#position = -1;
execute(command) {
// Supprimer le futur si on a fait undo
this.#history.splice(this.#position + 1);
command.execute();
this.#history.push(command);
this.#position++;
}
undo() {
if (this.#position < 0) return;
this.#history[this.#position].undo();
this.#position--;
}
redo() {
if (this.#position >= this.#history.length - 1) return;
this.#position++;
this.#history[this.#position].execute();
}
}
// Commande concrète
const addTrade = (portfolio, trade) => ({
execute: () => portfolio.push(trade),
undo: () => portfolio.pop()
});
const mgr = new CommandManager();
const portfolio = [];
mgr.execute(addTrade(portfolio, { symbol: 'BTC' }));
mgr.execute(addTrade(portfolio, { symbol: 'ETH' }));
mgr.undo(); // Retire ETH
mgr.redo(); // Remet ETHCurrying : Transforme une fonction de N arguments en N fonctions d'un argument. Permet la partial application — pré-remplir certains paramètres pour créer des variantes spécialisées.
Composition : Combiner des fonctions simples pour construire des transformations complexes.
pipe(f, g, h)(x) = h(g(f(x))).
// ─── Memoization ───
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const expensiveCalc = memoize((prices) => {
console.log('Calcul RSI...');
return prices.reduce((a, b) => a + b) / prices.length;
});
expensiveCalc([95, 96, 94]); // "Calcul RSI..." → 95
expensiveCalc([95, 96, 94]); // Cache hit — pas de recalcul
// ─── Currying ───
const curry = (fn) => {
const arity = fn.length;
return function curried(...args) {
if (args.length >= arity) return fn(...args);
return (...more) => curried(...args, ...more);
};
};
const formatPrice = curry((currency, decimals, amount) =>
`${amount.toFixed(decimals)} ${currency}`
);
// Créer des variantes spécialisées
const formatUSD = formatPrice('$', 2);
const formatBTC = formatPrice('BTC', 8);
formatUSD(95000); // "95000.00 $"
formatBTC(0.00045); // "0.00045000 BTC"
// ─── Composition (pipe) ───
const pipe = (...fns) => (x) =>
fns.reduce((acc, fn) => fn(acc), x);
const processPrice = pipe(
(p) => p * 1.2, // Ajouter 20% de marge
(p) => Math.round(p), // Arrondir
(p) => formatUSD(p) // Formater
);
processPrice(100); // "120.00 $"Error. Permet de différencier les types d'erreurs dans les catch et de transporter des données contextuelles (code HTTP, champ invalide...).Gestion globale :
•
window.onerror → erreurs non capturées•
window.onunhandledrejection → Promises non capturées•
try/catch → erreurs synchrones et async/awaitPattern Result : Au lieu de throw, retourner
{ ok, data, error }. Inspiré de Rust/Go — rend la gestion d'erreur explicite sans exceptions.
// ─── Erreurs personnalisées ───
class AppError extends Error {
constructor(message, code, details = {}) {
super(message);
this.name = 'AppError';
this.code = code;
this.details = details;
this.timestamp = new Date().toISOString();
}
}
class ValidationError extends AppError {
constructor(field, message) {
super(message, 'VALIDATION_ERROR', { field });
this.name = 'ValidationError';
}
}
class ApiError extends AppError {
constructor(status, message) {
super(message, 'API_ERROR', { status });
this.name = 'ApiError';
}
}
// Utilisation
async function createTrade(data) {
if (!data.symbol)
throw new ValidationError('symbol', 'Symbole requis');
const res = await fetch('/api/trade', { method: 'POST' });
if (!res.ok)
throw new ApiError(res.status, 'Trade échoué');
return res.json();
}
try {
await createTrade({});
} catch (err) {
if (err instanceof ValidationError) {
showFieldError(err.details.field, err.message);
} else if (err instanceof ApiError) {
showToast(`Erreur serveur (${err.details.status})`);
} else {
throw err; // Erreur inattendue → remonter
}
}
// ─── Pattern Result (sans exceptions) ───
async function safeFetch(url) {
try {
const res = await fetch(url);
const data = await res.json();
return { ok: true, data };
} catch (error) {
return { ok: false, error };
}
}
const result = await safeFetch('/api/price/BTC');
if (result.ok) console.log(result.data);
else console.error(result.error);
// ─── Gestion globale ───
window.addEventListener('unhandledrejection', (e) => {
console.error('Promise non gérée:', e.reason);
// Envoyer au service de monitoring
});Web APIs avancées
JS AvancéAPIs navigateur puissantes pour le stockage, les communications temps réel, les notifications et l'observation du DOM.
sessionStorage : Idem mais effacé à la fermeture de l'onglet. Utile pour les données temporaires de session.
IndexedDB : Base de données objet côté client. Asynchrone, transactionnelle, supporte les index et les requêtes. Pas de limite de taille pratique. Pour les apps offline, le cache de données volumineuses.
Cookies vs Storage : Les cookies sont envoyés à chaque requête HTTP (overhead réseau). localStorage/sessionStorage ne sont jamais envoyés au serveur.
// ─── localStorage ───
// Stocker (toujours en string)
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({
name: 'Julien', plan: 'premium'
}));
// Lire
const theme = localStorage.getItem('theme');
const user = JSON.parse(localStorage.getItem('user'));
// Supprimer
localStorage.removeItem('theme');
localStorage.clear(); // Tout supprimer
// Écouter les changements (entre onglets !)
window.addEventListener('storage', (e) => {
console.log(`${e.key}: ${e.oldValue} → ${e.newValue}`);
});
// ─── Wrapper typé avec expiration ───
const store = {
set(key, value, ttlMinutes = null) {
const item = { value, created: Date.now() };
if (ttlMinutes) item.expires = Date.now() + ttlMinutes * 60000;
localStorage.setItem(key, JSON.stringify(item));
},
get(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const item = JSON.parse(raw);
if (item.expires && Date.now() > item.expires) {
localStorage.removeItem(key);
return null; // Expiré
}
return item.value;
}
};
store.set('prices', { BTC: 95000 }, 5); // Expire dans 5min
store.get('prices'); // { BTC: 95000 } ou null
// ─── IndexedDB (simplifié) ───
async function openDB(name, version = 1) {
return new Promise((resolve, reject) => {
const req = indexedDB.open(name, version);
req.onupgradeneeded = (e) => {
const db = e.target.result;
db.createObjectStore('trades', { keyPath: 'id' });
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
const db = await openDB('NovaDash');
const tx = db.transaction('trades', 'readwrite');
tx.objectStore('trades').add({ id: 1, symbol: 'BTC' });Server-Sent Events (SSE) : Flux unidirectionnel du serveur vers le client. Plus simple que WebSocket, reconnexion automatique, fonctionne avec HTTP standard. Idéal pour les notifications, les feeds en direct.
Quand choisir :
• WebSocket → bidirectionnel (chat, trading, jeu)
• SSE → serveur → client uniquement (notifications, prix live)
• Polling → fallback si WS/SSE impossibles
// ─── WebSocket ───
class CryptoSocket {
#ws; #url; #reconnectDelay = 1000;
constructor(url) { this.#url = url; this.connect(); }
connect() {
this.#ws = new WebSocket(this.#url);
this.#ws.onopen = () => {
console.log('Connecté');
this.#ws.send(JSON.stringify({
action: 'subscribe',
channels: ['BTC-USD', 'ETH-USD']
}));
};
this.#ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.updateUI(data);
};
this.#ws.onclose = () => {
console.log('Déconnecté — reconnexion...');
setTimeout(() => this.connect(), this.#reconnectDelay);
this.#reconnectDelay = Math.min(
this.#reconnectDelay * 2, 30000
);
};
}
send(data) {
if (this.#ws.readyState === WebSocket.OPEN) {
this.#ws.send(JSON.stringify(data));
}
}
updateUI(data) {
document.querySelector(`#${data.symbol}`)
.textContent = data.price;
}
}
const ws = new CryptoSocket('wss://stream.exchange.com');
// ─── Server-Sent Events ───
const source = new EventSource('/api/stream/prices');
source.onmessage = (event) => {
const price = JSON.parse(event.data);
console.log(`${price.symbol}: ${price.value}`);
};
// Événements nommés
source.addEventListener('alert', (e) => {
const alert = JSON.parse(e.data);
showNotification(alert.message);
});
source.onerror = () => console.log('SSE reconnexion...');
// source.close(); // FermerResizeObserver : Détecte les changements de taille d'un élément. Plus précis que
window.resize — observe un élément spécifique, pas toute la fenêtre.MutationObserver : Observe les changements du DOM (ajout/suppression d'éléments, attributs, texte). Utile pour les composants tiers, les extensions, le monitoring de changements dynamiques.
Avantage : Tous les observers sont asynchrones et performants — ils ne bloquent pas le thread principal contrairement aux événements scroll/resize classiques.
// ─── IntersectionObserver — Lazy loading ───
const lazyObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
lazyObserver.unobserve(img); // Stop
}
});
},
{ rootMargin: '200px' } // Précharger 200px avant
);
document.querySelectorAll('img[data-src]')
.forEach(img => lazyObserver.observe(img));
// ─── Animate on scroll ───
const animObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
entry.target.classList.toggle(
'visible', entry.isIntersecting
);
});
},
{ threshold: 0.2 } // 20% visible
);
document.querySelectorAll('.el-card')
.forEach(el => animObserver.observe(el));
// ─── ResizeObserver ───
const resizer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width } = entry.contentRect;
entry.target.classList.toggle('compact', width < 400);
}
});
resizer.observe(document.querySelector('.dashboard'));
// ─── MutationObserver ───
const mutObserver = new MutationObserver((mutations) => {
mutations.forEach(m => {
if (m.type === 'childList') {
console.log('Nœuds ajoutés:', m.addedNodes.length);
}
});
});
mutObserver.observe(document.body, {
childList: true, subtree: true
});writeText() pour copier, readText() pour coller (demande permission).Notifications API : Affiche des notifications système (même quand l'onglet est inactif). Requiert la permission de l'utilisateur. Combinée avec les Service Workers pour les push notifications.
Fullscreen API : Passe un élément en plein écran. Utile pour les dashboards, les graphiques, les jeux et les présentations.
Vibration API : Fait vibrer l'appareil mobile. Utile pour les alertes de prix.
// ─── Clipboard API ───
// Copier du texte
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast('Copié !');
} catch (err) {
// Fallback pour HTTP
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
}
}
// Bouton "copier l'adresse wallet"
copyBtn.onclick = () => copyToClipboard('0xABC...DEF');
// ─── Notifications API ───
async function sendNotification(title, body) {
if (Notification.permission === 'default') {
await Notification.requestPermission();
}
if (Notification.permission === 'granted') {
new Notification(title, {
body,
icon: '/img/logo.png',
badge: '/img/badge.png',
tag: 'price-alert', // Remplace la précédente
requireInteraction: true
});
}
}
sendNotification('BTC Alert', 'Bitcoin > 100K $ !');
// ─── Fullscreen API ───
const chart = document.getElementById('chart');
function toggleFullscreen(element) {
if (!document.fullscreenElement) {
element.requestFullscreen();
} else {
document.exitFullscreen();
}
}
document.addEventListener('fullscreenchange', () => {
chart.classList.toggle('fullscreen',
!!document.fullscreenElement);
});
// ─── Vibration (mobile) ───
navigator.vibrate?.(200); // Vibrer 200ms
navigator.vibrate?.([100, 50, 100]); // PatternÉvénements source (élément glissé) :
•
dragstart → début du drag•
drag → pendant le mouvement•
dragend → fin (drop ou annulation)Événements cible (zone de dépôt) :
•
dragenter → entre dans la zone•
dragover → au-dessus (preventDefault obligatoire !)•
dragleave → sort de la zone•
drop → élément relâchéDataTransfer : Objet qui transporte les données entre la source et la cible. Supporte du texte, du HTML, des fichiers.
// ─── Drag & Drop — Liste réordonneable ───
// Rendre les éléments draggables
document.querySelectorAll('.trade-item').forEach(item => {
item.draggable = true;
item.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', item.id);
item.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
});
item.addEventListener('dragend', () => {
item.classList.remove('dragging');
});
});
// Zone de dépôt
const dropzone = document.querySelector('.portfolio');
dropzone.addEventListener('dragover', (e) => {
e.preventDefault(); // OBLIGATOIRE pour autoriser le drop
e.dataTransfer.dropEffect = 'move';
// Trouver l'élément le plus proche pour insérer
const afterEl = getDragAfterElement(dropzone, e.clientY);
const dragging = document.querySelector('.dragging');
if (afterEl) {
dropzone.insertBefore(dragging, afterEl);
} else {
dropzone.appendChild(dragging);
}
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
console.log(`Déposé : ${id}`);
});
function getDragAfterElement(container, y) {
const elements = [...container.querySelectorAll(
'.trade-item:not(.dragging)')
];
return elements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset, element: child };
}
return closest;
}, { offset: Number.NEGATIVE_INFINITY }).element;
}Performance & Sécurité
JS AvancéOptimisations de performance, bonnes pratiques de sécurité et outils de mesure pour des applications JavaScript de qualité production.
performance.now(), performance.mark() et performance.measure().Lazy Loading : Charger les ressources à la demande. Images avec
loading="lazy", modules avec import() dynamique, composants au scroll.DOM Batching : Minimiser les accès au DOM. Regrouper les lectures puis les écritures. Utiliser
DocumentFragment pour les insertions multiples.Web Vitals :
• LCP (Largest Contentful Paint) < 2.5s
• FID (First Input Delay) < 100ms
• CLS (Cumulative Layout Shift) < 0.1
// ─── Performance API ───
performance.mark('start-fetch');
const data = await fetch('/api/data').then(r => r.json());
performance.mark('end-fetch');
performance.measure('API Call', 'start-fetch', 'end-fetch');
const [measure] = performance.getEntriesByName('API Call');
console.log(`API: ${measure.duration.toFixed(2)}ms`);
// ─── Import dynamique (code splitting) ───
button.onclick = async () => {
const { ChartModule } = await import('./chart.js');
ChartModule.render(data);
};
// ─── DOM Batching avec DocumentFragment ───
function renderList(items) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = `${item.name}: ${item.price}$`;
fragment.appendChild(li);
});
// UN SEUL accès DOM au lieu de N
document.getElementById('list').appendChild(fragment);
}
// ─── Virtual Scroll (principe) ───
function virtualScroll(container, items, itemHeight) {
const visibleCount = Math.ceil(
container.clientHeight / itemHeight
) + 2; // Buffer
container.onscroll = () => {
const scrollTop = container.scrollTop;
const startIdx = Math.floor(scrollTop / itemHeight);
const visibleItems = items.slice(
startIdx, startIdx + visibleCount
);
renderList(visibleItems);
// Spacer pour le scroll
container.style.paddingTop =
`${startIdx * itemHeight}px`;
};
}
// ─── requestIdleCallback ───
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0) {
// Tâches non urgentes (analytics, préchargement)
doBackgroundWork();
}
});innerHTML avec des données non fiables. Utiliser textContent ou sanitizer.CSRF : Requête forgée par un site tiers. Protection : tokens CSRF, cookie SameSite, vérification Origin/Referer.
Content Security Policy (CSP) : En-tête HTTP qui restreint les sources de scripts, styles, images. Bloque les injections même si XSS réussit.
Bonnes pratiques :
•
textContent au lieu de innerHTML• Valider côté client ET serveur
• HttpOnly + Secure sur les cookies sensibles
• CORS restrictif
// ─── XSS — Le problème ───
const userInput = '<img onerror="alert(1)" src=x>';
// ❌ DANGEREUX — exécute le script
element.innerHTML = userInput;
// ✅ SAFE — échappe automatiquement
element.textContent = userInput;
// ─── Sanitization manuelle ───
function sanitizeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
// <script> → <script>
}
// ─── Sanitizer API (moderne) ───
// const sanitizer = new Sanitizer();
// element.setHTML(userInput, { sanitizer });
// ─── Échapper pour les URL ───
const query = encodeURIComponent(userInput);
const url = `/search?q=${query}`;
// Empêche l'injection dans l'URL
// ─── Protection CSRF ───
async function secureFetch(url, options = {}) {
const csrfToken = document.querySelector(
'meta[name="csrf-token"]'
)?.content;
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
},
credentials: 'same-origin' // Cookies same-origin
});
}
// ─── Validation d'entrée ───
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!re.test(email)) throw new Error('Email invalide');
if (email.length > 254) throw new Error('Email trop long');
return email.toLowerCase().trim();
}
// ─── Stockage sécurisé ───
// ❌ Ne JAMAIS stocker en localStorage :
// - Tokens JWT sensibles
// - Mots de passe
// - Données personnelles en clair
// → Utiliser des cookies HttpOnly + SecureWeakMap : Map dont les clés sont des références faibles. Si l'objet clé n'a plus d'autre référence, il peut être GC. Pas énumérable, pas de
.size. Idéal pour associer des métadonnées à des objets DOM.WeakSet : Comme Set mais avec des références faibles. Utile pour marquer des objets (visité, traité...).
WeakRef (ES2021) : Référence faible vers un objet.
.deref() retourne l'objet ou undefined s'il a été GC. Utile pour les caches intelligents.
// ─── WeakMap — métadonnées sans memory leak ───
const metadata = new WeakMap();
function trackElement(element) {
metadata.set(element, {
clickCount: 0,
createdAt: Date.now()
});
element.addEventListener('click', () => {
const data = metadata.get(element);
data.clickCount++;
});
}
// Si l'élément est supprimé du DOM,
// les métadonnées sont automatiquement GC ✅
// ─── WeakMap — données privées de classe ───
const _private = new WeakMap();
class Wallet {
constructor(balance) {
_private.set(this, { balance, pin: null });
}
get balance() { return _private.get(this).balance; }
setPin(pin) { _private.get(this).pin = pin; }
}
const w = new Wallet(1000);
w.balance; // 1000
_private.get(w); // { balance: 1000, pin: null }
// Inaccessible sans la référence _private
// ─── WeakRef — Cache intelligent ───
class SmartCache {
#cache = new Map();
set(key, value) {
this.#cache.set(key, new WeakRef(value));
}
get(key) {
const ref = this.#cache.get(key);
if (!ref) return undefined;
const obj = ref.deref();
if (!obj) {
this.#cache.delete(key); // GC a nettoyé
return undefined;
}
return obj;
}
}
// ─── Éviter les memory leaks ───
// ❌ Event listener jamais supprimé
// element.addEventListener('click', handler);
// ✅ Nettoyer à la destruction
const controller = new AbortController();
element.addEventListener('click', handler, {
signal: controller.signal
});
// Plus tard : controller.abort(); // Supprime le listenerJSON.parse(JSON.stringify()) qui ne gère ni les dates, ni les undefined, ni les références circulaires.JSON avancé :
JSON.stringify accepte un replacer (filtre) et un spacer. JSON.parse accepte un reviver pour transformer les valeurs à la lecture.Intl (Internationalisation) : API native pour formater les nombres, monnaies, dates, listes et messages selon la locale. Plus besoin de librairies pour le formatage basique.
// ─── structuredClone ───
const original = {
user: { name: 'Julien' },
trades: [{ symbol: 'BTC', date: new Date() }],
meta: new Map([['version', 2]])
};
// Clone profond — indépendant de l'original
const clone = structuredClone(original);
clone.user.name = 'Alice';
original.user.name; // 'Julien' — non affecté ✅
clone.trades[0].date instanceof Date; // true ✅
// ─── JSON avancé ───
// Replacer — filtrer/transformer
const json = JSON.stringify(original, (key, value) => {
if (key === 'pin') return undefined; // Exclure
if (value instanceof Date) return value.toISOString();
return value;
}, 2); // 2 = indentation
// Reviver — transformer à la lecture
const parsed = JSON.parse(json, (key, value) => {
// Reconvertir les dates ISO en objets Date
if (typeof value === 'string' &&
/\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
// ─── Intl — Formatage localisé ───
// Monnaie
new Intl.NumberFormat('fr-FR', {
style: 'currency', currency: 'EUR'
}).format(95000); // "95 000,00 €"
new Intl.NumberFormat('en-US', {
style: 'currency', currency: 'USD',
notation: 'compact'
}).format(95000); // "$95K"
// Date
new Intl.DateTimeFormat('fr-FR', {
dateStyle: 'full', timeStyle: 'short'
}).format(new Date()); // "vendredi 28 février 2026 à 14:30"
// Relatif
new Intl.RelativeTimeFormat('fr', { numeric: 'auto' })
.format(-1, 'day'); // "hier"
// Liste
new Intl.ListFormat('fr', { type: 'conjunction' })
.format(['BTC', 'ETH', 'SOL']); // "BTC, ETH et SOL"Introduction à TypeScript
TYPESCRIPTTypeScript est un surensemble typé de JavaScript. Il compile vers du JavaScript standard.
Avantages :
• Détection des bugs avant l'exécution
• Autocomplétion IDE et documentation intégrée
• Refactoring sécurisé sur de gros projets
• Standard industrie SaaS (Angular, Next.js, Nest.js)
: Type après le nom d'une variable, paramètre ou propriété.Inférence : TypeScript peut souvent deviner le type automatiquement. Annotez surtout les paramètres de fonctions — les variables locales initiálisées n'en ont souvent pas besoin.
Interfaces TypeScript
TYPESCRIPTLes interfaces définissent la forme des objets. Elles sont le pilier de la structuration de données en TypeScript.
Interface vs type :
interface pour les objets (extensible avec extends, declaration merging). type pour les unions, intersections et types complexes.extends permet à une interface d'hériter des propriétés d'une ou plusieurs autres interfaces.Héritage multiple : Contrairement aux classes, une interface peut étendre plusieurs interfaces simultanément.
Types Avancés
TSUnion, literal types, keyof, typeof et discriminated unions.
Narrowing : TypeScript rétrécit automatiquement le type dans les branches conditionnelles après une vérification.
type Status = "active" | "pending" | "deleted";
function processId(id: string | number) {
if (typeof id === "string") {
id.toUpperCase(); // ✅ string ici
} else {
id.toFixed(2); // ✅ number ici
}
}
string ou number — TypeScript vérifie que seules ces valeurs sont utilisées.Combiné avec union : Créer des types d'état finis, très utiles pour les SaaS.
type Direction = "up" | "down" | "left" | "right";
// Type d'état SaaS
type OrderStatus =
| "cart"
| "checkout"
| "paid"
| "shipped"
| "delivered";
function move(dir: Direction) { }
move("up"); // ✅
move("jump"); // ❌ Erreur TS
typeof : Extrait le type TypeScript d'une valeur ou variable. Utile pour typer à partir de valeurs existantes sans redéfinir.
interface User { nom: string; age: number; }
type UserKey = keyof User; // "nom" | "age"
function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// typeof
const config = { port: 3000, host: "localhost" };
type Config = typeof config;
// { port: number; host: string; }
C'est le pattern de base pour les state machines TypeScript.
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: string };
function render(state: AsyncState<User>) {
switch (state.status) {
case "success": return state.data; // ✅ data typée
case "error": return state.error; // ✅ error typée
}
}
Generics
TYPESCRIPTLes Generics permettent de créer des composants réutilisables tout en conservant la sécurité du typage.
<T> est un paramètre de type — un placeholder remplacé par le type réel à l'utilisation.Avantage vs any : Contrairement à
any, les generics conservent la relation entre les types. TypeScript peut vérifier l'utilisation des données.Pattern SaaS : Un type
ApiResponse<T> générique réutilisé pour toutes les réponses API garantit la cohérence.Utility Types
TSTypes utilitaires intégrés pour transformer les types existants.
Implémentation interne :
{ [P in keyof T]?: T[P] }nom: string;
email: string;
role: string;
}
// Mise à jour partielle
function updateUser(id: string, data: Partial<User>) {
// data peut avoir 0, 1 ou 3 propriétés
}
updateUser("1", { email: "new@mail.com" }); // ✅
Omit<T, K> : Exclut les propriétés listées dans K. L'inverse de Pick. Très utilisé pour créer des DTOs.
id: number; nom: string; email: string;
password: string; createdAt: Date;
}
// Aperçu public
type UserCard = Pick<User, "id" | "nom">;
// Création (sans id et createdAt auto)
type CreateUserDTO = Omit<User, "id" | "createdAt">;
// DTO public (sans mot de passe)
type PublicUser = Omit<User, "password">;
{ [key: string]: value }.Particulièrement utile : Pour des maps, caches, tables de correspondance.
type Scores = Record<string, number>;
const scores: Scores = { alice: 95, bob: 87 };
// Clés typées (union)
type Status = "active" | "pending" | "deleted";
type StatusConfig = Record<Status, { label: string; color: string }>;
const config: StatusConfig = {
active: { label: "Actif", color: "green" },
pending: { label: "En attente", color: "yellow" },
deleted: { label: "Supprimé", color: "red" },
};
Parameters<T> : Extrait les types des paramètres en tuple.
Awaited<T> : Extrait le type résolu d'une Promise.
return { id, nom: "Julien", role: "admin" };
}
// Extraire le type de retour
type FetchResult = ReturnType<typeof fetchUser>;
// Promise<{ id: string; nom: string; role: string }>
// Résoudre la Promise
type User = Awaited<FetchResult>;
// { id: string; nom: string; role: string }
React — Fondamentaux
Frameworks JSReact est une bibliothèque JavaScript permettant de construire des interfaces utilisateur déclaratives à partir de composants. React moderne utilise principalement les composants fonctionnels, les Hooks et une gestion déclarative de l'état. Écosystème : Vite, Next.js, React Router, TanStack Query, Redux Toolkit et Zustand.
JSX : syntaxe permettant d'écrire une structure proche du HTML directement dans JavaScript.
Props : données transmises d'un composant parent vers un composant enfant.
Règles JSX :
• les composants commencent par une majuscule
• utiliser
className à la place de
class• utiliser
htmlFor à la place de
for• les expressions JavaScript sont placées dans
{ }• plusieurs éléments peuvent être regroupés avec
<> ... </>
function UserCard({
name,
role,
avatar
}) {
return (
<article className="card">
<img
src={avatar}
alt={`Avatar de ${name}`}
/>
<h3>
{name}
</h3>
<p>
{role}
</p>
</article>
);
}
// Utilisation
<UserCard
name="Julien"
role="Développeur React"
avatar="/images/avatar.png"
/>
// Fragment : pas de div supplémentaire
function Stats() {
return (
<>
<h2>
Statistiques
</h2>
<p>
Total : 42
</p>
</>
);
}
Modifier l'état avec sa fonction setter demande à React d'effectuer un nouveau rendu.
État précédent : lorsqu'une nouvelle valeur dépend de l'ancienne, il est préférable d'utiliser une fonction :
setCount(count => count + 1).
Les objets et tableaux présents dans l'état doivent être remplacés plutôt que modifiés directement.
Les événements React utilisent une syntaxe comme
onClick, onChange,
onSubmit ou onMouseEnter.
import {
useState
} from 'react';
function Counter() {
const [
count,
setCount
] = useState(0);
const [
user,
setUser
] = useState({
name: 'Julien',
score: 0
});
function increment() {
setCount(
previous => previous + 1
);
}
function addScore() {
setUser(previous => ({
...previous,
score:
previous.score + 10
}));
}
return (
<div>
<p>
Compteur : {count}
</p>
<button
onClick={increment}
>
+1
</button>
<p>
{user.name} :
{user.score} points
</p>
<button
onClick={addScore}
>
+10 points
</button>
</div>
);
}
La propriété
value reçoit la valeur de l'état
et onChange met cet état à jour.
Cela crée une circulation :
état React → input → événement → état React
onSubmit permet d'intercepter
l'envoi du formulaire.
import {
useState
} from 'react';
function LoginForm() {
const [
email,
setEmail
] = useState('');
const [
password,
setPassword
] = useState('');
function handleSubmit(event) {
event.preventDefault();
console.log({
email,
password
});
}
return (
<form
onSubmit={handleSubmit}
>
<input
type="email"
value={email}
onChange={
event =>
setEmail(
event.target.value
)
}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={
event =>
setPassword(
event.target.value
)
}
placeholder="Mot de passe"
/>
<button
type="submit"
>
Connexion
</button>
</form>
);
}
Un
if peut être utilisé avant le
return.
À l'intérieur du JSX, on utilise souvent :
• l'opérateur ternaire
? :•
&& pour un affichage conditionnel•
.map() pour afficher une liste
Chaque élément d'une liste doit posséder une
key stable et unique.
function Dashboard({
user,
transactions
}) {
if (!user) {
return (
<p>
Connectez-vous
</p>
);
}
return (
<div>
<h2>
Bonjour {user.name}
</h2>
{user.isAdmin && (
<button>
Administration
</button>
)}
{transactions.length > 0 ? (
<ul>
{transactions.map(
transaction => (
<li
key={transaction.id}
>
{transaction.label}
{' — '}
{transaction.amount} €
</li>
)
)}
</ul>
) : (
<p>
Aucune transaction
</p>
)}
</div>
);
}
Exemples : API, timer, WebSocket, événements du navigateur ou bibliothèque externe.
Dépendances :
• pas de tableau → après chaque rendu concerné
•
[value] → lorsque cette valeur change•
[] → aucune dépendance réactive
En mode développement avec Strict Mode, React peut volontairement effectuer un cycle setup + cleanup supplémentaire pour détecter les effets mal nettoyés.
Si aucune synchronisation externe n'est nécessaire, un Effect n'est souvent pas nécessaire.
import {
useEffect,
useState
} from 'react';
function CryptoPrice({
symbol
}) {
const [
price,
setPrice
] = useState(null);
useEffect(() => {
const controller =
new AbortController();
async function loadPrice() {
try {
const response =
await fetch(
`/api/crypto/${symbol}`,
{
signal:
controller.signal
}
);
const data =
await response.json();
setPrice(
data.price
);
}
catch (error) {
if (
error.name !==
'AbortError'
) {
console.error(error);
}
}
}
loadPrice();
return () => {
controller.abort();
};
}, [symbol]);
return (
<p>
{symbol} :
{price ?? 'Chargement...'}
</p>
);
}
// Exemple avec un timer
useEffect(() => {
const id =
setInterval(() => {
console.log(
'Actualisation'
);
}, 5000);
return () => {
clearInterval(id);
};
}, []);
Sa propriété est accessible avec
ref.current.
Modifier une ref ne déclenche pas de nouveau rendu.
Deux usages courants :
• accéder directement à un élément du DOM
• mémoriser une information qui ne doit pas être affichée dans l'interface, comme un identifiant de timer.
import {
useRef
} from 'react';
function SearchBar() {
const inputRef =
useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<div>
<input
ref={inputRef}
placeholder="Rechercher..."
/>
<button
onClick={focusInput}
>
Focus
</button>
</div>
);
}
useState() lorsque la logique
de modification de l'état devient plus complexe.
Un reducer reçoit :
state + action → nouvel état
Context : permet de fournir une valeur à plusieurs composants descendants sans transmettre manuellement la même prop à chaque niveau.
Depuis React 19, le contexte lui-même peut être utilisé directement comme Provider :
<ThemeContext value={...}>.
Context est particulièrement utile pour le thème, la langue, l'utilisateur connecté ou certaines dépendances partagées.
import {
createContext,
useContext,
useReducer
} from 'react';
const CounterContext =
createContext(null);
function reducer(
state,
action
) {
switch (action.type) {
case 'increment':
return {
count:
state.count + 1
};
case 'reset':
return {
count: 0
};
default:
return state;
}
}
function CounterProvider({
children
}) {
const [
state,
dispatch
] = useReducer(
reducer,
{
count: 0
}
);
return (
<CounterContext
value={{
state,
dispatch
}}
>
{children}
</CounterContext>
);
}
function Counter() {
const {
state,
dispatch
} = useContext(
CounterContext
);
return (
<button
onClick={() =>
dispatch({
type: 'increment'
})
}
>
{state.count}
</button>
);
}
use.
Il permet d'extraire et de réutiliser une logique utilisant d'autres Hooks React.
Exemples :
useFetch,
useLocalStorage,
useWindowSize,
useAuth.
Un Custom Hook partage la logique, mais chaque composant qui l'utilise possède son propre état.
import {
useEffect,
useState
} from 'react';
function useFetch(url) {
const [
data,
setData
] = useState(null);
const [
loading,
setLoading
] = useState(true);
const [
error,
setError
] = useState(null);
useEffect(() => {
const controller =
new AbortController();
async function loadData() {
setLoading(true);
setError(null);
try {
const response =
await fetch(
url,
{
signal:
controller.signal
}
);
if (!response.ok) {
throw new Error(
'Erreur HTTP'
);
}
const result =
await response.json();
setData(result);
}
catch (error) {
if (
error.name !==
'AbortError'
) {
setError(error);
}
}
finally {
if (
!controller.signal.aborted
) {
setLoading(false);
}
}
}
loadData();
return () => {
controller.abort();
};
}, [url]);
return {
data,
loading,
error
};
}
function CryptoList() {
const {
data,
loading,
error
} = useFetch(
'/api/cryptos'
);
if (loading) {
return (
<p>
Chargement...
</p>
);
}
if (error) {
return (
<p>
Une erreur est survenue
</p>
);
}
return (
<ul>
{data.map(
crypto => (
<li
key={crypto.id}
>
{crypto.name}
</li>
)
)}
</ul>
);
}
use() : API React permettant notamment de lire une Promise compatible avec Suspense ou une valeur provenant d'un Context.
Si la Promise n'est pas encore terminée, React suspend le composant et affiche le
fallback du Suspense le plus proche.
Contrairement aux Hooks classiques,
use() possède des règles particulières
et peut notamment être utilisé dans certaines conditions.
import {
Suspense,
use
} from 'react';
function User({
userPromise
}) {
const user =
use(userPromise);
return (
<h3>
{user.name}
</h3>
);
}
function App({
userPromise
}) {
return (
<Suspense
fallback={
<p>
Chargement...
</p>
}
>
<User
userPromise={userPromise}
/>
</Suspense>
);
}
action d'un formulaire.
Ces fonctions asynchrones sont appelées Actions.
useActionState() : conserve l'état associé au résultat d'une Action et fournit également un état
isPending.
useFormStatus() : permet à un composant situé à l'intérieur d'un formulaire de connaître son état de soumission.
useOptimistic() : permet de modifier temporairement l'interface immédiatement, avant même que le serveur ait confirmé l'opération.
import {
useActionState
} from 'react';
async function saveName(
previousState,
formData
) {
const name =
formData.get('name');
if (!name) {
return {
error:
'Nom obligatoire'
};
}
await fetch(
'/api/user',
{
method: 'POST',
body:
JSON.stringify({
name
})
}
);
return {
success: true
};
}
function ProfileForm() {
const [
state,
formAction,
isPending
] = useActionState(
saveName,
null
);
return (
<form
action={formAction}
>
<input
name="name"
placeholder="Nom"
/>
<button
disabled={isPending}
>
{isPending
? 'Enregistrement...'
: 'Enregistrer'
}
</button>
{state?.error && (
<p>
{state.error}
</p>
)}
{state?.success && (
<p>
Enregistré !
</p>
)}
</form>
);
}
import {
useFormStatus
} from 'react-dom';
function SubmitButton() {
const {
pending
} = useFormStatus();
return (
<button
type="submit"
disabled={pending}
>
{pending
? 'Envoi...'
: 'Envoyer'
}
</button>
);
}
BrowserRouter : utilise l'historique du navigateur.
Routes : contient les différentes routes.
Route : associe une URL à un composant.
Link : permet de naviguer sans recharger complètement la page.
Les paramètres dynamiques utilisent une syntaxe comme
/crypto/:id.
import {
BrowserRouter,
Routes,
Route,
Link
} from 'react-router';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">
Accueil
</Link>
<Link to="/dashboard">
Dashboard
</Link>
</nav>
<Routes>
<Route
path="/"
element={
<Home />
}
/>
<Route
path="/dashboard"
element={
<Dashboard />
}
/>
<Route
path="/crypto/:id"
element={
<CryptoDetail />
}
/>
<Route
path="*"
element={
<NotFound />
}
/>
</Routes>
</BrowserRouter>
);
}
Il analyse le code au moment du build et peut effectuer automatiquement de nombreuses optimisations de mémorisation.
Cela réduit le besoin d'ajouter manuellement
useMemo(),
useCallback()
ou React.memo()
uniquement pour éviter des recalculs ou rendus inutiles.
Ces API existent toujours et restent utiles lorsque le développeur a besoin d'un contrôle précis.
Le plus important reste d'écrire des composants purs et de respecter les règles des Hooks.
// Code React classique
function ProductList({
products,
search
}) {
const filteredProducts =
products.filter(
product =>
product.name
.toLowerCase()
.includes(
search.toLowerCase()
)
);
return (
<ul>
{filteredProducts.map(
product => (
<li
key={product.id}
>
{product.name}
</li>
)
)}
</ul>
);
}
// Avec React Compiler activé,
// le compilateur peut automatiquement
// mémoriser certaines valeurs et
// certains calculs lorsque cela
// est bénéfique.
// useMemo() et useCallback()
// ne disparaissent pas :
// ils restent disponibles lorsque
// l'on souhaite contrôler précisément
// la mémorisation.
Vue.js — Fondamentaux
Frameworks JS
Vue.js est un framework JavaScript progressif permettant de créer des
interfaces utilisateur réactives et des applications web modernes.
Avec Vue 3, on utilise principalement la
Composition API et la syntaxe
<script setup>.
Écosystème :
Vue Router, Pinia, Vite et Nuxt.
.vue.
Il peut contenir trois parties :
<script>,
<template>
et <style>.
<script setup> : syntaxe recommandée avec la Composition API. Les variables, fonctions, refs, computed et composants importés sont directement utilisables dans le template.
Template : contient le HTML du composant. Vue peut y utiliser les variables JavaScript grâce aux doubles accolades
{{ }}.
Style scoped : avec
scoped, le CSS est limité au composant.
<!-- UserCard.vue -->
<script setup>
const props = defineProps({
name: {
type: String,
required: true
},
role: {
type: String,
default: 'Développeur'
},
avatar: {
type: String,
default: ''
}
});
</script>
<template>
<article class="card">
<img
v-if="props.avatar"
:src="props.avatar"
:alt="`Avatar de ${props.name}`"
/>
<h3>
{{ props.name }}
</h3>
<span class="badge">
{{ props.role }}
</span>
</article>
</template>
<style scoped>
.card {
padding: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
}
</style>
<!-- Utilisation depuis un composant parent -->
<UserCard
name="Julien"
role="Développeur Vue.js"
/>
.value.
Dans le template, Vue retire automatiquement
.value.
reactive() : transforme un objet en objet réactif. Ses propriétés peuvent être modifiées directement.
computed() : crée une valeur calculée à partir d'autres données réactives. Vue recalcule automatiquement cette valeur lorsque ses dépendances changent.
En pratique, utiliser
ref() pour la majorité des
états est une approche simple et très courante.
<script setup>
import {
ref,
reactive,
computed
} from 'vue';
const count = ref(0);
const name = ref('Julien');
const user = reactive({
name: 'Julien',
score: 0
});
const doubled = computed(() => {
return count.value * 2;
});
function increment() {
count.value++;
user.score += 10;
}
</script>
<template>
<p>
Compteur : {{ count }}
</p>
<p>
Double : {{ doubled }}
</p>
<p>
{{ user.name }} :
{{ user.score }} points
</p>
<button @click="increment">
Ajouter
</button>
</template>
v-show : conserve l'élément dans le DOM mais le masque avec CSS. Il est pratique pour les éléments souvent affichés ou masqués.
v-for : permet de parcourir un tableau. On ajoute généralement une
:key unique.
v-model : crée une liaison entre une valeur Vue et un champ de formulaire.
v-bind : lie dynamiquement un attribut HTML. Son raccourci est
:.
v-on : écoute un événement. Son raccourci est
@.
<script setup>
import {
ref,
computed
} from 'vue';
const search = ref('');
const isLoggedIn = ref(true);
const cryptos = ref([
{
id: 1,
name: 'Bitcoin',
price: 95000
},
{
id: 2,
name: 'Ethereum',
price: 3200
},
{
id: 3,
name: 'Solana',
price: 180
}
]);
const filteredCryptos = computed(() => {
return cryptos.value.filter((crypto) =>
crypto.name
.toLowerCase()
.includes(search.value.toLowerCase())
);
});
</script>
<template>
<input
v-model="search"
type="text"
placeholder="Rechercher une crypto"
/>
<div v-if="isLoggedIn">
<h3>
Dashboard
</h3>
</div>
<p v-else>
Connectez-vous
</p>
<ul>
<li
v-for="crypto in filteredCryptos"
:key="crypto.id"
>
{{ crypto.name }}
—
{{ crypto.price }} €
</li>
</ul>
<button
:class="{ active: isLoggedIn }"
@click="isLoggedIn = !isLoggedIn"
>
Changer l'état
</button>
</template>
Props : permettent au composant parent d'envoyer des données à un composant enfant.
Le flux est descendant :
Parent → Enfant
Emits : permettent à l'enfant d'envoyer un événement vers son parent.
Le flux devient :
Enfant → événement → Parent
C'est un principe essentiel pour créer des composants indépendants et réutilisables.
<!-- ExerciseCounter.vue -->
<script setup>
const props = defineProps({
count: {
type: Number,
default: 0
}
});
const emit = defineEmits([
'increment'
]);
function addSeries() {
emit('increment');
}
</script>
<template>
<div>
<p>
Séries : {{ props.count }}
</p>
<button @click="addSeries">
Ajouter une série
</button>
</div>
</template>
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue';
import ExerciseCounter
from './ExerciseCounter.vue';
const series = ref(3);
</script>
<template>
<ExerciseCounter
:count="series"
@increment="series++"
/>
</template>
Il est particulièrement utile pour : appels API, sauvegarde locale, synchronisation ou effets secondaires.
watchEffect() : détecte automatiquement les dépendances réactives utilisées dans sa fonction et s'exécute immédiatement.
À l'inverse de
computed(), un watcher sert
principalement à produire un
effet secondaire,
et non simplement à calculer une valeur.
<script setup>
import {
ref,
watch,
watchEffect
} from 'vue';
const symbol = ref('BTC');
const price = ref(null);
watch(symbol, async (newSymbol, oldSymbol) => {
console.log(
`${oldSymbol} → ${newSymbol}`
);
const response = await fetch(
`/api/price/${newSymbol}`
);
price.value =
await response.json();
});
watchEffect(() => {
console.log(
'Symbole actuel :',
symbol.value
);
});
</script>
onMounted() : exécuté lorsque le composant est affiché dans le DOM.
Exemple : démarrer un timer, charger des données ou initialiser une bibliothèque externe.
onUpdated() : exécuté après une mise à jour du DOM du composant.
onUnmounted() : exécuté lorsque le composant est retiré. Il sert notamment à nettoyer les timers ou événements.
<script setup>
import {
ref,
onMounted,
onUpdated,
onUnmounted
} from 'vue';
const seconds = ref(0);
let intervalId = null;
onMounted(() => {
console.log(
'Composant monté'
);
intervalId = setInterval(() => {
seconds.value++;
}, 1000);
});
onUpdated(() => {
console.log(
'DOM mis à jour'
);
});
onUnmounted(() => {
clearInterval(intervalId);
console.log(
'Timer supprimé'
);
});
</script>
<template>
<p>
Temps :
{{ seconds }} secondes
</p>
</template>
Il permet de créer plusieurs pages ou vues dans une application sans recharger complètement le navigateur.
<RouterLink> permet de créer les liens de navigation.
<RouterView> indique l'endroit où Vue doit afficher le composant correspondant à la route.
Les routes peuvent également contenir des paramètres, par exemple :
/crypto/:id.
// router/index.js
import {
createRouter,
createWebHistory
} from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () =>
import('../views/HomeView.vue')
},
{
path: '/crypto/:id',
name: 'crypto',
component: () =>
import('../views/CryptoView.vue')
},
{
path: '/:pathMatch(.*)*',
component: () =>
import('../views/NotFoundView.vue')
}
]
});
export default router;
// Dans un composant
import {
useRoute,
useRouter
} from 'vue-router';
const route = useRoute();
const router = useRouter();
console.log(
route.params.id
);
router.push('/');
Un store permet de partager des données entre plusieurs composants sans devoir transmettre continuellement des props à travers toute l'application.
Un store peut contenir :
• state → les données
• getters → les valeurs calculées
• actions → les fonctions qui travaillent sur l'état
Pinia devient surtout utile lorsque plusieurs composants éloignés ont besoin des mêmes données.
// stores/crypto.js
import {
defineStore
} from 'pinia';
export const useCryptoStore =
defineStore('crypto', {
state: () => ({
coins: [],
loading: false
}),
getters: {
topCoins: (state) => {
return state.coins.slice(0, 10);
}
},
actions: {
async fetchCoins() {
this.loading = true;
try {
const response =
await fetch('/api/coins');
this.coins =
await response.json();
}
finally {
this.loading = false;
}
}
}
});
// Dans un composant Vue
<script setup>
import {
onMounted
} from 'vue';
import {
useCryptoStore
} from '@/stores/crypto';
const store =
useCryptoStore();
onMounted(() => {
store.fetchCoins();
});
</script>
<template>
<p v-if="store.loading">
Chargement...
</p>
<ul v-else>
<li
v-for="coin in store.topCoins"
:key="coin.id"
>
{{ coin.name }}
</li>
</ul>
</template>
Angular — Fondamentaux
Frameworks JSAngular est un framework web complet basé sur TypeScript. Il propose une architecture orientée composants, un système de réactivité avec les Signals, l'injection de dépendances, un routeur, des formulaires, un client HTTP et une intégration avec RxJS. Écosystème : Angular CLI, Signals, RxJS, Angular Router et Angular Material.
@Component : décorateur qui indique à Angular que la classe représente un composant.
selector : définit la balise HTML permettant d'utiliser le composant.
Les composants Angular modernes sont standalone par défaut. Il n'est donc généralement plus nécessaire de créer des NgModules pour organiser une application moderne.
// user-card.component.ts
import {
Component
} from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<article class="card">
<h3>
{{ name }}
</h3>
<p>
{{ role }}
</p>
<button (click)="sayHello()">
Dire bonjour
</button>
</article>
`,
styles: [`
.card {
padding: 16px;
border-radius: 12px;
}
`]
})
export class UserCardComponent {
name = 'Julien';
role = 'Développeur';
sayHello() {
console.log(
`Bonjour ${this.name}`
);
}
}
input() : permet au composant parent d'envoyer une donnée vers un composant enfant.
output() : permet au composant enfant d'émettre un événement vers son parent.
model() : permet une communication bidirectionnelle. Le parent transmet une valeur et l'enfant peut la modifier.
Le principe est comparable aux props et emits de Vue.
// counter.component.ts
import {
Component,
input,
output,
model
} from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<h3>
{{ title() }}
</h3>
<p>
Valeur : {{ count() }}
</p>
<button (click)="increment()">
+1
</button>
`
})
export class CounterComponent {
// Parent → enfant
title = input('Compteur');
// Valeur modifiable dans les deux sens
count = model(0);
// Enfant → parent
changed = output<number>();
increment() {
this.count.update(
value => value + 1
);
this.changed.emit(
this.count()
);
}
}
<!-- Utilisation dans le parent -->
<app-counter
title="Séries"
[(count)]="series"
(changed)="onSeriesChanged($event)"
/>
count().
.set() : remplace directement la valeur.
.update() : calcule une nouvelle valeur à partir de l'ancienne.
computed() : crée une valeur calculée automatiquement à partir d'autres Signals.
effect() : exécute un effet secondaire lorsqu'une dépendance réactive utilisée dans l'effet change.
import {
Component,
signal,
computed,
effect
} from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<p>
Compteur : {{ count() }}
</p>
<p>
Double : {{ doubled() }}
</p>
<button (click)="increment()">
+1
</button>
<button (click)="reset()">
Reset
</button>
`
})
export class CounterComponent {
count = signal(0);
doubled = computed(() =>
this.count() * 2
);
constructor() {
effect(() => {
console.log(
'Compteur :',
this.count()
);
});
}
increment() {
this.count.update(
value => value + 1
);
}
reset() {
this.count.set(0);
}
}
{{ expression }} : interpolation d'une valeur dans le HTML.
[property] : lie une propriété HTML à une valeur TypeScript.
(event) : écoute un événement comme un clic.
@if : permet un affichage conditionnel.
@for : permet de parcourir une liste.
track permet à Angular d'identifier
chaque élément efficacement.
Pipes : transforment une valeur directement dans le template, par exemple
currency, date
ou uppercase.
import {
Component,
signal
} from '@angular/core';
import {
CurrencyPipe
} from '@angular/common';
@Component({
selector: 'app-dashboard',
imports: [
CurrencyPipe
],
template: `
@if (isLoggedIn()) {
<h3>
Bienvenue {{ userName() }}
</h3>
}
@else {
<p>
Connectez-vous
</p>
}
@for (
crypto of cryptos();
track crypto.id
) {
<div class="crypto-row">
<span>
{{ crypto.name }}
</span>
<span>
{{ crypto.price | currency:'EUR' }}
</span>
<button
[disabled]="crypto.price === 0"
(click)="selectCrypto(crypto)"
>
Détails
</button>
</div>
}
@empty {
<p>
Aucune crypto
</p>
}
`
})
export class DashboardComponent {
isLoggedIn =
signal(true);
userName =
signal('Julien');
cryptos = signal([
{
id: 1,
name: 'Bitcoin',
price: 95000
},
{
id: 2,
name: 'Ethereum',
price: 3200
}
]);
selectCrypto(crypto: any) {
console.log(crypto);
}
}
ngOnInit() : exécuté après l'initialisation du composant. Il est souvent utilisé pour charger des données.
ngOnChanges() : exécuté lorsque certaines valeurs d'entrée changent.
ngOnDestroy() : exécuté juste avant la destruction du composant. Il permet notamment de nettoyer des timers.
DestroyRef : API moderne permettant d'enregistrer directement une fonction de nettoyage.
import {
Component,
OnInit,
inject,
DestroyRef
} from '@angular/core';
@Component({
selector: 'app-timer',
template: `
<p>
Temps :
{{ seconds }}
secondes
</p>
`
})
export class TimerComponent
implements OnInit {
seconds = 0;
private intervalId:
ReturnType<typeof setInterval> | null = null;
private destroyRef =
inject(DestroyRef);
ngOnInit() {
this.intervalId =
setInterval(() => {
this.seconds++;
}, 1000);
this.destroyRef.onDestroy(() => {
if (this.intervalId) {
clearInterval(
this.intervalId
);
}
});
}
}
Un service peut contenir : appels API, logique métier, stockage ou état partagé.
@Injectable : indique qu'une classe peut participer au système d'injection de dépendances Angular.
providedIn: 'root' : crée généralement une seule instance du service pour toute l'application.
inject() : permet de récupérer simplement une dépendance.
// counter.service.ts
import {
Injectable,
signal
} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CounterService {
count = signal(0);
increment() {
this.count.update(
value => value + 1
);
}
}
// counter.component.ts
import {
Component,
inject
} from '@angular/core';
import {
CounterService
} from './counter.service';
@Component({
selector: 'app-counter',
template: `
<p>
{{ counter.count() }}
</p>
<button
(click)="counter.increment()"
>
+1
</button>
`
})
export class CounterComponent {
counter =
inject(CounterService);
}
Template-driven Forms : approche simple utilisant principalement le template et
ngModel.
Reactive Forms : approche basée sur
FormControl et
FormGroup.
Elle reste très utilisée dans les applications existantes.
Signal Forms : approche moderne construite autour des Signals. Le modèle du formulaire est stocké dans un signal et Angular synchronise automatiquement les champs.
Signal Forms permet également de gérer validation, erreurs, état touched, dirty et disabled.
import {
Component,
signal
} from '@angular/core';
import {
form,
FormField,
required,
email,
submit
} from '@angular/forms/signals';
@Component({
selector: 'app-login',
imports: [
FormField
],
template: `
<form
(submit)="onSubmit($event)"
>
<label>
Email
<input
type="email"
[formField]="loginForm.email"
/>
</label>
<label>
Mot de passe
<input
type="password"
[formField]="loginForm.password"
/>
</label>
<button
type="submit"
[disabled]="loginForm().invalid()"
>
Connexion
</button>
</form>
`
})
export class LoginComponent {
loginModel = signal({
email: '',
password: ''
});
loginForm =
form(
this.loginModel,
path => {
required(
path.email
);
email(
path.email
);
required(
path.password
);
}
);
onSubmit(event: Event) {
event.preventDefault();
submit(
this.loginForm,
async () => {
console.log(
this.loginModel()
);
}
);
}
}
Observable : flux de données utilisé intensivement par Angular et RxJS. Une requête
HttpClient retourne généralement
un Observable.
subscribe() : permet de recevoir les valeurs émises par l'Observable.
RxJS : fournit de nombreux opérateurs comme
map,
filter,
switchMap
ou debounceTime.
httpResource() : API moderne adaptée aux requêtes GET réactives. Elle expose directement les données, l'état de chargement et les erreurs sous forme réactive.
import {
Component,
signal
} from '@angular/core';
import {
httpResource
} from '@angular/common/http';
interface User {
id: number;
name: string;
}
@Component({
selector: 'app-user',
template: `
@if (user.isLoading()) {
<p>
Chargement...
</p>
}
@else if (user.error()) {
<p>
Erreur de chargement
</p>
}
@else if (user.hasValue()) {
<h3>
{{ user.value().name }}
</h3>
}
`
})
export class UserComponent {
userId =
signal(1);
user =
httpResource<User>(
() =>
`/api/users/${this.userId()}`
);
}
// Pour POST, PUT ou DELETE,
// on utilise généralement HttpClient.
import {
HttpClient
} from '@angular/common/http';
import {
inject
} from '@angular/core';
private http =
inject(HttpClient);
saveUser(user: User) {
this.http
.post('/api/users', user)
.subscribe({
next: data => {
console.log(data);
},
error: error => {
console.error(error);
}
});
}
toSignal() : transforme un Observable en Signal.
Cela permet d'utiliser une donnée RxJS dans le template avec la syntaxe habituelle des Signals.
takeUntilDestroyed() : arrête automatiquement une souscription RxJS lorsque le composant est détruit.
Cela évite de nombreuses fuites mémoire liées aux abonnements manuels.
import {
Component
} from '@angular/core';
import {
interval
} from 'rxjs';
import {
toSignal
} from '@angular/core/rxjs-interop';
@Component({
selector: 'app-clock',
template: `
<p>
Compteur :
{{ counter() }}
</p>
`
})
export class ClockComponent {
counter$ =
interval(1000);
counter =
toSignal(
this.counter$,
{
initialValue: 0
}
);
}
routerLink : crée un lien interne sans recharger complètement la page.
router-outlet : représente l'emplacement où Angular affiche le composant de la route active.
Lazy loading : permet de charger un composant seulement lorsqu'il est nécessaire.
Guards : contrôlent l'accès à une route. Ils sont souvent utilisés pour l'authentification.
Les routes dynamiques comme
/user/:id
permettent de transmettre un paramètre dans l'URL.
// app.routes.ts
import {
Routes
} from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import(
'./home/home.component'
)
.then(
module =>
module.HomeComponent
)
},
{
path: 'dashboard',
loadComponent: () =>
import(
'./dashboard/dashboard.component'
)
.then(
module =>
module.DashboardComponent
),
canActivate: [
authGuard
]
},
{
path: 'user/:id',
loadComponent: () =>
import(
'./user/user.component'
)
.then(
module =>
module.UserComponent
)
},
{
path: '**',
loadComponent: () =>
import(
'./not-found/not-found.component'
)
.then(
module =>
module.NotFoundComponent
)
}
];
// auth.guard.ts
import {
inject
} from '@angular/core';
import {
Router
} from '@angular/router';
export const authGuard = () => {
const auth =
inject(AuthService);
const router =
inject(Router);
if (
auth.isAuthenticated()
) {
return true;
}
return router.parseUrl(
'/login'
);
};
<!-- Navigation Angular -->
<a
routerLink="/dashboard"
routerLinkActive="active"
>
Dashboard
</a>
<router-outlet />