Comprendre les fondamentaux du JavaScript pour rendre vos pages web interactives : variables, fonctions, DOM, événements, fetch API et bonnes pratiques modernes.
1. Introduction — Pourquoi JavaScript ?
Le web repose sur une triade incontournable : HTML structure le contenu, CSS le met en forme, et JavaScript lui donne vie. Sans JavaScript, vos pages seraient statiques : aucune validation de formulaire en temps réel, aucune animation interactive, aucune communication avec un serveur sans rechargement de page.
JavaScript est né en 1995 dans le navigateur Netscape. Il est aujourd'hui le seul langage nativement compris par tous les navigateurs. Grâce à Node.js, il s'est également imposé côté serveur, permettant aux développeurs de travailler avec un seul langage en front et en back.
JS côté navigateur vs côté serveur
- Navigateur (client-side) : manipulation du DOM, gestion des événements utilisateur, appels API, animations, stockage local (localStorage, sessionStorage).
- Serveur (Node.js) : création d'API REST, accès aux bases de données, gestion de fichiers, traitement en arrière-plan.
Ce que vous allez apprendre
À la fin de cet article, vous saurez déclarer des variables, écrire des fonctions, manipuler le DOM,
écouter des événements, interroger une API externe avec fetch(), et appliquer les
fonctionnalités modernes d'ES6+. Chaque concept est illustré par des exemples commentés et des
comparaisons bonne/mauvaise pratique.
2. Les bases : variables et types
var, let, const — quelles différences ?
Historiquement, on utilisait var pour déclarer des variables. Depuis ES6 (2015),
let et const sont préférés car ils respectent la portée de bloc
(block scope), contrairement à var qui est limité à la portée de la fonction.
// ❌ Mauvaise pratique — var est function-scoped et peut être re-déclaré
var compteur = 0;
var compteur = 5; // aucune erreur... très risqué
// ✅ Bonne pratique — const par défaut, let si la valeur change
const PI = 3.14159; // valeur constante, jamais réassignée
let score = 0; // valeur qui va évoluer
score = score + 10; // OK
// ⚠️ Différence de portée
if (true) {
var x = "var"; // accessible en dehors du bloc !
let y = "let"; // uniquement dans ce bloc
const z = "const"; // uniquement dans ce bloc
}
console.log(x); // "var" — fonctionne (et c'est un problème)
// console.log(y); // ReferenceError — y n'existe pas ici
const par défaut. Passez à let
uniquement quand vous savez que la valeur changera. N'utilisez plus var.
Les types primitifs
JavaScript possède 7 types primitifs :
// string — chaîne de caractères
const prenom = "Alice";
const message = 'Bonjour le monde';
// number — entiers et flottants (un seul type)
const age = 30;
const prix = 19.99;
const negatif = -5;
// boolean — vrai ou faux
const estActif = true;
const estAdmin = false;
// null — absence intentionnelle de valeur
const utilisateur = null;
// undefined — variable déclarée mais non initialisée
let resultat;
console.log(resultat); // undefined
// symbol — identifiant unique (avancé)
const id = Symbol("id");
// bigint — entiers très grands (ES2020)
const grandNombre = 9007199254740991n;
typeof — vérifier le type
console.log(typeof "Bonjour"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" ⚠️ bug historique de JS !
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function(){}); // "function"
typeof null === "object" est un bug connu de JavaScript
qui n'a jamais été corrigé pour des raisons de rétrocompatibilité. Pour tester si une valeur
est null, utilisez valeur === null.
Template literals — les gabarits de chaînes
Les template literals (introduits en ES6) permettent d'insérer des expressions directement
dans une chaîne avec la syntaxe ${expression}, entre backticks `.
const nom = "Alice";
const age = 28;
// ❌ Avant ES6 — concaténation fastidieuse
const msg1 = "Bonjour, je m'appelle " + nom + " et j'ai " + age + " ans.";
// ✅ Avec les template literals
const msg2 = `Bonjour, je m'appelle ${nom} et j'ai ${age} ans.`;
// On peut y mettre des expressions complètes
const prix = 49.9;
const tva = 0.2;
console.log(`Prix TTC : ${(prix * (1 + tva)).toFixed(2)} €`); // "Prix TTC : 59.88 €"
// Multilignes natifs
const html = `
<div class="card">
<h2>${nom}</h2>
<p>Age : ${age} ans</p>
</div>
`;
3. Les opérateurs
Opérateurs arithmétiques
const a = 10;
const b = 3;
console.log(a + b); // 13 — addition
console.log(a - b); // 7 — soustraction
console.log(a * b); // 30 — multiplication
console.log(a / b); // 3.333... — division
console.log(a % b); // 1 — modulo (reste de la division)
console.log(a ** b); // 1000 — puissance (ES2016)
// Raccourcis d'assignation
let n = 5;
n += 3; // n = n + 3 => 8
n -= 2; // n = n - 2 => 6
n *= 4; // n = n * 4 => 24
n /= 6; // n = n / 6 => 4
// Incrément / décrément
n++; // n = n + 1
n--; // n = n - 1
Comparaison : == vs ===
C'est l'une des plus grandes sources de bugs chez les débutants. L'opérateur ==
effectue une comparaison avec coercition de type (il convertit les valeurs avant
de comparer). L'opérateur === compare valeur ET type, sans conversion.
// ❌ == avec coercition de type — résultats surprenants
console.log(0 == false); // true ⚠️
console.log("" == false); // true ⚠️
console.log(0 == ""); // true ⚠️
console.log(null == undefined); // true ⚠️
console.log("5" == 5); // true ⚠️ (string convertie en number)
// ✅ === sans coercition — strict et prévisible
console.log(0 === false); // false ✅
console.log("" === false); // false ✅
console.log("5" === 5); // false ✅
console.log(null === undefined); // false ✅
// Inégalités
console.log(5 !== "5"); // true — !== est l'équivalent strict de !=
console.log(10 > 5); // true
console.log(10 >= 10); // true
console.log(3 < 7); // true
console.log(3 <= 2); // false
=== et !==.
Bannissez == et != de votre code.
Opérateurs logiques
const estConnecte = true;
const estAdmin = false;
// && — ET logique : vrai si les DEUX sont vrais
console.log(estConnecte && estAdmin); // false
// || — OU logique : vrai si AU MOINS UN est vrai
console.log(estConnecte || estAdmin); // true
// ! — NON logique : inverse la valeur
console.log(!estConnecte); // false
// Valeurs "falsy" en JavaScript (évaluées à false dans un contexte booléen)
// false, 0, "", null, undefined, NaN
// Valeurs "truthy" — tout le reste
// true, 1, "texte", [], {}, function(){}
Opérateur ternaire
// Syntaxe : condition ? valeurSiVrai : valeurSiFaux
const age = 20;
// ❌ Avec if/else — verbeux pour des cas simples
let statut;
if (age >= 18) {
statut = "majeur";
} else {
statut = "mineur";
}
// ✅ Avec l'opérateur ternaire
const statut2 = age >= 18 ? "majeur" : "mineur";
console.log(statut2); // "majeur"
// Exemple dans un template literal
const msg = `Vous êtes ${age >= 18 ? "majeur" : "mineur"}.`;
Nullish coalescing (??) — ES2020
// ?? retourne la valeur de droite uniquement si la valeur de gauche est null ou undefined
// Contrairement à ||, il ne traite pas 0 ou "" comme falsy
const prenom = null;
const affichage = prenom ?? "Anonyme";
console.log(affichage); // "Anonyme"
// Différence importante avec ||
const score = 0;
console.log(score || 10); // 10 ⚠️ — 0 est falsy pour ||
console.log(score ?? 10); // 0 ✅ — 0 n'est pas null/undefined
const config = {
timeout: 0,
retries: null
};
console.log(config.timeout ?? 5000); // 0 — 0 est une valeur valide
console.log(config.retries ?? 3); // 3 — null remplacé par défaut
4. Les structures de contrôle
if / else if / else
const note = 75;
if (note >= 90) {
console.log("Excellent !");
} else if (note >= 70) {
console.log("Bien");
} else if (note >= 50) {
console.log("Passable");
} else {
console.log("Insuffisant");
}
// Affiche : "Bien"
switch — pour des cas multiples
const jour = "lundi";
switch (jour) {
case "lundi":
case "mardi":
case "mercredi":
case "jeudi":
case "vendredi":
console.log("Jour de semaine");
break; // IMPORTANT : sans break, l'exécution continue au cas suivant
case "samedi":
case "dimanche":
console.log("Week-end !");
break;
default:
console.log("Jour inconnu");
}
break dans chaque case
du switch, sinon JavaScript continue l'exécution des cases suivants
(comportement dit de "fall-through").
Les boucles
// for classique — quand on connaît le nombre d'itérations
for (let i = 0; i < 5; i++) {
console.log(`Itération ${i}`); // 0, 1, 2, 3, 4
}
// for...of — itère sur les valeurs d'un itérable (tableau, string)
const fruits = ["pomme", "banane", "cerise"];
for (const fruit of fruits) {
console.log(fruit); // "pomme", "banane", "cerise"
}
// for...in — itère sur les clés d'un objet
const personne = { nom: "Alice", age: 30, ville: "Paris" };
for (const cle in personne) {
console.log(`${cle}: ${personne[cle]}`);
// "nom: Alice", "age: 30", "ville: Paris"
}
// while — tant qu'une condition est vraie
let compteur = 0;
while (compteur < 3) {
console.log(`Compteur : ${compteur}`);
compteur++;
}
// do...while — s'exécute au moins une fois
let tentatives = 0;
do {
console.log(`Tentative ${tentatives + 1}`);
tentatives++;
} while (tentatives < 3);
// forEach — méthode de tableau (ne supporte pas break/continue)
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
break et continue
// break — sort de la boucle immédiatement
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue — passe à l'itération suivante
for (let i = 0; i < 6; i++) {
if (i % 2 === 0) continue; // ignore les pairs
console.log(i); // 1, 3, 5
}
5. Les fonctions
Les fonctions sont les briques fondamentales de JavaScript. Elles permettent d'encapsuler une logique réutilisable, de nommer des comportements et de structurer votre code.
3 façons de déclarer une fonction
// 1. Function declaration — hoistée (disponible avant sa déclaration dans le code)
function saluer(nom) {
return `Bonjour, ${nom} !`;
}
console.log(saluer("Alice")); // "Bonjour, Alice !"
// 2. Function expression — non hoistée
const multiplier = function(a, b) {
return a * b;
};
console.log(multiplier(4, 5)); // 20
// 3. Arrow function (ES6) — syntaxe concise, pas de 'this' propre
const diviser = (a, b) => a / b; // retour implicite pour une seule expression
console.log(diviser(10, 2)); // 5
// Arrow function avec corps de bloc
const calculerTTC = (ht, taux) => {
const ttc = ht * (1 + taux);
return ttc.toFixed(2);
};
console.log(calculerTTC(100, 0.2)); // "120.00"
Paramètres par défaut — ES6
// ❌ Avant ES6 — vérification manuelle
function cree_utilisateur(nom, role) {
role = role || "visiteur"; // problème si role = 0 ou ""
return { nom, role };
}
// ✅ ES6 — paramètre par défaut
function creerUtilisateur(nom, role = "visiteur") {
return { nom, role };
}
console.log(creerUtilisateur("Alice")); // { nom: "Alice", role: "visiteur" }
console.log(creerUtilisateur("Bob", "admin")); // { nom: "Bob", role: "admin" }
Rest et Spread
// Rest (...args) — capture plusieurs arguments dans un tableau
function somme(...nombres) {
return nombres.reduce((total, n) => total + n, 0);
}
console.log(somme(1, 2, 3, 4, 5)); // 15
console.log(somme(10, 20)); // 30
// Spread (...) — déploie un tableau ou objet
const a = [1, 2, 3];
const b = [4, 5, 6];
const fusion = [...a, ...b];
console.log(fusion); // [1, 2, 3, 4, 5, 6]
// Spread avec objet
const base = { couleur: "bleu", taille: "M" };
const etendu = { ...base, taille: "L", prix: 29.99 }; // taille écrase la précédente
console.log(etendu); // { couleur: "bleu", taille: "L", prix: 29.99 }
Callbacks
Un callback est une fonction passée en argument d'une autre fonction, pour être appelée plus tard (après une opération, un événement, etc.).
// Exemple simple de callback
function traiter(valeur, callback) {
const resultat = valeur * 2;
callback(resultat);
}
traiter(5, function(res) {
console.log(`Résultat : ${res}`); // "Résultat : 10"
});
// Avec une arrow function
traiter(8, (res) => console.log(`Résultat : ${res}`)); // "Résultat : 16"
// Callbacks dans les méthodes natives
const nombres = [3, 1, 4, 1, 5, 9];
nombres.sort((a, b) => a - b); // callback de comparaison
console.log(nombres); // [1, 1, 3, 4, 5, 9]
Portée (scope) et closures
// Portée globale vs locale
const global = "je suis global";
function exemple() {
const local = "je suis local";
console.log(global); // accessible
console.log(local); // accessible
}
// console.log(local); // ❌ ReferenceError
// Closure — une fonction qui "mémorise" son environnement lexical
function creerCompteur() {
let count = 0; // variable capturée par la closure
return {
incrementer: () => ++count,
decrementer: () => --count,
valeur: () => count
};
}
const compteur = creerCompteur();
compteur.incrementer();
compteur.incrementer();
compteur.incrementer();
compteur.decrementer();
console.log(compteur.valeur()); // 2
// La variable 'count' est privée — inaccessible de l'extérieur
6. Les tableaux et objets
Les tableaux (Array)
// Déclaration
const fruits = ["pomme", "banane", "cerise"];
// Accès par index (commence à 0)
console.log(fruits[0]); // "pomme"
console.log(fruits[fruits.length - 1]); // "cerise"
// Ajout / suppression
fruits.push("fraise"); // ajoute à la fin → ["pomme","banane","cerise","fraise"]
fruits.pop(); // retire le dernier → ["pomme","banane","cerise"]
fruits.unshift("kiwi"); // ajoute au début → ["kiwi","pomme","banane","cerise"]
fruits.shift(); // retire le premier → ["pomme","banane","cerise"]
// splice — modifie le tableau en place
fruits.splice(1, 1); // supprime 1 élément à l'index 1 → ["pomme","cerise"]
fruits.splice(1, 0, "mangue"); // insère "mangue" à l'index 1
// Méthodes fonctionnelles (retournent un NOUVEAU tableau, n'altèrent pas l'original)
const nombres = [1, 2, 3, 4, 5, 6];
// map — transforme chaque élément
const doubles = nombres.map(n => n * 2);
console.log(doubles); // [2, 4, 6, 8, 10, 12]
// filter — garde uniquement les éléments qui passent le test
const pairs = nombres.filter(n => n % 2 === 0);
console.log(pairs); // [2, 4, 6]
// reduce — réduit à une valeur unique
const somme = nombres.reduce((acc, n) => acc + n, 0);
console.log(somme); // 21
// find — retourne le premier élément qui satisfait le test
const premier_pair = nombres.find(n => n % 2 === 0);
console.log(premier_pair); // 2
// findIndex — retourne l'index du premier élément trouvé
const idx = nombres.findIndex(n => n > 4);
console.log(idx); // 4 (l'élément 5 est à l'index 4)
// some — true si au moins un élément passe le test
console.log(nombres.some(n => n > 5)); // true
// every — true si TOUS les éléments passent le test
console.log(nombres.every(n => n > 0)); // true
console.log(nombres.every(n => n > 3)); // false
// flat — aplatit un tableau imbriqué
const imbrique = [1, [2, 3], [4, [5, 6]]];
console.log(imbrique.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(imbrique.flat(2)); // [1, 2, 3, 4, 5, 6]
console.log(imbrique.flat(Infinity)); // [1, 2, 3, 4, 5, 6]
// includes — vérifie la présence d'une valeur
console.log(fruits.includes("pomme")); // true
// Chaînage de méthodes
const resultat = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.filter(n => n % 2 === 0) // [2, 4, 6, 8, 10]
.map(n => n ** 2) // [4, 16, 36, 64, 100]
.reduce((acc, n) => acc + n, 0); // 220
console.log(resultat); // 220
Les objets
// Déclaration d'un objet littéral
const utilisateur = {
nom: "Alice",
age: 28,
email: "alice@exemple.com",
adresse: {
ville: "Paris",
codePostal: "75001"
},
saluer: function() {
return `Bonjour, je m'appelle ${this.nom}`;
}
};
// Accès aux propriétés
console.log(utilisateur.nom); // "Alice" — notation pointée
console.log(utilisateur["age"]); // 28 — notation crochet (utile pour les clés dynamiques)
console.log(utilisateur.adresse.ville); // "Paris"
console.log(utilisateur.saluer()); // "Bonjour, je m'appelle Alice"
// Modification
utilisateur.age = 29;
utilisateur.telephone = "06 00 00 00 00"; // ajout d'une propriété
// Suppression
delete utilisateur.telephone;
// Destructuring — extraire des propriétés dans des variables
const { nom, age, email } = utilisateur;
console.log(nom); // "Alice"
console.log(age); // 29
// Destructuring avec renommage
const { nom: prenom, age: annees } = utilisateur;
console.log(prenom); // "Alice"
console.log(annees); // 29
// Destructuring avec valeur par défaut
const { role = "visiteur" } = utilisateur;
console.log(role); // "visiteur" — la propriété n'existe pas
// Spread sur un objet — copie superficielle
const copie = { ...utilisateur };
const miseAJour = { ...utilisateur, age: 30, role: "admin" };
// Object.keys / values / entries
console.log(Object.keys(utilisateur)); // ["nom", "age", "email", "adresse", "saluer"]
console.log(Object.values(utilisateur)); // ["Alice", 29, "alice@exemple.com", {...}, f]
console.log(Object.entries(utilisateur)); // [["nom","Alice"], ["age",29], ...]
// Itérer sur les entrées
Object.entries(utilisateur).forEach(([cle, valeur]) => {
console.log(`${cle} => ${valeur}`);
});
map(), filter() et reduce()
sont les trois méthodes fonctionnelles les plus importantes. Elles ne modifient jamais le tableau
original et peuvent être chaînées pour des transformations élégantes.
7. Le DOM — Manipuler la page
Le DOM (Document Object Model) est la représentation en mémoire de votre page HTML sous forme d'arbre d'objets. JavaScript peut lire et modifier cet arbre en temps réel, ce qui permet d'ajouter/supprimer des éléments, de changer des styles ou du texte sans rechargement de page.
Sélectionner des éléments
// querySelector — retourne le PREMIER élément correspondant au sélecteur CSS
const titre = document.querySelector("h1");
const bouton = document.querySelector("#mon-bouton");
const carte = document.querySelector(".card");
const input = document.querySelector('input[type="email"]');
// querySelectorAll — retourne une NodeList de TOUS les éléments correspondants
const tousLesParagraphes = document.querySelectorAll("p");
const tousLesBoutons = document.querySelectorAll(".btn");
// Itérer sur une NodeList
tousLesParagraphes.forEach(p => {
p.style.color = "blue";
});
// getElementById — accès direct par ID (plus rapide pour un ID unique)
const formulaire = document.getElementById("mon-form");
// getElementsByClassName — retourne une HTMLCollection (dynamique)
const cartes = document.getElementsByClassName("card");
innerHTML vs textContent
const div = document.querySelector(".message");
// innerHTML — parse le HTML, risque XSS si données utilisateur
div.innerHTML = "<strong>Bonjour !</strong>"; // affiche "Bonjour!" en gras
// textContent — texte brut, PAS de rendu HTML, sécurisé
div.textContent = "<strong>Bonjour !</strong>"; // affiche la chaîne telle quelle
// ✅ Règle de sécurité : utilisez textContent pour les données utilisateur
// Utilisez innerHTML uniquement pour du HTML que VOUS contrôlez
const nomUtilisateur = "<script>alert('xss')</script>"; // ⚠️ injection XSS
div.innerHTML = nomUtilisateur; // ❌ DANGEREUX
div.textContent = nomUtilisateur; // ✅ sûr, affiche le texte brut
Manipuler les classes CSS
const element = document.querySelector(".ma-carte");
// classList.add — ajoute une classe
element.classList.add("active");
element.classList.add("visible", "highlight"); // plusieurs à la fois
// classList.remove — retire une classe
element.classList.remove("hidden");
// classList.toggle — ajoute si absente, retire si présente
element.classList.toggle("open"); // parfait pour les menus
// classList.contains — vérifie la présence
if (element.classList.contains("active")) {
console.log("L'élément est actif");
}
// classList.replace — remplace une classe par une autre
element.classList.replace("btn-primary", "btn-secondary");
Modifier les styles inline
const boite = document.querySelector(".boite");
// Modification directe des styles (camelCase pour les propriétés CSS)
boite.style.backgroundColor = "#3498db";
boite.style.color = "white";
boite.style.padding = "16px";
boite.style.borderRadius = "8px";
boite.style.display = "none"; // masquer un élément
// ✅ Préférer les classes CSS plutôt que les styles inline
// Dans votre CSS : .visible { display: block; }
boite.classList.add("visible"); // bien mieux !
Créer, insérer et supprimer des éléments
// Créer un nouvel élément
const nouvelleCarte = document.createElement("div");
nouvelleCarte.classList.add("card");
nouvelleCarte.innerHTML = `
<h3>Nouveau produit</h3>
<p>Description du produit.</p>
`;
// Insérer dans le DOM
const conteneur = document.querySelector(".container");
conteneur.appendChild(nouvelleCarte); // à la fin
conteneur.prepend(nouvelleCarte); // au début
conteneur.insertAdjacentElement("afterend", nouvelleCarte); // après le conteneur
// Méthode moderne : insertAdjacentHTML
conteneur.insertAdjacentHTML("beforeend", `
<div class="card"><h3>Carte dynamique</h3></div>
`);
// Supprimer un élément
const aSupprimer = document.querySelector(".obsolete");
if (aSupprimer) {
aSupprimer.remove(); // supprime l'élément lui-même
}
// Vider un conteneur (supprimer tous ses enfants)
const liste = document.querySelector("#ma-liste");
liste.innerHTML = ""; // méthode simple
// Méthode performante (évite reflow)
while (liste.firstChild) {
liste.removeChild(liste.firstChild);
}
Lire et modifier les attributs
const lien = document.querySelector("a.mon-lien");
// getAttribute / setAttribute
console.log(lien.getAttribute("href"));
lien.setAttribute("href", "https://angularforall.com");
lien.setAttribute("target", "_blank");
// removeAttribute
lien.removeAttribute("disabled");
// dataset — accès aux attributs data-*
// HTML : <div data-user-id="42" data-role="admin"></div>
const el = document.querySelector("[data-user-id]");
console.log(el.dataset.userId); // "42" (camelCase automatique)
console.log(el.dataset.role); // "admin"
el.dataset.status = "active"; // crée data-status="active"
querySelector et querySelectorAll
pour sélectionner vos éléments — ils acceptent n'importe quel sélecteur CSS et sont cohérents.
Utilisez classList plutôt que style pour les modifications visuelles.
8. Les événements
Les événements permettent à votre code de réagir aux interactions de l'utilisateur :
clics, saisies, soumissions de formulaires, défilements de page, etc.
addEventListener est la méthode moderne pour attacher un gestionnaire d'événement.
addEventListener — la syntaxe de base
// Syntaxe : element.addEventListener(type, handler, options)
const bouton = document.querySelector("#mon-bouton");
// Gestionnaire en fonction nommée (recommandé si on veut pouvoir le retirer)
function handleClick(event) {
console.log("Cliqué !", event);
}
bouton.addEventListener("click", handleClick);
// Gestionnaire en arrow function (pratique, mais non retirait)
bouton.addEventListener("click", (event) => {
console.log("Cliqué !", event.target);
});
// Retirer un gestionnaire d'événement
bouton.removeEventListener("click", handleClick);
// L'objet event contient de nombreuses infos
bouton.addEventListener("click", (e) => {
console.log(e.target); // l'élément cliqué
console.log(e.currentTarget); // l'élément sur lequel addEventListener est attaché
console.log(e.type); // "click"
console.log(e.clientX, e.clientY); // position du curseur
});
Événements courants
// Formulaires
const form = document.querySelector("form");
const emailInput = document.querySelector('input[type="email"]');
form.addEventListener("submit", (e) => {
e.preventDefault(); // empêche le rechargement de la page ✅
const email = emailInput.value.trim();
if (!email) {
alert("Email requis !");
return;
}
console.log(`Formulaire soumis avec : ${email}`);
});
// Saisie clavier
emailInput.addEventListener("input", (e) => {
console.log(`Valeur : ${e.target.value}`);
});
emailInput.addEventListener("keydown", (e) => {
if (e.key === "Enter") console.log("Touche Entrée pressée");
if (e.key === "Escape") emailInput.value = "";
console.log(e.key, e.code, e.ctrlKey, e.shiftKey);
});
// Change — déclenché quand la valeur est confirmée (blur ou sélection)
const select = document.querySelector("select");
select.addEventListener("change", (e) => {
console.log(`Option choisie : ${e.target.value}`);
});
// Scroll et resize (sur window)
window.addEventListener("scroll", () => {
const scrollY = window.scrollY;
if (scrollY > 300) {
document.querySelector(".back-to-top").classList.add("visible");
}
});
window.addEventListener("resize", () => {
console.log(`Taille : ${window.innerWidth}x${window.innerHeight}`);
});
// Mouse events
const carte = document.querySelector(".card");
carte.addEventListener("mouseenter", () => carte.classList.add("hovered"));
carte.addEventListener("mouseleave", () => carte.classList.remove("hovered"));
carte.addEventListener("dblclick", () => console.log("Double-clic !"));
event.preventDefault() et event.stopPropagation()
// event.preventDefault() — empêche le comportement par défaut du navigateur
const lien = document.querySelector("a.ajax-link");
lien.addEventListener("click", (e) => {
e.preventDefault(); // ne navigue pas vers href
// Chargement AJAX custom ici
console.log("Navigation interceptée !");
});
// Autres exemples de comportements par défaut qu'on peut bloquer :
// - submit d'un formulaire (rechargement de la page)
// - contextmenu (menu clic droit)
// - drag (glisser-déposer natif)
// event.stopPropagation() — empêche la remontée de l'événement (bubbling)
// Quand on clique sur un enfant, l'événement "remonte" vers ses parents
document.querySelector(".modal").addEventListener("click", () => {
console.log("Clic sur le fond du modal");
fermerModal();
});
document.querySelector(".modal-content").addEventListener("click", (e) => {
e.stopPropagation(); // empêche le clic de remonter jusqu'au fond
console.log("Clic sur le contenu — modal reste ouvert");
});
Délégation d'événements
Au lieu d'attacher un gestionnaire sur chaque élément enfant (coûteux en mémoire),
on attache un seul gestionnaire sur le parent et on utilise
event.target pour identifier quel enfant a déclenché l'événement.
Essentiel pour les éléments créés dynamiquement.
// ❌ Mauvaise pratique — un listener par bouton
document.querySelectorAll(".item-btn").forEach(btn => {
btn.addEventListener("click", handleItemClick);
});
// Si de nouveaux boutons sont ajoutés dynamiquement, ils n'ont pas de listener !
// ✅ Bonne pratique — délégation sur le parent
const liste = document.querySelector("#product-list");
liste.addEventListener("click", (e) => {
// Vérifie si l'élément cliqué (ou un ancêtre) est un .item-btn
const btn = e.target.closest(".item-btn");
if (!btn) return;
const productId = btn.dataset.id;
console.log(`Action sur le produit ${productId}`);
});
// Fonctionne aussi pour les éléments ajoutés après coup !
const nouvelItem = document.createElement("button");
nouvelItem.classList.add("item-btn");
nouvelItem.dataset.id = "42";
nouvelItem.textContent = "Voir le produit";
liste.appendChild(nouvelItem); // capturé automatiquement par la délégation
e.preventDefault() dans les
gestionnaires de submit pour éviter le rechargement de page. Utilisez la
délégation d'événements pour les listes dynamiques — un seul listener est bien plus
performant que des dizaines.
9. La Fetch API et les Promises
La Fetch API permet d'envoyer des requêtes HTTP depuis le navigateur, sans rechargement de page. Elle est basée sur les Promises, un mécanisme JavaScript pour gérer les opérations asynchrones.
Les Promises — comprendre l'asynchrone
// Une Promise représente une valeur future : elle peut être
// - pending (en attente)
// - fulfilled (résolue avec succès)
// - rejected (rejetée avec une erreur)
// Créer une Promise manuellement
const maPromesse = new Promise((resolve, reject) => {
const succes = true;
if (succes) {
resolve("Opération réussie !");
} else {
reject(new Error("Quelque chose a mal tourné"));
}
});
// Consommer une Promise avec .then() / .catch() / .finally()
maPromesse
.then(resultat => {
console.log(resultat); // "Opération réussie !"
return resultat.toUpperCase(); // on peut chaîner les .then()
})
.then(majuscule => console.log(majuscule)) // "OPÉRATION RÉUSSIE !"
.catch(erreur => console.error("Erreur :", erreur.message))
.finally(() => console.log("Terminé, qu'il y ait eu erreur ou non"));
fetch() — requêtes HTTP
// GET — récupérer des données
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP : ${response.status}`);
}
return response.json(); // parse le JSON, retourne une Promise
})
.then(data => {
console.log(data);
// { id: 1, title: "...", body: "...", userId: 1 }
})
.catch(erreur => {
console.error("Erreur de réseau ou serveur :", erreur.message);
});
async/await — syntaxe synchrone pour l'asynchrone
async/await (ES2017) est du sucre syntaxique au-dessus des Promises.
Il rend le code asynchrone aussi lisible que du code synchrone.
// async/await — GET
async function chargerPost(id) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
if (!response.ok) {
throw new Error(`Erreur ${response.status} : ${response.statusText}`);
}
const post = await response.json();
console.log(post.title);
return post;
} catch (erreur) {
console.error("Impossible de charger le post :", erreur.message);
return null;
}
}
chargerPost(1);
// async/await — POST (envoyer des données)
async function creerPost(titre, contenu) {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title: titre,
body: contenu,
userId: 1
})
});
if (!response.ok) throw new Error(`Erreur ${response.status}`);
const nouveauPost = await response.json();
console.log("Post créé avec l'ID :", nouveauPost.id);
return nouveauPost;
} catch (erreur) {
console.error("Erreur lors de la création :", erreur.message);
}
}
creerPost("Mon titre", "Mon contenu...");
JSON.parse et JSON.stringify
// JSON.stringify — convertit un objet JS en chaîne JSON
const utilisateur = { nom: "Alice", age: 28, actif: true };
const jsonString = JSON.stringify(utilisateur);
console.log(jsonString); // '{"nom":"Alice","age":28,"actif":true}'
// Avec indentation (lisible)
console.log(JSON.stringify(utilisateur, null, 2));
// {
// "nom": "Alice",
// "age": 28,
// "actif": true
// }
// JSON.parse — convertit une chaîne JSON en objet JS
const retour = JSON.parse(jsonString);
console.log(retour.nom); // "Alice"
// Utilisation pratique : localStorage
localStorage.setItem("user", JSON.stringify(utilisateur));
const storedUser = JSON.parse(localStorage.getItem("user") || "{}");
console.log(storedUser.nom); // "Alice"
Exemple complet : afficher une liste depuis une API
async function afficherUtilisateurs() {
const conteneur = document.querySelector("#user-list");
conteneur.innerHTML = "<p>Chargement...</p>";
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) throw new Error(`Erreur ${response.status}`);
const users = await response.json();
conteneur.innerHTML = ""; // vider le "Chargement..."
users.forEach(user => {
const li = document.createElement("li");
li.innerHTML = `
<strong>${user.name}</strong> —
<a href="mailto:${user.email}">${user.email}</a>
`;
conteneur.appendChild(li);
});
} catch (erreur) {
conteneur.innerHTML = `<p class="error">Erreur : ${erreur.message}</p>`;
}
}
document.addEventListener("DOMContentLoaded", afficherUtilisateurs);
fetch() ne rejette la Promise qu'en cas d'erreur
réseau (pas d'internet, serveur injoignable). Une réponse HTTP 404 ou 500 ne déclenche
pas le catch — vérifiez toujours response.ok
ou response.status manuellement.
10. ES6+ : les fonctionnalités modernes
ES6 (ECMAScript 2015) a introduit des fonctionnalités révolutionnaires pour JavaScript. Depuis, une nouvelle version sort chaque année. Voici les plus importantes à connaître.
Destructuring avancé
// Destructuring de tableau
const [premier, second, ...reste] = [10, 20, 30, 40, 50];
console.log(premier); // 10
console.log(second); // 20
console.log(reste); // [30, 40, 50]
// Ignorer des éléments
const [, , troisieme] = [1, 2, 3, 4];
console.log(troisieme); // 3
// Swap de variables avec destructuring
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// Destructuring de retour de fonction
function getCoordonnees() {
return { lat: 48.8566, lng: 2.3522, ville: "Paris" };
}
const { lat, lng, ville } = getCoordonnees();
console.log(`${ville} : ${lat}, ${lng}`); // "Paris : 48.8566, 2.3522"
// Destructuring dans les paramètres de fonction
function afficherProfil({ nom, age, role = "membre" }) {
console.log(`${nom}, ${age} ans, rôle : ${role}`);
}
afficherProfil({ nom: "Alice", age: 28 }); // "Alice, 28 ans, rôle : membre"
Modules ES6 — import/export
// utils.js — exporter des fonctions
export function formaterDate(date) {
return new Intl.DateTimeFormat("fr-FR").format(date);
}
export function capitaliser(str) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
export const VERSION = "1.0.0";
// Export par défaut (un seul par fichier)
export default function calculerTTC(ht, tva = 0.2) {
return ht * (1 + tva);
}
// main.js — importer
import calculerTTC, { formaterDate, capitaliser, VERSION } from "./utils.js";
console.log(calculerTTC(100)); // 120
console.log(formaterDate(new Date())); // "14/04/2026"
console.log(capitaliser("bonjour")); // "Bonjour"
console.log(VERSION); // "1.0.0"
// Import de tout avec alias
import * as Utils from "./utils.js";
console.log(Utils.capitaliser("monde")); // "Monde"
// Dans le HTML : <script type="module" src="main.js"></script>
Optional chaining (?.)
// ❌ Sans optional chaining — vérifications longues
const ville = utilisateur &&
utilisateur.adresse &&
utilisateur.adresse.ville;
// ✅ Avec optional chaining — court-circuit si null/undefined
const utilisateur2 = {
nom: "Bob",
adresse: null
};
console.log(utilisateur2?.adresse?.ville); // undefined — pas d'erreur !
console.log(utilisateur2?.telephone?.mobile); // undefined
// Avec des méthodes
const resultat = tableau?.find(item => item.id === 5)?.nom ?? "Non trouvé";
// Avec les tableaux
const premier = tableau?.[0]?.nom;
Map et Set
// Map — dictionnaire clé/valeur (les clés peuvent être de n'importe quel type)
const cache = new Map();
cache.set("user:1", { nom: "Alice", age: 28 });
cache.set("user:2", { nom: "Bob", age: 35 });
console.log(cache.get("user:1")); // { nom: "Alice", age: 28 }
console.log(cache.has("user:3")); // false
console.log(cache.size); // 2
cache.delete("user:2");
cache.forEach((valeur, cle) => {
console.log(`${cle} :`, valeur);
});
// Convertir en tableau
const entrees = Array.from(cache.entries());
// Set — ensemble de valeurs UNIQUES
const tags = new Set(["js", "css", "html", "js", "css"]);
console.log(tags); // Set { "js", "css", "html" } — doublons supprimés
console.log(tags.size); // 3
tags.add("typescript");
tags.delete("css");
console.log(tags.has("js")); // true
// Dédoublonner un tableau avec Set
const avecDoublons = [1, 2, 3, 2, 4, 1, 5];
const sansDoublons = [...new Set(avecDoublons)];
console.log(sansDoublons); // [1, 2, 3, 4, 5]
?. est l'une des fonctionnalités
les plus utiles d'ES2020. Il évite les erreurs Cannot read property of undefined
si courantes lors de la manipulation de données d'une API.
11. Bonnes pratiques
DRY — Don't Repeat Yourself
// ❌ Code dupliqué
document.querySelector("#btn-1").addEventListener("click", () => {
document.querySelector("#btn-1").classList.toggle("active");
});
document.querySelector("#btn-2").addEventListener("click", () => {
document.querySelector("#btn-2").classList.toggle("active");
});
// ✅ Factorisation
document.querySelectorAll(".toggle-btn").forEach(btn => {
btn.addEventListener("click", () => btn.classList.toggle("active"));
});
Nommer clairement les variables et fonctions
// ❌ Noms cryptiques
const x = document.querySelectorAll("li");
function fn(a, b) { return a + b; }
let d = new Date();
// ✅ Noms expressifs
const listeItems = document.querySelectorAll("li");
function calculerSomme(nombreA, nombreB) { return nombreA + nombreB; }
const dateCreation = new Date();
// ✅ Conventions de nommage JS
const MAX_RETRIES = 3; // constante globale → SCREAMING_SNAKE_CASE
function chargerUtilisateurs() {} // fonctions → camelCase
const utilisateurConnecte = {}; // variables → camelCase
class GestionnaireDeFormulaire {} // classes → PascalCase
const _donneePrivee = "interne"; // convention "privé" → prefixe _
Éviter les variables globales
// ❌ Variables globales — polluent le scope global, risques de conflits
var compteur = 0;
function incrementer() { compteur++; }
// ✅ Encapsuler dans un module ou une IIFE (Immediately Invoked Function Expression)
const CompteurModule = (() => {
let _compteur = 0; // privé
return {
incrementer: () => ++_compteur,
decrementer: () => --_compteur,
valeur: () => _compteur,
reset: () => { _compteur = 0; }
};
})();
CompteurModule.incrementer();
CompteurModule.incrementer();
console.log(CompteurModule.valeur()); // 2
Le mode strict
// "use strict" active le mode strict — détecte des erreurs silencieuses
// À placer en tête du fichier JS (ou d'une fonction)
"use strict";
// En mode strict :
// - les variables non déclarées provoquent une ReferenceError
// - le this dans une fonction non-méthode est undefined (pas window)
// - certains mots réservés ne peuvent pas être utilisés comme variables
// - la suppression de propriétés non configurables échoue
x = 10; // ❌ ReferenceError : x is not defined (sans "use strict", ça marcherait !)
// Note : les modules ES6 (import/export) sont automatiquement en mode strict
console.log pour déboguer
// Différentes méthodes de console
console.log("Message normal");
console.warn("⚠️ Avertissement");
console.error("❌ Erreur");
console.info("ℹ️ Information");
// Afficher un tableau formaté
const users = [
{ nom: "Alice", age: 28 },
{ nom: "Bob", age: 35 }
];
console.table(users); // affiche un tableau ASCII dans la console
// Grouper des logs
console.group("Détails utilisateur");
console.log("Nom :", users[0].nom);
console.log("Age :", users[0].age);
console.groupEnd();
// Mesurer les performances
console.time("fetch-users");
// ... opération à mesurer ...
console.timeEnd("fetch-users"); // "fetch-users: 120ms"
// ✅ Penser à retirer les console.log avant la mise en production
// Ou utiliser une variable DEBUG
const DEBUG = false;
const log = (...args) => DEBUG && console.log(...args);
log("Ceci s'affiche uniquement si DEBUG = true");
- Utilisez
constpar défaut,letsi nécessaire - Nommez vos variables et fonctions de manière explicite
- Évitez les variables globales — encapsulez dans des modules ou IIFE
- Activez
"use strict"dans vos scripts non-modules - Appliquez DRY — factorisez le code répété
- Retirez les
console.logen production - Commentez le pourquoi, pas le comment
12. Erreurs fréquentes des débutants
1. undefined vs null — ne pas confondre
// undefined — la variable existe mais n'a pas été assignée
let prenom;
console.log(prenom); // undefined
// null — absence de valeur intentionnelle
let utilisateur = null; // on sait qu'il n'y a pas d'utilisateur
// ❌ Piège courant
function getUser(id) {
// ... si non trouvé, on ne retourne rien
}
const user = getUser(99);
console.log(user.nom); // ❌ TypeError: Cannot read properties of undefined
// ✅ Toujours vérifier avant d'accéder aux propriétés
const user2 = getUser(99);
if (user2) {
console.log(user2.nom);
}
// Ou avec optional chaining :
console.log(user2?.nom); // undefined — pas d'erreur
2. == vs === (rappel crucial)
// ❌ Un bug classique
const input = document.querySelector("input").value; // retourne TOUJOURS une string
if (input == 5) { // "5" == 5 est true à cause de la coercition
// ... ce code s'exécute, mais pour de mauvaises raisons !
}
// ✅ Comparer correctement
const valeur = parseInt(input, 10); // convertir explicitement
if (valeur === 5) {
// ...
}
3. Le contexte de this
// ❌ this perdu dans un callback classique
class Minuteur {
constructor() {
this.secondes = 0;
}
demarrer() {
setInterval(function() {
this.secondes++; // ❌ this est window (ou undefined en strict mode)
console.log(this.secondes);
}, 1000);
}
}
// ✅ Solution 1 : arrow function (hérite du this du contexte englobant)
class Minuteur2 {
constructor() {
this.secondes = 0;
}
demarrer() {
setInterval(() => {
this.secondes++; // ✅ this est bien l'instance de Minuteur2
console.log(this.secondes);
}, 1000);
}
}
// ✅ Solution 2 : bind(this)
class Minuteur3 {
constructor() {
this.secondes = 0;
this.tick = this.tick.bind(this);
}
tick() { this.secondes++; }
demarrer() { setInterval(this.tick, 1000); }
}
4. Oublier await pour les fonctions async
// ❌ Oublier await — on manipule une Promise, pas la valeur !
async function afficherPost() {
const post = fetch("https://jsonplaceholder.typicode.com/posts/1"); // manque await !
console.log(post.title); // undefined — post est une Promise, pas un objet
}
// ✅ Toujours await les fonctions async
async function afficherPost2() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const post = await response.json(); // await aussi le .json()
console.log(post.title); // "sunt aut facere..."
}
// ❌ Utiliser await en dehors d'une fonction async
// const data = await fetch("..."); // SyntaxError !
// ✅ Top-level await disponible dans les modules ES6
// (dans un fichier .js chargé avec type="module")
// const data = await fetch("..."); // OK dans un module
5. addEventListener dans une boucle
// ❌ Problème classique avec var dans une boucle
const boutons = document.querySelectorAll(".btn");
for (var i = 0; i < boutons.length; i++) {
boutons[i].addEventListener("click", function() {
console.log(`Bouton ${i} cliqué`); // i vaut TOUJOURS boutons.length à l'exécution !
});
}
// ✅ Solution 1 : utiliser let (portée de bloc)
for (let i = 0; i < boutons.length; i++) {
boutons[i].addEventListener("click", function() {
console.log(`Bouton ${i} cliqué`); // ✅ i est capturé correctement
});
}
// ✅ Solution 2 : utiliser forEach (recommandé)
boutons.forEach((btn, i) => {
btn.addEventListener("click", () => {
console.log(`Bouton ${i} cliqué`); // ✅
});
});
// ✅ Solution 3 : délégation d'événements (la meilleure approche)
document.querySelector(".btn-container").addEventListener("click", (e) => {
const btn = e.target.closest(".btn");
if (!btn) return;
const index = [...boutons].indexOf(btn);
console.log(`Bouton ${index} cliqué`);
});
13. Conclusion & ressources
Vous avez maintenant une vue complète des fondamentaux du JavaScript moderne : des variables et types jusqu'à la Fetch API, en passant par la manipulation du DOM, les événements, les closures et les fonctionnalités ES6+.
JavaScript est un langage vaste et en constante évolution. Ce guide couvre l'essentiel pour démarrer en intégration web, mais l'apprentissage ne s'arrête pas là. La prochaine étape naturelle est de découvrir un framework comme Angular, React ou Vue.js qui s'appuient tous sur ces fondamentaux.
Ce que vous savez maintenant faire
- Déclarer des variables avec
const/letet connaître les types primitifs - Utiliser les opérateurs
===, ternaire,?? - Écrire des boucles (
for...of,forEach) et conditions - Créer des fonctions (déclarations, expressions, arrow functions)
- Manipuler les tableaux avec
map,filter,reduce - Manipuler le DOM avec
querySelector,classList,createElement - Attacher des événements et comprendre la délégation
- Interroger une API avec
fetch()etasync/await - Utiliser les fonctionnalités ES6+ (destructuring, spread, modules, optional chaining)
- Éviter les erreurs courantes des débutants
Ressources pour aller plus loin
- MDN Web Docs — JavaScript : la référence officielle et complète, disponible en français. La documentation la plus fiable.
- javascript.info : le meilleur tutoriel gratuit et complet sur JavaScript moderne. Couvre du débutant à l'avancé.
- Eloquent JavaScript : livre en ligne gratuit (en anglais) — excellent pour apprendre à programmer en JS.
- DevTools Console (F12) : ouvrez la console de votre navigateur et testez directement tous les exemples de cet article.
- freeCodeCamp — JavaScript Algorithms : exercices pratiques pour consolider vos acquis par la pratique.