Form HTML
Raccogliere dati dagli utenti: input, validazione e invio al server. Il ponte tra HTML statico e applicazioni dinamiche.
Introduzione
Un form HTML è una porzione di pagina web che permette agli utenti di inserire e inviare dati. Ogni volta che ci registriamo, scriviamo un commento, facciamo una ricerca o effettuiamo un acquisto online, stiamo usando un form.
A differenza delle prime tre lezioni, qui non parleremo di layout o stili: i form sono il ponte tra HTML statico e applicazioni dinamiche. In questa lezione vedremo come costruirli, come validare i dati e come questi viaggiano fino al server.
Le lezioni precedenti riguardavano la presentazione (Flexbox, Grid, Media Queries). Questa lezione riguarda l'interazione: come l'utente comunica col sito.
L'elemento <form>
Il tag <form> è il contenitore che raggruppa tutti i campi e definisce dove e come saranno inviati i dati. Al suo interno si inseriscono input, textarea, select e pulsanti.
Attributi principali
| Attributo | Comportamento |
|---|---|
action | URL della risorsa che riceverà i dati. |
method | Metodo HTTP: get o post. |
name | Nome del form (utile via JavaScript). |
autocomplete | Abilita/disabilita autocompletamento (on/off). |
novalidate | Disabilita la validazione automatica del browser. |
enctype | Codifica dei dati. Necessario per upload file (multipart/form-data). |
target | Dove aprire la risposta (_self, _blank, …). |
Ogni form serio ha sempre almeno: una <label>, un campo (<input>/<textarea>/<select>) e un pulsante di submit. Tutto il resto è opzionale.
Tipi di input
L'elemento <input> cambia comportamento drasticamente in base all'attributo type. Vediamoli per categorie.
Testuali — text, password, email, url, tel, search
Pur somigliandosi visivamente, alcuni di questi attivano una tastiera virtuale specifica su mobile e applicano una validazione automatica.
Su smartphone, type="email" mostra la tastiera con il tasto @; type="tel" mostra un tastierino numerico; type="url" facilita l'inserimento di indirizzi web. Scegliere il type giusto è anche un'ottimizzazione UX.
Numerici — number, range
Si combinano con min, max e step per definire i vincoli. Lo slider qui sotto aggiorna live il valore mostrato.
Data/tempo — date, time, datetime-local, month, week
Mostrano un selettore (date picker) appropriato. Aspetto e supporto variano leggermente tra browser.
Selezione — checkbox e radio
La differenza fondamentale: checkbox = più scelte indipendenti; radio = scelta unica fra opzioni dello stesso gruppo. Per raggruppare radio button basta condividere lo stesso name.
Se due <input type="radio"> hanno name diversi, NON sono mutuamente esclusivi. Errore classico — il sintomo è “perché posso selezionarne due insieme?”.
File — type="file"
Permette il caricamento di uno o più file. accept filtra i tipi accettati, multiple abilita la selezione multipla.
<!-- una sola immagine -->
<input type="file" name="foto" accept="image/*">
<!-- più documenti PDF/DOCX -->
<input type="file" name="docs" accept=".pdf,.docx" multiple>Per inviare file con un form serve impostare enctype="multipart/form-data" sul tag <form>. Lo vedremo nella sezione “Invio dei dati”.
Speciali — color, hidden
Il color picker apre il selettore nativo del sistema. Il colore scelto colora live il quadrato accanto.
<!-- Hidden: l'utente non lo vede, ma viene inviato -->
<input type="hidden" name="id_utente" value="42">Azioni — submit, reset, button, image
Pulsanti realizzati tramite <input>. Sono ancora supportati per retrocompatibilità, ma nei progetti nuovi è preferibile il tag <button> (vedi sezione successiva).
<input type="submit" value="Invia">
<input type="reset" value="Resetta">Tabella riassuntiva
type="text"
Testo libero (default)
type="password"
Caratteri mascherati
type="email"
Validazione email + tastiera @
type="url"
Validazione URL
type="tel"
Tastiera numerica mobile
type="search"
Campo ricerca (con ×)
type="number"
Numeri con frecce ↑↓
type="range"
Slider grafico
type="date"
Selettore data
type="time"
Selettore ora
type="datetime-local"
Data + ora
type="month"
Mese e anno
type="week"
Settimana e anno
type="checkbox"
Selezione multipla
type="radio"
Selezione esclusiva
type="file"
Upload file
type="color"
Color picker
type="hidden"
Nascosto, ma inviato
type="submit"
Pulsante invio
type="reset"
Pulsante reset
Label e accessibilità
La <label> è uno degli elementi più sottovalutati e più importanti dei form. Ogni input dovrebbe avere una label associata.
- Accessibilità — gli screen reader leggono la label all'utente quando il focus arriva sul campo.
- Usabilità — cliccando sul testo della label il focus passa automaticamente al campo. Cruciale per checkbox e radio, che hanno aree cliccabili minuscole.
- Chiarezza — il modulo è più comprensibile e professionale.
Metodo 1 — for / id (raccomandato)
<label for="email">Indirizzo email:</label>
<input type="email" id="email" name="email">La label e l'input possono stare anche distanti nel codice, purché gli identificatori coincidano.
Metodo 2 — annidamento
<label>
<input type="checkbox" name="newsletter"> Iscriviti alla newsletter
</label>Non serve id/for. Particolarmente comodo per checkbox e radio.
Molti principianti pensano che il placeholder sostituisca la label. NO: il placeholder sparisce non appena si scrive, e gli screen reader spesso lo ignorano. Usate sempre una <label>.
Altri elementi dei form
<textarea> — testo multi-riga
Mentre <input type="text"> è limitato a una riga,<textarea> permette testo lungo su più righe. Va sempre chiusa con </textarea>; il valore iniziale si scrive fra i tag, non con value.
<label for="messaggio">Messaggio:</label>
<textarea id="messaggio" name="messaggio"
rows="5" cols="40" maxlength="500"
placeholder="Scrivi qui..."></textarea><select>, <option> e <optgroup>
Menu a tendina. L'attributo value di <option> è ciò che viene inviato; il testo visibile è solo per l'utente. Con liste lunghe conviene raggruppare in <optgroup>.
<label for="citta">Città:</label>
<select id="citta" name="citta">
<optgroup label="Svizzera">
<option value="lugano">Lugano</option>
<option value="bellinzona" selected>Bellinzona</option>
<option value="zurigo">Zurigo</option>
</optgroup>
<optgroup label="Italia">
<option value="milano">Milano</option>
<option value="roma">Roma</option>
</optgroup>
</select>
<!-- Selezione multipla -->
<select name="lingue" multiple size="4">
<option value="it" selected>Italiano</option>
<option value="en">English</option>
<option value="de">Deutsch</option>
</select><button> — pulsanti moderni
A differenza di <input type="submit">, può contenere HTML al suo interno (icone, testo formattato, ecc.). I tre type:
| type | Comportamento |
|---|---|
submit | Invia il form (default dentro un form). |
reset | Riporta i campi ai valori iniziali. |
button | Pulsante generico, non fa nulla senza JS. |
<button type="submit">Invia</button>
<button type="reset">Annulla</button>
<button type="button">Pulsante generico</button>Dentro un <form>, un <button> senza type esplicito è di default submit. Questo causa invii accidentali (es. premendo Invio in un campo). Indicate sempre type="submit", type="reset" o type="button".
<fieldset> e <legend> — raggruppare campi
Quando un form ha molti campi, è utile organizzarli in gruppi tematici con <fieldset>. <legend>, posto come primo figlio, fornisce un titolo annunciato dagli screen reader come contesto per tutti i campi interni.
<form action="/registrazione" method="post">
<fieldset>
<legend>Dati personali</legend>
<label for="nome">Nome:</label>
<input type="text" id="nome" name="nome">
<label for="cognome">Cognome:</label>
<input type="text" id="cognome" name="cognome">
</fieldset>
<fieldset>
<legend>Account</legend>
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<label for="pwd">Password:</label>
<input type="password" id="pwd" name="pwd">
</fieldset>
<button type="submit">Registrati</button>
</form>Validazione HTML5
HTML5 offre attributi che fanno validare i dati al browser, prima ancora di inviarli al server. Se i dati non sono validi, il browser mostra un messaggio di errore e blocca l'invio.
Gli attributi di validazione
| Attributo | Effetto |
|---|---|
required | Campo obbligatorio. |
type="email" / url | Validazione automatica del formato. |
min / max / step | Vincoli numerici e di data. |
minlength / maxlength | Vincoli sulla lunghezza del testo. |
pattern | Espressione regolare (regex) custom. |
title | Tooltip esplicativo in caso di errore. |
Prova a inviare il form qui sotto con campi vuoti o malformati: il browser mostra il messaggio di errore in italiano. Lo stile :user-invalid colora di rosso solo i campi che l'utente ha già toccato.
Pattern e regex (cenno)
L'attributo pattern richiede che il valore corrisponda a un'espressione regolare. Le regex sono un argomento ampio: per ora basti sapere che esistono e sono lo strumento per validazioni complesse.
<!-- esattamente 4 cifre (es. CAP svizzero) -->
<input type="text" name="cap" pattern="[0-9]{4}"
title="Inserisci un CAP svizzero di 4 cifre" required>
<!-- targa svizzera tipo TI 12345 -->
<input type="text" name="targa" pattern="[A-Z]{2} [0-9]{1,6}">Disabilitare la validazione: novalidate
<form action="/bozza" method="post" novalidate>
<!-- la validazione è disabilitata, anche con required -->
</form>È solo una cortesia per l'utente onesto: un attaccante può aggirarla in 10 secondi (disabilitando JS, modificando il DOM, inviando con curl). La validazione server è sempre necessaria. Tornerà nella sezione “Cenno al backend”.
Stilizzare i form
Gli elementi dei form si stilizzano con CSS come tutti gli altri, ma con qualche peculiarità.
Selettori utili
| Selettore | Descrizione |
|---|---|
input[type="text"] | Tutti gli input testuali. |
:focus | Elemento attualmente con il focus. |
:hover | Elemento sotto il cursore. |
:disabled | Elementi disabilitati. |
:required | Campi obbligatori. |
:valid / :invalid | Stato di validazione. |
:user-valid / :user-invalid | Validità solo dopo interazione utente (consigliato). |
:placeholder-shown | Campo che mostra il placeholder (vuoto). |
Esempio completo
Form di contatto con stili moderni: bordi arrotondati, focus visibile, feedback rosso su :user-invalid, hover sul bottone.
<select>, checkbox/radio e <input type="file"> sono notoriamente difficili da stilizzare in modo uniforme su tutti i browser: la freccia del select, il pallino del radio e il pulsante “Sfoglia” sono grafiche di sistema. Per progetti professionali si nascondono gli elementi nativi e si ricreano con CSS o librerie dedicate.
Invio dei dati
Cosa succede quando l'utente preme “Invia”? Il browser:
- raccoglie tutti i campi del form che hanno un attributo
name; - li impacchetta come coppie
name=valore; - li invia all'URL specificato in
action, con il metodo HTTP definito inmethod; - attende la risposta del server (a meno di intercettazione via JavaScript).
L'attributo name: il fondamento
Regola d'oro: solo i campi con name vengono inviati al server. Un campo senza name esiste visivamente, ma è invisibile ai dati trasmessi. Premi “Invia” nel demo qui sotto per vedere quali campi finiscono nella query string:
Un campo senza name è invisibile per il server. Errore classico: si dimentica name e ci si chiede perché il dato non arriva.
L'attributo action: dove vanno i dati
<!-- URL relativo: stesso dominio del sito -->
<form action="/registrazione" method="post">
<!-- URL assoluto: dominio diverso -->
<form action="https://api.esempio.ch/contatti" method="post">
<!-- Action vuoto: stessa pagina (default) -->
<form action="" method="post">L'attributo method: GET vs POST
<form action="/cerca" method="get">
<input type="text" name="q" value="formaggio">
<input type="text" name="cat" value="alimentari">
<button type="submit">Cerca</button>
</form>
<!-- URL risultante -->
<!-- /cerca?q=formaggio&cat=alimentari --><form action="/login" method="post">
<input type="text" name="user">
<input type="password" name="pwd">
<button type="submit">Login</button>
</form>
<!-- URL: /login (i dati sono nascosti nel body) -->| Caratteristica | GET | POST |
|---|---|---|
| Visibilità dati | Nell'URL | Nel body |
| Salvabile nei preferiti | Sì | No |
| Modifica dati sul server | No (lettura) | Sì (scrittura) |
| Adatto a password | No | Sì |
| Adatto a file | No | Sì |
| Caso d'uso tipico | Ricerche, filtri | Login, registrazione, invio |
GET per leggere/cercare (i parametri sono visibili e linkabili). POST per inviare/modificare (password, dati sensibili, file).
L'attributo enctype
Specifica come codificare i dati prima di trasmetterli. Rilevante solo con method="post".
| Valore | Quando usarlo |
|---|---|
application/x-www-form-urlencoded | Default. Adatto alla maggior parte dei form. |
multipart/form-data | Obbligatorio quando il form contiene <input type="file">. |
text/plain | Solo per debug, non in produzione. |
<!-- Form di upload: serve enctype="multipart/form-data" -->
<form action="/carica" method="post" enctype="multipart/form-data">
<input type="file" name="documento">
<button type="submit">Carica</button>
</form>Cenno al backend
Il form HTML è solo metà del lavoro. L'altra metà è sul server: senza un endpoint che “ascolti”, il form non serve a nulla.
[Browser/Form] --POST--> [Server: PHP, Node.js, Python, ...] --> [Database / Email / ...]
--> Risposta al browserQuando il browser invia un form, il programma server tipicamente:
- riceve la richiesta HTTP con i dati;
- estrae i campi cercandoli per
name; - li valida nuovamente (mai fidarsi del client!);
- li elabora: salva in database, invia un'email, autentica un utente, ecc.;
- risponde al browser con una nuova pagina, un redirect o un messaggio.
La validazione HTML5 è una comodità per l'utente, NON una misura di sicurezza. Un utente malintenzionato può aggirarla in pochi secondi (disabilitando JavaScript, modificando l'HTML con i devtools, o inviando richieste con curl/Postman). Ogni validazione fatta nel form va sempre ripetuta sul server: solo lì può davvero proteggere i dati e l'applicazione.
La realizzazione concreta del backend viene affrontata in moduli successivi del corso, dove vedremo come scrivere endpoint che ricevono ed elaborano i form qui costruiti.
Esercizi
✏️Metti in pratica
Esercizio A — Base
Form di contatto
Costruisci un form con:
- Nome (
type="text",required) - Email (
type="email",required) - Messaggio (
<textarea>,maxlength="500") - Pulsante invia (
type="submit") - Tutte le label associate correttamente con
for/id
Risultato atteso: il browser blocca l'invio se i campi obbligatori sono vuoti o se l'email è malformata.
Esercizio B — Intermedio
Registrazione utente
Costruisci un form di registrazione con tre <fieldset>:
- Dati personali: nome, cognome, data di nascita (con
min/maxper limitare gli anni) - Account: email, password (
minlength="8"), conferma password - Preferenze:
<select>con paese, checkbox newsletter, radio per genere - Pulsanti: Registrati (
type="submit") e Annulla (type="reset")
Risultato atteso: form completo con tutti gli attributi di validazione, organizzato in fieldset visivamente distinti.
Esercizio C — Avanzato
Iscrizione SSSE/SIG con pattern
Costruisci un form “Iscrizione SSSE/SIG” che includa:
- Nome (testo,
required,minlength="2") - Cognome (testo,
required,minlength="2") - Email (
type="email",required) - CAP svizzero (
pattern="[0-9]{4}"+titleesplicativo) - Telefono CH (
patternper il formato+41 XX XXX XX XX) - Anno di nascita (
type="number",min="1950",max="2026")
Risultato atteso: form con messaggi di errore configurati tramite title; gli stili :user-invalid colorano in rosso i campi non validi.