6.3 — APIs du navigateur
Débutant
🎯 Objectif : connaître ce que le navigateur offre nativement avant de tirer un framework. Beaucoup de besoins se résolvent en 5 lignes de vanilla JS.
À l'issue de cet axe, tu sauras :
- Manipuler le DOM (sélection, création, événements)
- Faire des requêtes avec fetch et FormData
- Choisir entre localStorage, sessionStorage, IndexedDB
- Utiliser IntersectionObserver, MutationObserver, ResizeObserver
- Décharger un calcul lourd vers un Web Worker
Le DOM — Document Object Model
Section intitulée « Le DOM — Document Object Model »Le DOM est l’arbre d’objets qui représente la page. JavaScript peut le lire et le modifier.
Sélection
Section intitulée « Sélection »// Un élémentdocument.querySelector('#header')document.querySelector('.btn-primary')document.querySelector('input[type="email"]')document.getElementById('header') // plus rapide mais moins flexible
// Plusieurs (NodeList)document.querySelectorAll('.card')
// Itérerdocument.querySelectorAll('.card').forEach(card => card.classList.add('visible'));Manipulation
Section intitulée « Manipulation »const el = document.querySelector('#title');
// Texteel.textContent = 'Nouveau titre'; // brut, sécuriséel.innerHTML = '<strong>HTML</strong>'; // ⚠ XSS si la valeur vient de l'utilisateur
// Attributsel.setAttribute('data-state', 'open');el.dataset.state = 'open'; // raccourci pour data-*el.classList.add('active');el.classList.toggle('active');el.classList.contains('active');
// Styleel.style.backgroundColor = 'blue'; // évite, préfère ajouter une classeel.style.setProperty('--color', '#f00'); // pour les variables CSS
// Créationconst div = document.createElement('div');div.textContent = 'Nouveau';document.body.append(div); // append : à la fin (1+ enfants ou strings)parent.prepend(child); // au débutparent.insertBefore(newNode, refNode); // avant un autre
// Suppressionel.remove();Événements
Section intitulée « Événements »button.addEventListener('click', (event) => { console.log(event.target); event.preventDefault(); // empêche le comportement par défaut});
// Une fois seulementbutton.addEventListener('click', handler, { once: true });
// Délégation — un seul listener pour de nombreux enfantsdocument.querySelector('.list').addEventListener('click', (e) => { if (e.target.matches('.item')) { console.log('Item cliqué :', e.target.textContent); }});Fetch — requêtes HTTP
Section intitulée « Fetch — requêtes HTTP »// GET simpleconst response = await fetch('/api/users');if (!response.ok) throw new Error(`HTTP ${response.status}`);const users = await response.json();
// POST JSONconst r = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Alice' }),});
// FormData (upload, formulaires)const formData = new FormData();formData.append('file', fileInput.files[0]);formData.append('name', 'Alice');
await fetch('/api/upload', { method: 'POST', body: formData, // pas besoin de Content-Type, le navigateur le met});
// Avec credentials (cookies)fetch('/api/me', { credentials: 'include' });Status vs Network error
Section intitulée « Status vs Network error »try { const r = await fetch('/api'); if (!r.ok) { // 4xx/5xx — la requête est partie, mais le serveur a refusé throw new Error(`HTTP ${r.status}`); } const data = await r.json();} catch (err) { if (err instanceof TypeError) { // erreur réseau (DNS, offline, CORS bloqué…) }}Important : fetch ne rejette pas sur 404 ou 500. Il faut tester r.ok. Il rejette uniquement sur erreurs réseau ou CORS.
URL et URLSearchParams
Section intitulée « URL et URLSearchParams »const url = new URL('https://example.com/search?q=cat&page=2');url.protocol; // "https:"url.hostname; // "example.com"url.pathname; // "/search"url.searchParams.get('q'); // "cat"url.searchParams.set('q', 'dog');url.searchParams.append('tag', 'pet');url.toString(); // "https://example.com/search?q=dog&page=2&tag=pet"
// Construire à partir de paramètresconst params = new URLSearchParams({ q: 'cat', page: 2 });fetch(`/search?${params}`);Bien plus propre que la concat de strings.
Storage navigateur
Section intitulée « Storage navigateur »| API | Capacité | Persistance | API |
|---|---|---|---|
| localStorage | ~5 Mo | Persistante | clé/valeur (string) |
| sessionStorage | ~5 Mo | Onglet uniquement | idem |
| IndexedDB | 50 % du disque | Persistante | base structurée async |
| Cookies | ~4 Ko | Configurable | envoyé au serveur |
localStorage / sessionStorage
Section intitulée « localStorage / sessionStorage »localStorage.setItem('user', JSON.stringify({ id: 1, name: 'Alice' }));
const user = JSON.parse(localStorage.getItem('user'));localStorage.removeItem('user');localStorage.clear();
// Quota dépassétry { localStorage.setItem('big', hugeData);} catch (err) { if (err.name === 'QuotaExceededError') { // 5 Mo dépassés }}Limites :
- Synchrone (bloque le thread).
- Stockage de strings uniquement →
JSON.stringify. - Accessible en JS → jamais de tokens d’auth dedans.
IndexedDB
Section intitulée « IndexedDB »// API native verbeuse — utilise plutôt une lib comme "idb"import { openDB } from 'idb';
const db = await openDB('myApp', 1, { upgrade(db) { db.createObjectStore('todos', { keyPath: 'id', autoIncrement: true }); },});
await db.add('todos', { title: 'Apprendre IndexedDB' });const todos = await db.getAll('todos');Pour des gros volumes ou des données structurées (cache offline d’une PWA, par exemple).
Observers
Section intitulée « Observers »3 observers utiles pour réagir à des changements sans polling.
IntersectionObserver — détecter la visibilité
Section intitulée « IntersectionObserver — détecter la visibilité »Cas typiques : lazy-load d’images, scroll spy, animation à l’apparition.
const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); // arrêter d'observer } });}, { threshold: 0.1, // 10 % visible rootMargin: '50px', // commence 50px avant});
document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));MutationObserver — détecter les changements DOM
Section intitulée « MutationObserver — détecter les changements DOM »const observer = new MutationObserver((mutations) => { for (const m of mutations) { if (m.type === 'childList') { console.log('Enfants ajoutés/retirés'); } }});
observer.observe(document.querySelector('#content'), { childList: true, subtree: true, attributes: true,});Utile pour les libs tierces qui injectent du DOM (chats, widgets, etc.).
ResizeObserver — détecter les changements de taille
Section intitulée « ResizeObserver — détecter les changements de taille »const observer = new ResizeObserver((entries) => { for (const e of entries) { console.log(`Largeur : ${e.contentRect.width}`); }});
observer.observe(document.querySelector('.responsive-element'));Bien plus performant que window.addEventListener('resize') couplé à des calculs : ne se déclenche que pour l’élément observé, et seulement quand sa taille change.
Web Workers — paralléliser les calculs lourds
Section intitulée « Web Workers — paralléliser les calculs lourds »Un Web Worker tourne dans un vrai thread parallèle. Pas d’accès au DOM, mais utile pour les calculs CPU-intensifs.
const worker = new Worker(new URL('./compute.js', import.meta.url), { type: 'module' });
worker.postMessage({ data: hugeArray });
worker.onmessage = (e) => { console.log('Résultat :', e.data);};self.onmessage = (e) => { const result = doHeavyCalculation(e.data.data); self.postMessage(result);};Cas d’usage : compression, encodage, parsing de gros JSON, machine learning côté client (tensorflow.js).
Service Workers — un proxy entre la page et le réseau
Section intitulée « Service Workers — un proxy entre la page et le réseau »Voir axe 2.4. Permet :
- Mode hors-ligne (cache des ressources).
- Notifications push.
- Background sync.
C’est la base d’une PWA.
History API — manipuler l’URL sans recharger
Section intitulée « History API — manipuler l’URL sans recharger »// Ajouter à l'historiquehistory.pushState({ id: 42 }, '', '/products/42');
// Remplacer l'URL actuellehistory.replaceState(null, '', '/login');
// Réagir au bouton retourwindow.addEventListener('popstate', (e) => { console.log('État précédent :', e.state); router.navigate(location.pathname);});C’est la base d’un router SPA : on change l’URL sans recharger, on rend une nouvelle vue.
Clipboard, geolocation, file system — APIs récentes
Section intitulée « Clipboard, geolocation, file system — APIs récentes »// Clipboardawait navigator.clipboard.writeText('hello');const text = await navigator.clipboard.readText();
// Geolocation (demande permission)navigator.geolocation.getCurrentPosition( (pos) => console.log(pos.coords.latitude, pos.coords.longitude), (err) => console.error(err),);
// File System Access API (Chrome surtout)const [handle] = await window.showOpenFilePicker();const file = await handle.getFile();const text = await file.text();Auto-évaluation
Section intitulée « Auto-évaluation »Pour aller plus loin
Section intitulée « Pour aller plus loin »- MDN — Web APIs : developer.mozilla.org/en-US/docs/Web/API
- web.dev — Patterns : web.dev/patterns
- Can I use : caniuse.com — vérifier le support navigateur d’une API
Suite : 6.4 — TypeScript — typer ton JS pour éliminer une catégorie entière de bugs.