Abbiamo già visto che una pagina web è composta da elementi (tag HTML) che formano il cosiddetto DOM (Document Object Model), ovvero una struttura ad albero che ci dà accesso al contenuto della pagina e ci permette di cambiare o creare qualunque cosa all’interno della stessa.
Gli elementi della pagina possono emettere eventi (click, submit, change, ecc.) che possiamo intercettare con JavaScript, ad esempio usando addEventListener():
Abbiamo visto, inoltre, che diversi eventi vengono gestiti direttamente dal browser (es. la navigazione al click su un link, o l’invio di un form); è comunque possibile prevenire il comportamento predefinito usando il metodo preventDefault() dell’oggetto evento passato alla funzione di callback:
Abbiamo più volte accennato all’elemento HTML <form>, senza però approfondirlo dovutamente.
Un form (letteralmente modulo) è un elemento utilizzato per collezionare gli input degli utenti ed inviarli poi ad un server che processerà quelle informazioni (ad esempio, per registrare un nuovo utente, o per effettuare una ricerca):
Al suo interno, un form può contenere diversi tipi di input (testo, data, select, ecc.) che l’utente può compilare.
Tipicamente contiene anche un bottone di tipo submit che, quando cliccato, invia i dati del form al server.
action e methodUn form HTML ha due attributi che controllano come i dati vengono inviati al server:
action: URL del server dove inviare i dati (es. /api/registrazione)method: tipo di richiesta HTTP:
GET: dati visibili in URL, max ~2KBPOST: dati nel body, size illimitato, per dati sensibiliIn questa lezione useremo preventDefault() per bloccare l’invio tradizionale e gestire i dati con JavaScript.
Tramite l’attributo type di un elemento <input> possiamo specificare il tipo di dato che vogliamo ricevere dall’utente, e il browser ci fornirà un’interfaccia adatta per quel tipo di dato.
type="text": testo generico, su un’unica riga
type="password": testo nascosto (per password):
type="email": testo con validazione email
type="number": input per numeri, con frecce +/-
type="date": selettore per date, con calendario
type="file": input per file (upload)
type="checkbox": spunta (per selezioni multiple)
type="radio": spunta (per selezioni singole)
Come sempre, ci sono molti altri tipi di input che possiamo esplorare nella documentazione MDN.
Il primo approccio è leggere i campi uno per uno con id o name, usando .value.
const form = document.getElementById('loginForm');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
form.addEventListener('submit', function(event) {
event.preventDefault();
const email = emailInput.value;
const password = passwordInput.value;
console.log({ email, password });
});Questo approccio è semplice e chiaro, ma diventa molto verboso se il form ha molti campi.
Generalmente è preferibile evitare questo metodo salvo necessità specifiche (es. campi dinamici senza name).
document.formsI form del documento sono esposti dal DOM in document.forms:
Si tratta di una collezione contenente tutti i form della pagina, accessibili tramite name o indice; è infatti una cosiddetta “named collection”, sia associativa che ordinata.
Ciascun oggetto form ha a sua volta una collezione elements che contiene tutti i campi del form, anch’essa accessibile tramite name o indice.
Se più campi condividono lo stesso name, form.elements[name] restituisce una collezione:
Un form può avere uno o più elementi <fieldset> all’interno.
Questi elementi si comportano come dei “sub-form”, e hanno una proprietà elements per accedere agli elementi al loro interno:
FormDataIl metodo più conciso per accedere a un form è usando l’oggetto FormData.
const form = document.querySelector('#loginForm');
form.addEventListener('submit', function(event) {
event.preventDefault();
const formData = new FormData(this); // Crea FormData dal form
// Convertire a oggetto JavaScript
const dati = Object.fromEntries(formData);
console.log(dati); // { username: 'mario', password: '123', email: 'mario@example.com' }
});Il costruttore FormData accetta un elemento form e legge automaticamente tutti i campi con name, creando un oggetto dedicato.
È particolarmente utile per inviare dati a un server, in quanto i metodi per le richieste di rete, come ad esempio fetch, possono accettare gli oggetti FormData come body della richiesta, ma è anche un modo rapido per ottenere un oggetto con tutti i dati del form senza dover accedere a ciascun campo manualmente.
Leggi i dati di un form
Crea un form HTML con almeno 3 campi di input (ad esempio: nome, email, messaggio).
Scrivi JavaScript che:
FormDataVerifica: compila il form e clicca il bottone. In console dovrai vedere un oggetto con le proprietà corrispondenti ai campi compilati.
checkbox, radio, selectA differenza degli input di testo, i campi di selezione richiedono un trattamento speciale per leggere i valori:
Checkbox:
Radio (selezione esclusiva):
Select (menu a tendina):
Tramite FormData, invece, questi campi vengono gestiti automaticamente, restituendo i valori selezionati.
La validazione lato client (nel browser) migliora l’esperienza utente:
Attenzione
Bisogna comunque tenere a mente che la validazione client-side non è sicura! Devi sempre validare anche sul server (backend).
Un utente esperto può bypassare la validazione JavaScript. La sicurezza vera è sul server.
HTML5 fornisce attributi di validazione integrati:
<form id="regForm">
<input type="text" name="username"
required
minlength="3"
maxlength="20"
placeholder="Username (3-20 caratteri)">
<input type="email" name="email"
required
placeholder="Email obbligatoria">
<input type="number" name="eta"
required
min="18"
max="99"
placeholder="Età (18-99)">
<input type="password" name="password"
required
pattern="[A-Za-z0-9]{6,}"
placeholder="Password (min 6 caratteri alfanumerici)">
<button type="submit">Registrati</button>
</form>| Attributo | Descrizione | Esempio |
|---|---|---|
required |
Campo obbligatorio | <input required> |
minlength |
Lunghezza minima | <input minlength="3"> |
maxlength |
Lunghezza massima | <input maxlength="20"> |
min |
Valore minimo (number, date) | <input type="number" min="18"> |
max |
Valore massimo (number, date) | <input type="number" max="120"> |
pattern |
Espressione regolare | <input pattern="[0-9]{5}"> |
Il browser mostra messaggi di errore automatici quando si prova a inviare un form non valido.
Si tratta di due approcci complementari per validare i form:
Validazione HTML5 (dichiarativa):
La soluzione più semplice, con messaggi automatici.
Validazione JavaScript (programmatica):
Permette logica complessa e messaggi personalizzati, ma richiede più codice.
Generalmente, è consigliato utilizzare HTML5 solamente per la validazione base.
JavaScript è da preferirsi per tutte le validazioni un minimo complesse.
L’attributo pattern dei campi input è espresso tramite espressioni regolari (regex).
Si tratta di pattern testuali per utilizzati per validare formati specifici:
| Pattern | Descrizione | Esempio |
|---|---|---|
[0-9]{5} |
5 cifre (es. CAP) | <input pattern="[0-9]{5}"> |
[A-Za-z0-9]{6,12} |
Alfanumerico 6-12 caratteri | <input pattern="[A-Za-z0-9]{6,12}"> |
[A-Za-z]+ |
Solo lettere | <input pattern="[A-Za-z]+"> |
JavaScript mette a disposizione un oggetto nativo per lavorare con le regex:
Si tratta di uno strumento molto potente ma altrettanto complesso; non lo approfondiremo in questa sede.
Se siete interessati, regex101.com e MDN sono risorse eccellenti.
Oltre a submit, possiamo intercettare altri eventi utili per una validazione real-time.
| Evento | Quando scatta | Utilizzo pratico |
|---|---|---|
focus |
Campo selezionato | Suggerimenti, evidenziazione |
blur |
Focus rimosso dal campo | Messaggi (i.e. “Campo vuoto”) |
input |
Ad ogni carattere digitato | Feedback in tempo reale |
change |
Focus rimosso dal campo dopo aver modificato il valore | Validazione al completamento |
Form con validazione HTML5
Crea un form di registrazione con:
Prova a inviare il form senza compilarlo, con email non valida, con numero incompleto. Osserva i messaggi di errore automatici del browser.
Form con validazione in JavaScript
Riscrivi lo stesso form, ma questa volta senza usare gli attributi di validazione HTML5. Implementa la validazione usando JavaScript.
La compilazione di un form è un’azione che spesso vogliamo memorizzare per l’utente.
Tuttavia, non possiamo semplicemente salvare i dati in variabili JavaScript, perché quando la pagina viene ricaricata, lo stato del programma viene azzerato.
Il browser offre diverse soluzioni per la persistenza dei dati:
localStorage e sessionStorage)I cookie sono stati storicamente il metodo principale per la persistenza dei dati nel browser. Si tratta di piccole stringhe di dati, memorizzate direttamente nel browser, che vengono inviate al server ad ogni richiesta HTTP.
Limitazioni:
get/set)Il browser fornisce API moderne e semplici per salvare dati localmente, dette Web Storage. Sono disponibili due storage con interfaccia identica ma durata diversa:
localStorage: dati persistono indefinitamentesessionStorage: dati scompaiono al riavvio browser (o chiusura tab)Entrambi offrono la stessa API:
Limite: ~5-10 MB per dominio, dati salvati come stringhe.
localStorage permette di salvare dati che persistono indefinitamente nel browser:
// Salvare preferenze
localStorage.setItem('tema', 'dark');
localStorage.setItem('lingua', 'it');
// Leggere
const tema = localStorage.getItem('tema');
console.log('Tema salvato:', tema); // 'dark'
// Controllare se esiste
if (localStorage.getItem('tema')) {
console.log('Tema trovato!');
}
// Rimuovere e pulire
localStorage.removeItem('tema');
localStorage.clear(); // Cancella tuttoProva ad aprire la Console dei DevTools e scrivere questi comandi!
I dati rimangono anche se ricarichi la pagina (F5).
Due API identiche, ma con durata diversa:
| Caratteristica | localStorage | sessionStorage |
|---|---|---|
| Durata | Indefinita (finché non cancellata) | Fino alla chiusura del tab |
| Condivisione | Condivisa tra tutti i tab dello stesso sito | Isolata per ogni tab |
| Uso tipico | Preferenze utente, temi, cache | Dati temporanei, sessione |
L’API è esattamente la stessa (setItem, getItem, removeItem, clear).
Limitazioni di sicurezza
Non salvare dati sensibili (password, token, carte di credito) in localStorage!
È vulnerabile ad attacchi XSS: qualsiasi script sulla pagina può leggere i dati.
Salviamo i dati del form in localStorage per recuperarli al prossimo caricamento:
const form = document.querySelector('#profiloForm');
// Al caricamento, recupera dati salvati
window.addEventListener('DOMContentLoaded', function() {
const nomeSalvato = localStorage.getItem('nome');
const emailSalvata = localStorage.getItem('email');
if (nomeSalvato) form.nome.value = nomeSalvato;
if (emailSalvata) form.email.value = emailSalvata;
});
// Al submit, salva dati
form.addEventListener('submit', function(event) {
event.preventDefault();
localStorage.setItem('nome', form.nome.value);
localStorage.setItem('email', form.email.value);
alert('Profilo salvato!');
});Ora i dati rimangono anche dopo la ricarica della pagina!
Aggiungi form e localStorage al ricettario
Riprendiamo il ricettario della lezione 4. L’obiettivo è aggiungere un form per inserire nuove ricette e salvare il ricettario in localStorage in modo che le ricette rimangano anche dopo il riavvio della pagina.
Funzionalità richieste:
Ricetta, passarla a ricettario.aggiungi(), salvare in localStoragelocalStorage e ricaricarleAssicurarsi che la navigazione tra le ricette funzioni correttamente anche dopo il salvataggio.
Niccolò Maltoni