ITS Web Design e Strategie Digitali
ITS Academy I-CREA @ CFP Bauer

Guida interattiva ai

Form HTML

Costruire ponti interattivi tra utente e server

Onnipresenti e indispensabili

I moduli (form) HTML sono il meccanismo principale per raccogliere input dagli utenti. Sono ovunque nell'esperienza web quotidiana: login, registrazione, ricerca, e-commerce, sondaggi, commenti.

Trasformano la navigazione da passiva (leggere contenuti) ad attiva (interagire, inviare informazioni).

L'elemento cardine per la creazione di un modulo è il tag <form>. Questo elemento agisce da contenitore per tutti i controlli — campi di testo, pulsanti, checkbox, ecc. — che costituiscono un modulo specifico.

Cosa imparerete:

  • Come creare la struttura di un form e inviare dati a un server
  • Tutti i tipi di controlli: testo, selezione, checkbox, date, file...
  • Come raggruppare e validare i dati
  • I principi di accessibilità nei form

L'Anatomia di un Form

Un form HTML è composto da diversi "pezzi" che lavorano insieme. Ecco una mappa mentale per orientarvi:

<form>

<form> ... </form>

Il contenitore: dove e come inviare i dati

<input> text

Campo testo a riga singola

<input> checkbox / radio

Scelte multiple o singole

<input> date / color / range

Controlli specializzati del browser

<label>

Le etichette: descrivono ogni controllo

<textarea>

Testo su più righe (messaggi, commenti)

<select>

Menu a tendina per scelta tra opzioni

<fieldset> + <legend>

Gruppo...

I raggruppamenti: organizzano sezioni correlate

<button>

I pulsanti: avviano l'invio

In questa lezione esploreremo ognuno di questi pezzi in dettaglio, costruendo form sempre più completi.

Il Contenitore <form>

Il tag <form> è il contenitore per tutti i controlli di un modulo. Due attributi fondamentali:

  • action: l'URL di destinazione dei dati (dove inviarli). Se omesso, invia alla stessa pagina (sconsigliato).
  • method: come inviare i dati. Due valori:
    • GET (default): dati visibili nell'URL. Come una cartolina: tutti possono leggere. Va bene per ricerche e filtri.
    • POST: dati nel corpo della richiesta, non visibili nell'URL. Come una busta chiusa. Obbligatorio per dati sensibili.
<form action="/registrazione" method="POST">
  <!-- Qui andranno i controlli -->
</form>

Regola: è severamente vietato annidare un <form> dentro un altro <form>.

method:

Risposta del server

Premete "Invia" per vedere cosa riceve il server.

Se non specificate method, il browser usa GET di default. Ricordatevi di scrivere method="POST" quando servono dati non visibili nell'URL!

Provate voi!

Scrivete il Vostro Primo Form

Aprite CodePen e scrivete un form da zero. Provate a inviarlo e osservate cosa succede!

Codice di partenza

<!-- Scrivete un form che invia i dati all'endpoint di test -->
<form action="https://guida-form.pages.dev/api/echo" method="GET" target="_blank">

  <p>Il vostro nome: <input type="text" name="nome"></p>

  <button type="submit">Invia</button>
</form>

Cosa esplorare

  1. Inviate il form: si apre una nuova pagina che mostra i dati ricevuti dal server. Cosa vedete?
  2. Guardate la barra degli indirizzi della pagina di risposta: il vostro nome è visibile nell'URL?
  3. Tornate indietro e cambiate method="GET" in method="POST". Inviate di nuovo. L'URL è diverso?
  4. Provate ad aggiungere un secondo campo (es. <input type="text" name="cognome">) e inviate: cosa cambia nella risposta?
  5. Cosa succede se togliete name="nome" dall'input e inviate?

Etichette e Primi Controlli

Il controllo più comune è <input type="text">: un campo per testo a riga singola. È un tag "vuoto" (non ha tag di chiusura).

Ma un input da solo non basta: l'utente non sa cosa scriverci! Serve un'etichetta: il tag <label>.

Collegare label e input con for/id:

  1. Si dà un id univoco all'input: <input id="nome_utente">
  2. Si assegna for alla label con lo stesso valore: <label for="nome_utente">

Benefici:

  • Accessibilità: lo screen reader legge l'etichetta quando l'utente raggiunge il campo
  • Usabilità: cliccando sulla label, il cursore va nel campo (area cliccabile più grande)
<label for="nome_utente">Nome Utente:</label>
<input type="text" id="nome_utente">

Senza label

Cosa ci scrivo?

Label non collegata

Click sulla label: non succede nulla

Label collegata

Click sulla label: il cursore va nel campo!

Ogni controllo interattivo in un form (tranne i pulsanti) deve avere una <label> associata con for/id. Non usate mai solo del testo vicino all'input senza collegarlo!

Come Viaggiano i Dati

Tre concetti fondamentali per capire come i form inviano informazioni:

1. name — il badge identificativo per il server

L'attributo name identifica il dato per il server. Come un badge identificativo: senza badge, il server ignora il campo. Il name è l'etichetta per il server; il <label> è l'etichetta per l'utente.

2. value — il valore associato

Per i campi di testo, il value è ciò che l'utente digita. I dati viaggiano come coppie name=value (es. username=Mario).

3. placeholder — il testo fantasma

Testo suggerimento nel campo vuoto, come il testo grigio scritto a matita in un modulo cartaceo: scompare quando si inizia a scrivere. NON sostituisce la <label>.

4. <button type="submit">

Il pulsante che avvia la raccolta e l'invio di tutte le coppie name=value.

<form action="/processa" method="POST">
  <label for="user">Nome Utente:</label>
  <input type="text" id="user" name="username" placeholder="mario_rossi">

  <button type="submit">Invia</button>
</form>
<!-- Se l'utente scrive "Mario", il server riceve: username=Mario -->

Dati del form (coppie name=value)

namevalueinviato?

Ricordate: id collega la label all'input (per l'utente). name identifica il dato per il server. Sono due cose diverse e servono entrambe!

Attenzione ai <button> nei form!

Un <button> dentro un <form> senza attributo type viene trattato dal browser come type="submit" di default. Questo significa che cliccandolo si invia il form! Se volete un pulsante che non invii (es. per toggle o altre azioni), dovete specificare type="button".

Provate voi!

Costruite un Form di Contatto

Aprite CodePen e mettete insieme tutto quello che avete imparato fin qui: form, label, input, name, e submit.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <!-- 1. Aggiungete un campo "Nome" con label, id, e name -->

  <!-- 2. Aggiungete un campo "Email" con label, id, e name -->

  <!-- 3. Aggiungete il pulsante di invio -->

</form>

Cosa esplorare

  1. Create i campi seguendo i commenti. Ogni campo ha bisogno di <label for="...">, <input id="..." name="...">, e un placeholder
  2. Inviate il form: la risposta mostra tutte le coppie name=value?
  3. Cosa succede se dimenticate il name su un campo? Appare nella risposta?
  4. Provate a cliccare sul testo della label: il cursore va nel campo?
  5. Cambiate method da POST a GET: vedete i dati nell'URL della risposta?

Campi Speciali: Password e Email

Oltre a type="text", esistono tipi di input specializzati che il browser gestisce in modo diverso:

type="password":

  • Maschera il testo con pallini (••••) — protezione visiva da sguardi indiscreti
  • Attenzione: è solo una maschera visiva! La password viaggia come testo normale nella richiesta

type="email":

  • Il browser verifica che il testo assomigli a un'email (contiene @, dominio, ecc.)
  • Su mobile, mostra una tastiera ottimizzata con @ e . subito accessibili
  • Se il formato è sbagliato, blocca l'invio con un messaggio di errore
<input type="password" id="pwd" name="password">
<input type="email" id="email" name="user_email">

type="text"

Nessuna validazione, nessuna maschera

type="password"

Mostra pallini ••••

type="email"

Scrivete "ciao" e premete Invia ↓

Il tipo password protegge dagli sguardi, non dalla trasmissione. Per la sicurezza della trasmissione serve HTTPS (il lucchetto nel browser). Il tipo email offre una validazione base gratuita, molto utile!

Testo su Più Righe: <textarea>

Per messaggi, commenti o descrizioni lunghe, <input> non basta: serve <textarea>.

Differenze strutturali da <input>:

  • Ha tag di apertura e chiusura: <textarea>...</textarea>
  • Il testo predefinito va tra i tag (non nell'attributo value)
  • Qualsiasi contenuto tra i tag viene trattato come testo semplice

Attributi per dimensioni suggerite:

  • rows: numero di righe visibili (altezza iniziale)
  • cols: numero di caratteri visibili (larghezza iniziale)
  • Oggi si preferisce il CSS per le dimensioni reali
  • Molti browser permettono il ridimensionamento manuale (gestibile con CSS resize)
  • placeholder funziona anche qui
<label for="messaggio">Il vostro messaggio:</label>
<textarea id="messaggio" name="user_message" rows="5" cols="40"
          placeholder="Scrivete qui il messaggio..."></textarea>
rows: 4
cols: 40

Attenzione agli spazi! Se scrivete <textarea> testo </textarea> con spazi extra, quegli spazi appariranno nel campo. Il contenuto tra i tag è letterale.

Provate voi!

Sperimentate con i Tipi di Input

Aprite CodePen e arricchite il vostro form con campi password, email e textarea.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <p>
    <label for="nome">Nome:</label>
    <input type="text" id="nome" name="username" placeholder="Mario Rossi">
  </p>
  <p>
    <label for="email">Email:</label>
    <!-- Cambiate il type qui sotto: provate "email" -->
    <input type="text" id="email" name="user_email" placeholder="mario@example.com">
  </p>
  <p>
    <label for="pwd">Password:</label>
    <!-- Cambiate il type qui sotto: provate "password" -->
    <input type="text" id="pwd" name="user_pwd">
  </p>
  <p>
    <label for="msg">Messaggio:</label><br>
    <textarea id="msg" name="user_message" rows="4" cols="40"></textarea>
  </p>

  <button type="submit">Invia</button>
</form>

Cosa esplorare

  1. Cambiate il tipo del campo email in type="email". Provate a scrivere "ciao" e inviare: cosa succede?
  2. Cambiate il tipo del campo password in type="password". I caratteri vengono mascherati?
  3. Scrivete un messaggio lungo nella textarea. Provate a ridimensionarla trascinando l'angolo
  4. Inviate il form: tutti i campi appaiono nella risposta con le coppie name=value?
  5. Provate a cambiare il valore di rows della textarea: l'altezza iniziale cambia?

Raggruppare con <fieldset> e <legend>

Quando i form crescono, servono raggruppamenti logici. Come i cassetti di un armadietto: ogni cassetto contiene documenti correlati, con un'etichetta sul fronte.

  • <fieldset>: il cassetto. Raggruppa controlli correlati. Il browser disegna un bordo attorno.
  • <legend>: l'etichetta del cassetto. Deve essere il primo elemento figlio dentro <fieldset>.

Benefici:

  • Chiarezza visiva: il form è diviso in sezioni
  • Accessibilità: lo screen reader annuncia la <legend> prima di ogni campo nel gruppo
<fieldset>
  <legend>Dati Anagrafici</legend>
  <p>
    <label for="nome">Nome:</label>
    <input type="text" id="nome" name="nome">
  </p>
  <p>
    <label for="cognome">Cognome:</label>
    <input type="text" id="cognome" name="cognome">
  </p>
</fieldset>

Scelta Singola: Radio Button

Quando l'utente deve scegliere una sola opzione da un gruppo (esclusiva), si usa <input type="radio">. Come i canali di una vecchia TV: solo uno alla volta.

Regole fondamentali:

  • Tutti i radio dello stesso gruppo devono avere lo stesso name: è il name condiviso che li raggruppa
  • Ogni radio deve avere un value diverso: è il valore inviato al server
  • checked pre-seleziona un'opzione (solo una per gruppo)
  • Una volta selezionato, un radio non può essere deselezionato senza JavaScript
  • Ogni radio ha la propria <label> (con for/id)
  • Vanno sempre dentro <fieldset>/<legend> per l'accessibilità
<fieldset>
  <legend>Dimensione Bevanda</legend>
  <p>
    <input type="radio" id="piccolo" name="dimensione" value="s">
    <label for="piccolo">Piccolo</label>
  </p>
  <p>
    <input type="radio" id="medio" name="dimensione" value="m">
    <label for="medio">Medio</label>
  </p>
  <p>
    <input type="radio" id="grande" name="dimensione" value="l" checked>
    <label for="grande">Grande</label>
  </p>
</fieldset>
<!-- Se scelgo "Medio", il server riceve: dimensione=m -->
Dimensione Bevanda

Dati del form (coppie name=value)

namevalueinviato?
Il server riceve:

Scelta Multipla: Checkbox

Quando l'utente può scegliere zero, una o più opzioni (non esclusive), si usa <input type="checkbox">. Come una lista della spesa: spunti quello che vuoi.

Gestione del name:

  • Opzioni correlate (stessa domanda, es. "interessi"): stesso name, con [] alla fine → name="interessi[]"
  • Opzioni indipendenti (es. "accetto termini", "newsletter"): name diversi

value è fondamentale! Se manca, il browser invia un inutile "on". Specificatelo sempre.

Solo le checkbox selezionate vengono inviate. checked pre-seleziona (più di una possibile).

<!-- Stesso name per opzioni correlate -->
<fieldset>
  <legend>Interessi</legend>
  <input type="checkbox" id="sport" name="interessi[]" value="sport">
  <label for="sport">Sport</label>
  <input type="checkbox" id="musica" name="interessi[]" value="musica" checked>
  <label for="musica">Musica</label>
</fieldset>

<!-- Name diversi per opzioni indipendenti -->
<input type="checkbox" id="terms" name="accetta_termini" value="si">
<label for="terms">Accetto i termini</label>

<!-- Senza value: il browser invia "on" -->
<input type="checkbox" id="test" name="test_senza_value">
<label for="test">Test senza value</label>
Interessi

Dati del form (coppie name=value)

namevalueinviato?
Il server riceve:
Provate voi!

Sperimentate con Radio e Checkbox

Aprite CodePen e create una sezione sondaggio con radio button e checkbox.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="GET" target="_blank">

  <!-- Radio: scelta singola -->
  <fieldset>
    <legend>Qual è il vostro linguaggio preferito?</legend>
    <p>
      <input type="radio" id="html" name="linguaggio" value="html">
      <label for="html">HTML</label>
    </p>
    <p>
      <input type="radio" id="css" name="linguaggio" value="css">
      <label for="css">CSS</label>
    </p>
    <!-- Aggiungete un'altra opzione "JavaScript" -->
  </fieldset>

  <!-- Checkbox: scelta multipla -->
  <fieldset>
    <legend>Quali strumenti usate? (più risposte)</legend>
    <!-- Aggiungete 3 checkbox con name="strumenti[]" -->
    <!-- Suggerimento: value="vscode", "figma", "github" -->
  </fieldset>

  <button type="submit">Invia Sondaggio</button>
</form>

Cosa esplorare

  1. Completate i radio e le checkbox seguendo i commenti
  2. Inviate con GET: osservate l'URL. I radio appaiono come linguaggio=valore, le checkbox come strumenti[]=valore
  3. Cosa succede se date name diversi ai radio? Si possono selezionare tutti?
  4. Provate a togliere il value da una checkbox: cosa appare nella risposta?
  5. Aggiungete checked a una delle opzioni radio e a due checkbox: sono già selezionate al caricamento?
Esercizio

Modulo di Registrazione

Obiettivo

Create un form di registrazione completo che usa tutti gli elementi visti nella Parte 1.

Dati Personali

Preferenze

Ruolo:

Interessi:

Indicazioni

  1. Il form deve inviare i dati in POST all'endpoint di test, aprendosi in una nuova scheda
  2. La sezione "Dati Personali" raccoglie tre informazioni: come pensate di raccoglierle in modo che il browser possa aiutarvi con la validazione e la privacy?
  3. Ogni campo deve essere accessibile: cosa serve perché uno screen reader sappia descriverlo all'utente?
  4. La sezione "Preferenze" ha due domande diverse: una ammette una sola risposta, l'altra più di una. Quali tipi di input corrispondono a questi due comportamenti?
  5. Pensate ai name: il server deve poter distinguere ogni dato ricevuto. Per le opzioni dello stesso gruppo, come si indicano?
  6. Non dimenticate un modo per inviare il tutto!

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <fieldset>
    <legend>Dati Personali</legend>
    <!-- Aggiungete: nome (text), email (email), password (password) -->
    <!-- Ogni campo: label + input con id, name, placeholder -->
  </fieldset>

  <fieldset>
    <legend>Preferenze</legend>
    <!-- Aggiungete: 3 radio "Ruolo" con name="ruolo" -->
    <!-- Aggiungete: 2 checkbox "Interessi" con name="interessi[]" -->
  </fieldset>

  <!-- Aggiungete il pulsante submit -->
</form>
Mostra Soluzione
<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <fieldset>
    <legend>Dati Personali</legend>
    <p>
      <label for="reg-nome">Nome:</label>
      <input type="text" id="reg-nome" name="nome" placeholder="Mario Rossi">
    </p>
    <p>
      <label for="reg-email">Email:</label>
      <input type="email" id="reg-email" name="email" placeholder="mario@example.com">
    </p>
    <p>
      <label for="reg-pwd">Password:</label>
      <input type="password" id="reg-pwd" name="password">
    </p>
  </fieldset>

  <fieldset>
    <legend>Preferenze</legend>

    <p><strong>Ruolo:</strong></p>
    <p>
      <input type="radio" id="reg-designer" name="ruolo" value="designer">
      <label for="reg-designer">Designer</label>
    </p>
    <p>
      <input type="radio" id="reg-developer" name="ruolo" value="developer">
      <label for="reg-developer">Developer</label>
    </p>
    <p>
      <input type="radio" id="reg-entrambi" name="ruolo" value="entrambi">
      <label for="reg-entrambi">Entrambi</label>
    </p>

    <p><strong>Interessi:</strong></p>
    <p>
      <input type="checkbox" id="reg-frontend" name="interessi[]" value="frontend">
      <label for="reg-frontend">Frontend</label>
    </p>
    <p>
      <input type="checkbox" id="reg-backend" name="interessi[]" value="backend">
      <label for="reg-backend">Backend</label>
    </p>
  </fieldset>

  <button type="submit">Registrati</button>
</form>

Riepilogo

Elemento / AttributoScopo
<form>Contenitore del modulo
actionURL di destinazione dei dati
methodMetodo di invio: GET (URL) o POST (corpo)
<input type="text">Campo testo a riga singola
<input type="password">Campo con testo mascherato
<input type="email">Campo email con validazione base
<textarea>Campo testo multi-riga
<label for="...">Etichetta collegata a un controllo (via id)
nameIdentificativo del dato per il server
valueValore del dato inviato
placeholderSuggerimento nel campo vuoto (non sostituisce label)
<button type="submit">Pulsante per inviare il form
<fieldset> + <legend>Raggruppamento logico con titolo
<input type="radio">Scelta singola (name condiviso, value unico)
<input type="checkbox">Scelta multipla (value obbligatorio!)
checkedPre-selezione per radio/checkbox

Menu a Tendina: <select> e <option>

Per scegliere da una lista predefinita di opzioni, si usa il menu a tendina (dropdown) con <select> e <option>.

  • <select>: il contenitore, con name (per l'invio) e id (per la label)
  • <option>: ogni voce selezionabile. Il testo tra i tag è ciò che l'utente vede; l'attributo value è ciò che viene inviato al server

Specificare sempre value: se manca, il browser invia il testo interno dell'opzione.

<label for="nazione">Nazione:</label>
<select id="nazione" name="user_nation">
  <option value="it">Italia</option>
  <option value="fr">Francia</option>
  <option value="de">Germania</option>
</select>

Risposta del server

Premete "Invia" per vedere quale valore viene inviato.

Opzioni Predefinite e Segnaposto

Attributi per controllare il comportamento delle opzioni:

  • selected: opzione predefinita al caricamento della pagina
  • disabled: opzione visibile ma non selezionabile (appare grigia)

Pattern segnaposto (best practice):

<option value="" disabled selected>-- Selezionate un paese --</option>

Con value="" + disabled + selected: appare come scelta iniziale, obbliga l'utente a scegliere attivamente. Se il <select> ha required, questa opzione non sarà considerata valida.

<label for="paese">Paese di Residenza:</label>
<select id="paese" name="user_country">
  <option value="" disabled selected>-- Selezionate un paese --</option>
  <option value="it">Italia</option>
  <option value="fr">Francia</option>
  <option value="es" disabled>Spagna (Non disponibile)</option>
</select>

Raggruppare e Selezione Multipla

<optgroup> per raggruppare opzioni sotto un titolo non selezionabile:

  • Attributo label obbligatorio (il titolo del gruppo)
<select id="bevanda" name="drink">
  <optgroup label="Bevande Calde">
    <option value="the">Tè</option>
    <option value="caffe">Caffè</option>
  </optgroup>
  <optgroup label="Bevande Fredde">
    <option value="acqua">Acqua</option>
    <option value="succo">Succo</option>
  </optgroup>
</select>

multiple per selezione multipla:

  • Trasforma la tendina in una lista scrollabile
  • L'utente seleziona più voci con Ctrl/Cmd+Click
  • size suggerisce quante righe mostrare
  • Il server riceve più valori per lo stesso name
<select id="ingredienti" name="pizza[]" multiple size="5">
  <option value="pomodoro">Pomodoro</option>
  <option value="mozzarella">Mozzarella</option>
  <option value="basilico">Basilico</option>
  <option value="funghi">Funghi</option>
  <option value="prosciutto">Prosciutto</option>
</select>

Provate voi!

Costruite Menu a Tendina

Aprite CodePen e create select con optgroup e provate la selezione multipla.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="GET" target="_blank">
  <p>
    <label for="orario">Orario Preferito:</label>
    <select id="orario" name="pref_hour">
      <option value="" disabled selected>-- Selezionate --</option>
      <!-- Aggiungete optgroup "Mattina" con opzioni 9:00, 10:00, 11:00 -->
      <!-- Aggiungete optgroup "Pomeriggio" con opzioni 14:00, 15:00, 16:00 -->
    </select>
  </p>

  <button type="submit">Invia</button>
</form>

Cosa esplorare

  1. Completate gli optgroup con le opzioni. Ogni option ha un value significativo (es. "09:00")
  2. Inviate: il value selezionato appare nell'URL?
  3. Aggiungete multiple al select: come cambia l'aspetto?
  4. Con multiple, provate a selezionare più voci (Ctrl/Cmd+Click). Inviate: appaiono tutti i valori?
  5. Aggiungete size="4" con multiple: quante righe vedete?

Input Numerici

<input type="number"> per valori numerici:

  • Mostra spinner (+/-) su desktop, tastiera numerica su mobile
  • Attributi: min, max, step, value, placeholder

Attenzione a step

  • Default step="1": accetta solo numeri interi. Provare a scrivere "3.5" dà errore!
  • step="0.01": accetta centesimi (utile per prezzi)
  • step="0.1": accetta un decimale
  • step="any": qualsiasi decimale senza limiti di precisione
<input type="number" name="qty" min="1" max="10" step="1" value="1">
<input type="number" name="price" min="0" step="0.01" placeholder="Es. 19.99">
<input type="number" name="measure" step="any">
min: 0
max: 100
step:

Lo Slider e <output>

<input type="range"> per selezione approssimativa con cursore:

  • Essenziale specificare min, max, step
  • Problema principale: non mostra il valore numerico selezionato!

<output> risolve: elemento semantico per mostrare risultati. Si collega con for (punta all'id del range).

Con solo HTML, il valore non si aggiorna muovendo lo slider. Serve JavaScript.

<label for="soddisfazione">Soddisfazione (1-5):</label>
<input type="range" id="soddisfazione" name="satisfaction"
       min="1" max="5" step="1" value="3">
<output for="soddisfazione">3</output>
3

Provate a muovere lo slider in modalità "Senza JS": l'output resta fisso! Poi attivate "Con JS" per vedere la differenza. Questo è un caso dove JavaScript fa una vera differenza nell'usabilità.

Provate voi!

Sperimentate con i Valori Numerici

Aprite CodePen e provate i controlli numerici.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="GET" target="_blank">
  <p>
    <label for="qty">Quantità (1-10):</label>
    <input type="number" id="qty" name="quantita" min="1" max="10" step="1" value="1">
  </p>
  <p>
    <label for="budget">Budget:</label>
    <input type="range" id="budget" name="budget" min="0" max="1000" step="50" value="500">
    <output for="budget">500</output>
  </p>

  <button type="submit">Invia</button>
</form>

Cosa esplorare

  1. Provate a scrivere un numero decimale (3.5) nel campo quantità: cosa succede?
  2. Cambiate step="1" in step="any" e riprovate con un decimale
  3. Muovete lo slider budget: l'output si aggiorna? (No, serve JS!)
  4. Inviate il form: il valore del range appare nella risposta?
  5. Cambiate step="50" del range in step="100": lo slider "salta" diversamente?

Date e Orari

Gestire date e orari è complicato: 10/10, Oct 10, 10 Ottobre... HTML semplifica con input specializzati che attivano selettori nativi e inviano formati standardizzati.

TipoSelezionaFormato inviato
dateAnno, Mese, GiornoAAAA-MM-GG
timeOraHH:MM
datetime-localData + OraAAAA-MM-GGTHH:MM
monthAnno, MeseAAAA-MM
weekAnno, SettimanaAAAA-WNN

Vincoli con min, max, step:

  • min/max: limite date/ore selezionabili (formato coerente col tipo)
  • step per time: in secondi (900 = 15 min, 1800 = 30 min)
  • step per date: in giorni (7 = stesso giorno della settimana)
<input type="date" name="event_date">
<input type="time" name="start_time" min="07:00" max="18:00" step="1800">
<input type="datetime-local" name="arrival"
       min="2026-01-01T00:00" max="2026-12-31T23:59">

Risposta del server

Premete "Invia" per vedere i formati standardizzati.

Input Specializzati: search, tel, url

TipoVantaggiValidazione?
searchSemantica corretta, possibile X per cancellare, ricerche recentiNo
telTastiera telefonica su mobileNo! Accetta qualsiasi testo
urlTastiera con / e . su mobileSì: richiede schema (http/https)
<input type="search" name="query" placeholder="Parola chiave...">
<input type="tel" name="phone" placeholder="+39 123 456 7890">
<input type="url" name="website" placeholder="https://esempio.com">

type="search"

Possibile icona X per cancellare

type="tel"

Scrivete "ciao": nessun errore!

type="url"

Scrivete "ciao" e provate a inviare: errore!

type="tel" NON valida il formato! Per controllare che sia un numero di telefono valido, servono pattern o controlli server-side.

Colore e Campo Nascosto

type="color": apre il selettore colori nativo del sistema operativo. Il valore è sempre una stringa esadecimale #rrggbb minuscola.

<input type="color" name="fav_color" value="#0000ff">

type="hidden": campo completamente invisibile all'utente, ma partecipa all'invio. Deve avere name e value nel codice. Non necessita <label>.

Casi d'uso: ID utente, token di sicurezza, ID prodotto, timestamp, versione del form.

<input type="hidden" name="user_id" value="12345">
<input type="hidden" name="csrf_token" value="aBcDeF">

Dati del form (coppie name=value)

namevalueinviato?

Suggerimenti con <datalist>

<datalist> offre suggerimenti di autocompletamento senza forzare la scelta.

Funzionamento:

  1. Si crea un <datalist> con id e <option> con i suggerimenti
  2. Si collega a un <input> con l'attributo list (punta all'id del datalist)
  3. Il datalist è invisibile. Quando l'utente digita, il browser mostra le opzioni corrispondenti
  4. L'utente può scegliere un suggerimento o ignorarlo e scrivere altro

Funziona con: text, search, url, tel, email. Supporto variabile per date, time, number, range, color.

<input type="text" id="browser" name="user_browser" list="browsers_list">

<datalist id="browsers_list">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
  <option value="Opera">
</datalist>
Provate voi!

Sperimentate con gli Input Avanzati

Aprite CodePen e provate i controlli avanzati: date, colore, datalist e campo nascosto.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="GET" target="_blank">
  <p>
    <label for="data">Data Evento:</label>
    <input type="date" id="data" name="event_date">
  </p>
  <p>
    <label for="colore">Colore Tema:</label>
    <input type="color" id="colore" name="theme_color" value="#3b82f6">
  </p>
  <p>
    <label for="citta">Città:</label>
    <input type="text" id="citta" name="city" list="city_suggestions">
    <datalist id="city_suggestions">
      <option value="Milano">
      <option value="Roma">
      <option value="Napoli">
      <option value="Torino">
      <option value="Firenze">
    </datalist>
  </p>

  <input type="hidden" name="form_version" value="2.0">

  <button type="submit">Invia</button>
</form>

Cosa esplorare

  1. Cliccate sul campo data: appare un calendario? Inviate: in che formato arriva la data?
  2. Cliccate sul selettore colore: scegliete un colore e inviate. Che formato ha il valore?
  3. Nel campo città, iniziate a scrivere "Mi...": appaiono suggerimenti? Potete scrivere una città non in lista?
  4. Guardate la risposta: c'è anche il campo hidden form_version=2.0?
  5. Aggiungete min al campo data (es. min="2026-01-01"): potete selezionare date precedenti?
Esercizio

Form di Prenotazione

Obiettivo

Create un form di prenotazione che combina i controlli della Parte 2: select con optgroup, date, number, range, datalist, e hidden.

200

Indicazioni

  1. Guardate la preview qui sopra: è il risultato che dovete ottenere. Il codice di partenza ha già la struttura — completate i pezzi mancanti
  2. Il menu "Tipo Camera" ha due categorie: Standard (Singola, Doppia) e Premium (Suite, Deluxe). Quale elemento HTML raggruppa le opzioni sotto un'etichetta? Come si impedisce che il segnaposto iniziale venga inviato?
  3. Le date di check-in e check-out sono già nel codice. Controllate che tipo di input servono per far apparire il selettore calendario
  4. Il campo "Ospiti" accetta da 1 a 6 persone, solo numeri interi. Quale tipo di input offre gli spinner +/- e impedisce valori fuori range?
  5. Il "Budget" va da 50€ a 500€ a scatti di 50€. Quale tipo di input mostra un cursore? Come si fa a mostrare il valore selezionato accanto ad esso?
  6. Per la "Città" serve un campo di testo che suggerisca almeno 4 città italiane senza forzare la scelta. Quale meccanismo di autocompletamento avete visto nella Parte 2?
  7. Il form deve portarsi dietro un codice offerta "SUMMER2026" che l'utente non vede né modifica. Quale tipo di input è pensato per dati invisibili?
  8. Inviate e controllate la risposta del server: compaiono tutti i campi? Se ne manca qualcuno, cosa potrebbe essere sfuggito?

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <p>
    <label for="pren-camera">Tipo Camera:</label>
    <select id="pren-camera" name="room_type">
      <!-- Segnaposto + optgroup Standard + optgroup Premium -->
    </select>
  </p>

  <p>
    <label for="pren-checkin">Check-in:</label>
    <input type="date" id="pren-checkin" name="checkin">
    <label for="pren-checkout">Check-out:</label>
    <input type="date" id="pren-checkout" name="checkout">
  </p>

  <p>
    <label for="pren-ospiti">Ospiti:</label>
    <!-- Completate questo input: name="guests" -->
    <input id="pren-ospiti">
  </p>

  <p>
    <label for="pren-budget">Budget:</label>
    <!-- Completate questo input: name="budget" -->
    <input id="pren-budget">
  </p>

  <p>
    <label for="pren-citta">Città:</label>
    <!-- Completate questo input: name="city" -->
    <input id="pren-citta">
  </p>

  <!-- Aggiungete un campo: name="offerta_id" -->

  <button type="submit">Prenota</button>
</form>
Mostra Soluzione
<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">

  <p>
    <label for="pren-camera">Tipo Camera:</label>
    <select id="pren-camera" name="room_type">
      <option value="" disabled selected>-- Selezionate --</option>
      <optgroup label="Standard">
        <option value="single">Singola</option>
        <option value="double">Doppia</option>
      </optgroup>
      <optgroup label="Premium">
        <option value="suite">Suite</option>
        <option value="deluxe">Deluxe</option>
      </optgroup>
    </select>
  </p>

  <p>
    <label for="pren-checkin">Check-in:</label>
    <input type="date" id="pren-checkin" name="checkin">
    <label for="pren-checkout">Check-out:</label>
    <input type="date" id="pren-checkout" name="checkout">
  </p>

  <p>
    <label for="pren-ospiti">Ospiti:</label>
    <input type="number" id="pren-ospiti" name="guests" min="1" max="6" step="1" value="1">
  </p>

  <p>
    <label for="pren-budget">Budget (€):</label>
    <input type="range" id="pren-budget" name="budget" min="50" max="500" step="50" value="200">
    <output for="pren-budget">200</output>
  </p>

  <p>
    <label for="pren-citta">Città:</label>
    <input type="text" id="pren-citta" name="city" list="citta-list">
    <datalist id="citta-list">
      <option value="Milano">
      <option value="Roma">
      <option value="Firenze">
      <option value="Venezia">
    </datalist>
  </p>

  <input type="hidden" name="offerta_id" value="SUMMER2026">

  <button type="submit">Prenota</button>
</form>

Riepilogo

Elemento / AttributoScopo
<select> + <option>Menu a tendina (dropdown)
value su optionValore inviato al server (diverso dal testo visibile)
selectedOpzione predefinita
disabledOpzione non selezionabile
<optgroup label="...">Raggruppa opzioni sotto un titolo
multiple + sizeSelezione multipla con lista visibile
<input type="number">Campo numerico con spinner
min, max, stepVincoli su valori numerici e date
step="any"Accetta qualsiasi decimale
<input type="range">Slider per selezione approssimativa
<output for="...">Mostra valore (si aggiorna con JS)
type="date", time, datetime-localSelettori data/ora nativi
type="search", tel, urlInput semantici (tel non valida!)
type="color"Selettore colore (#rrggbb)
type="hidden"Campo invisibile (dati tecnici)
<datalist> + listSuggerimenti autocompletamento

Validazione Client-Side

HTML integra meccanismi di validazione eseguiti direttamente dal browser (lato client), senza JavaScript. Lo scopo principale è migliorare l'esperienza utente, fornendo feedback immediato su errori di compilazione.

Analogia: il buttafuori e la security

  • Il buttafuori (validazione client-side) controlla all'ingresso: "Hai il documento? Sei maggiorenne?" Aiuta a tenere fuori chi ovviamente non dovrebbe entrare.
  • La security interna (validazione server-side) controlla di nuovo dentro, con più attenzione e strumenti migliori.
  • Servono ENTRAMBI: il buttafuori per comodità (feedback immediato), la security per sicurezza reale.

Client e Server

"Client" = il computer/browser dell'utente (chi compila il form).
"Server" = il computer remoto che riceve i dati (chi li elabora).

Campi Obbligatori: required

L'attributo required su <input>, <select> o <textarea> impedisce l'invio se il campo è vuoto.

Se l'utente prova a inviare:

  1. Il browser blocca l'invio
  2. Mostra un messaggio di errore standard
  3. Sposta il focus sul campo non valido

Best practice: accompagnare i campi required con un indicatore visivo (es. *) nella label.

<label for="nome_req">Nome: <span style="color:red;">*</span></label>
<input type="text" id="nome_req" name="username" required>

Risposta del server

Premete "Invia" per testare la validazione.

IL GRANDE AVVERTIMENTO

La validazione HTML (client-side) aiuta l'utente ma NON È SICUREZZA!

Può essere facilmente aggirata da chiunque.

Principio chiave: "Mai fidarsi ciecamente dell'input dell'utente!"

La validazione sul server (server-side) è SEMPRE necessaria! Il server che riceve i dati DEVE SEMPRE ri-validare ogni singolo dato. È l'unica vera garanzia di sicurezza e integrità.

Pensate alla validazione HTML come al buttafuori che controlla l'età all'ingresso di un locale. Utile, ma un documento falso può ingannarlo. La security interna (il server) deve ricontrollare.

Limiti e Formato

Oltre a required, altri attributi di validazione client-side:

Lunghezza testo:

  • minlength: minimo caratteri richiesti
  • maxlength: massimo caratteri consentiti (spesso blocca la digitazione)

Valori numerici/date (già visti): min, max, step

Formato specifico con pattern:

  • Accetta una Regular Expression (regex): un mini-linguaggio per descrivere schemi di testo
  • Esempio CAP: pattern="[0-9]{5}" = "5 cifre numeriche"
  • Usare title per spiegare il formato richiesto (appare nel messaggio di errore)
  • Non serve impararle nel dettaglio ora, si trovano pronte online
<input type="password" name="pwd" required minlength="8">
<input type="text" name="zip" required pattern="[0-9]{5}"
       title="Il CAP deve contenere 5 cifre.">

Risposta del server

Premete "Invia" per testare la validazione.

Provate voi!

Aggiungete Validazione

Aprite CodePen e aggiungete attributi di validazione a un form esistente.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <p>
    <label for="nome_v">Nome: *</label>
    <input type="text" id="nome_v" name="nome">
    <!-- Aggiungete: required -->
  </p>
  <p>
    <label for="email_v">Email: *</label>
    <input type="email" id="email_v" name="email">
    <!-- Aggiungete: required -->
  </p>
  <p>
    <label for="pwd_v">Password (min 8 caratteri): *</label>
    <input type="password" id="pwd_v" name="password">
    <!-- Aggiungete: required, minlength="8" -->
  </p>
  <p>
    <label for="cap_v">CAP (5 cifre):</label>
    <input type="text" id="cap_v" name="cap">
    <!-- Aggiungete: pattern="[0-9]{5}" title="Inserite 5 cifre" -->
  </p>

  <button type="submit">Registrati</button>
</form>

Cosa esplorare

  • Aggiungete required ai primi 3 campi. Provate a inviare vuoto: cosa succede?
  • Aggiungete minlength="8" alla password. Scrivete "abc" e inviate: errore?
  • Aggiungete pattern="[0-9]{5}" e title al CAP. Scrivete "abc" e inviate
  • Provate a inviare un'email senza @: il tipo email la blocca?
  • Compilate tutto correttamente e inviate: vedete i dati nella risposta?
Esercizio

Form di Login Validato

Obiettivo

Create un semplice form di login con validazione HTML.

I campi con * sono obbligatori.

Indicazioni

  1. Il form deve inviare dati in POST all'endpoint di test, aprendosi in una nuova scheda
  2. L'utente deve inserire un'email: quale tipo di input la valida automaticamente?
  3. La password deve avere almeno 8 caratteri: quale attributo impone un minimo?
  4. Entrambi i campi sono obbligatori: come lo indicate sia all'utente che al browser?
  5. Una checkbox permette di "ricordare" l'accesso: pensate a name e value per il server
  6. Non dimenticate un modo per inviare il tutto!

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <!-- Aggiungete: campo email con label, required -->

  <!-- Aggiungete: campo password con label, required, minlength="8" -->

  <!-- Aggiungete: checkbox "Ricordami" con label -->

  <!-- Aggiungete: pulsante submit "Accedi" -->
</form>
Mostra Soluzione
<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <p>
    <label for="login-email">Email: *</label>
    <input type="email" id="login-email" name="email" required
           placeholder="mario@example.com">
  </p>
  <p>
    <label for="login-pwd">Password (min 8 caratteri): *</label>
    <input type="password" id="login-pwd" name="password" required minlength="8">
  </p>
  <p>
    <input type="checkbox" id="login-remember" name="remember" value="yes">
    <label for="login-remember">Ricordami</label>
  </p>

  <button type="submit">Accedi</button>
</form>

Upload di File

<input type="file"> renderizza un pulsante "Sfoglia" per caricare file.

accept suggerisce quali tipi di file mostrare nella finestra di selezione:

  • Estensioni: .jpg, .png
  • Tipo MIME generico: image/* (qualsiasi immagine). I Tipi MIME sono etichette standard che identificano il tipo di un file su Internet.
  • Tipo MIME specifico: application/pdf
  • NON è validazione sicura: il server deve sempre verificare

Altri attributi:

  • multiple: selezionare più file contemporaneamente
  • required: upload obbligatorio
  • capture: su mobile, suggerisce fotocamera (user per selfie, environment per posteriore)
<input type="file" name="photo" accept=".jpg,.png,image/*">
<input type="file" name="docs[]" accept="application/pdf" multiple required>

La Regola d'Oro dell'Upload

Per l'upload di file servono obbligatoriamente due attributi sul <form>:

  1. method="POST": i file sono dati binari, non possono viaggiare nell'URL
  2. enctype="multipart/form-data": dice al browser di usare un "pacco speciale" per spedire file e testo insieme. Il default (application/x-www-form-urlencoded) non funziona per i file.

Analogia: spedire una lettera con una foto allegata richiede un pacco con scomparti separati — uno per la lettera (testo), uno per la foto (file).

Senza method="POST" e enctype="multipart/form-data", l'upload fallirà.

<!-- CORRETTO -->
<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="documento">
  <button type="submit">Carica</button>
</form>

<!-- SBAGLIATO: manca enctype! -->
<form action="/upload" method="POST">
  <input type="file" name="documento">
  <button type="submit">Carica (non funzionerà)</button>
</form>

✅ Con enctype

Risposta del server

Premete "Carica" per vedere la risposta.

❌ Senza enctype

Risposta del server

Premete "Carica" per vedere la risposta.

Provate a caricare un file con entrambi i form: quello con enctype mostrerà i dettagli del file nella risposta, l'altro no.

Provate voi!

Costruite un Form con Upload

Aprite CodePen e create un form con upload di file.

Codice di partenza

<!-- Notate: method="POST" e enctype="multipart/form-data" -->
<form action="https://guida-form.pages.dev/api/echo"
      method="POST"
      enctype="multipart/form-data"
      target="_blank">
  <p>
    <label for="nome_up">Nome:</label>
    <input type="text" id="nome_up" name="nome" required>
  </p>
  <p>
    <label for="foto">Foto profilo (JPG/PNG):</label>
    <input type="file" id="foto" name="avatar" accept=".jpg,.png">
  </p>

  <button type="submit">Carica</button>
</form>

Cosa esplorare

  • Inviate con un file selezionato: la risposta mostra i dettagli del file?
  • Provate a rimuovere enctype="multipart/form-data" dal form e inviate di nuovo: il file arriva ancora?
  • Aggiungete multiple all'input file: potete selezionare più file?
  • Aggiungete required all'input file: potete inviare senza file?
  • Cambiate accept in accept="application/pdf": cosa cambia nella finestra di selezione?

Tipi di Pulsanti

Tre tipi di pulsante (attributo type):

  • submit (default dentro un form): invia il form e attiva la validazione
  • reset: ripristina tutti i campi ai valori iniziali. Quasi sempre sconsigliato: un click accidentale cancella tutto senza conferma e senza possibilità di annullare. Accettabile solo in form brevi dove ricompilare costa poco (es. filtri di ricerca, calcolatori)
  • button: neutro, non fa nulla di default. Serve per azioni JavaScript personalizzate

Nota: preferite <button> a <input type="submit"> — può contenere HTML ricco (icone, testo formattato), mentre <input> accetta solo testo semplice via value.

<form action="/api/echo" method="POST">
  <label for="nome">Nome:</label>
  <input type="text" id="nome" name="nome">

  <button type="submit">Invia (submit)</button>
  <button type="button" onclick="alert('Non invio nulla!')">Azione Custom (button)</button>
  <button type="reset">Reset</button>
</form>

Risposta del server

Premete "Invia" per vedere cosa riceve il server.

Notate: il pulsante Reset cancella il campo, il pulsante Button mostra un alert senza inviare, il pulsante Submit invia i dati. Ricordate: un <button> dentro un form senza type esplicito è submit di default — specificate sempre type="button" per azioni non-submit!

Accessibilità dei Form

L'uso corretto degli elementi HTML semantici (<form>, <label>, <input>, <button>, <fieldset>, <legend>) è la fondazione dell'accessibilità.

Benefici automatici del browser (senza codice aggiuntivo):

  • Struttura comprensibile: screen reader interpreta correttamente gli elementi
  • Navigazione da tastiera: Tab / Shift+Tab per muoversi tra controlli
  • Focus visibile: il browser evidenzia l'elemento attivo (outline)

Evitare "finti controlli" con <div>/<span> + JavaScript: richiedono di reimplementare manualmente tutta l'accessibilità, rendendo il codice complesso e fragile.

<!-- BUONO: semantico e accessibile -->
<form>
  <label for="nome">Nome:</label>
  <input type="text" id="nome" name="nome">

  <label for="email">Email:</label>
  <input type="email" id="email" name="email">

  <button type="submit">Invia</button>
</form>

<!-- CATTIVO: finti controlli con div -->
<div class="fake-form">
  <span>Nome:</span>
  <div class="fake-input">Scrivi qui...</div>

  <span>Email:</span>
  <div class="fake-input">Scrivi qui...</div>

  <div class="fake-button" onclick="...">Invia</div>
</div>

Provate a navigare entrambi i form usando SOLO il tasto Tab. Nel primo il focus si sposta tra i campi; nel secondo... non succede nulla.

✅ Elementi semantici

❌ Finti controlli con <div>

Nome:

Scrivi qui...

Email:

Scrivi qui...

Invia

Indicatori e Controllo Validazione

Indicare campi obbligatori in modo accessibile:

  • Includere * dentro la <label> (non fuori)
  • Aggiungere una legenda all'inizio del form: "I campi con * sono obbligatori"
  • Opzionalmente, usare <span aria-label="obbligatorio">*</span>

Disabilitare la validazione automatica:

  • novalidate sul <form>: disabilita per tutto il form (utile se la validazione è gestita da JS)
  • formnovalidate su un <button type="submit">: disabilita solo per quel pulsante (utile per "Salva Bozza")
<form novalidate> <!-- Nessun messaggio automatico -->
  <input type="email" required>
  <button type="submit">Invia (validazione JS)</button>
  <button type="submit" formnovalidate>Salva Bozza</button>
</form>

Nota: questi attributi disabilitano solo il feedback automatico del browser, non la possibilità di controllare la validità via CSS o JS.

Provate voi!

Migliorate l'Accessibilità

Aprite CodePen e provate la navigazione da tastiera e il controllo della validazione.

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo" method="POST" target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <fieldset>
    <legend>Contatto</legend>
    <p>
      <label for="nome_a">Nome: *</label>
      <input type="text" id="nome_a" name="nome" required>
    </p>
    <p>
      <label for="email_a">Email: *</label>
      <input type="email" id="email_a" name="email" required>
    </p>
    <p>
      <label for="msg_a">Messaggio:</label><br>
      <textarea id="msg_a" name="messaggio" rows="3"></textarea>
    </p>
  </fieldset>

  <button type="submit">Invia</button>
  <button type="submit" formnovalidate>Salva Bozza</button>
</form>

Cosa esplorare

  • Navigate il form usando solo Tab: il focus si sposta correttamente tra i campi?
  • Cliccate sul testo "Nome:": il cursore va nel campo input?
  • Premete "Invia" con i campi required vuoti: vedete l'errore?
  • Premete "Salva Bozza" con i campi vuoti: il form si invia lo stesso? (Grazie a formnovalidate)
  • Provate a rimuovere gli id dai campi e i for dalle label: il click sulla label funziona ancora?
Esercizio

Modulo di Contatto Completo

Obiettivo

Create un form professionale che combina validazione, file upload, e accessibilità.

I campi con * sono obbligatori.

Dati Personali

Il Vostro Messaggio


Indicazioni

  1. Il form usa POST con upload di file: ricordate la "regola d'oro"?
  2. Due sezioni raggruppano logicamente i campi: quale elemento HTML serve per raggruppare?
  3. I campi obbligatori devono essere indicati sia visivamente (*) che nel codice: quale attributo?
  4. L'oggetto ha un limite di 100 caratteri, il messaggio un minimo di 20: quali attributi?
  5. L'allegato accetta solo certi formati: come lo suggerite al browser?
  6. La privacy richiede un tipo di input a scelta binaria (accetto/non accetto)
  7. "Salva Bozza" deve permettere l'invio anche senza compilare i campi obbligatori
  8. Tutte le label devono essere collegate ai rispettivi campi!

Codice di partenza

<form action="https://guida-form.pages.dev/api/echo"
      method="POST"
      enctype="multipart/form-data"
      target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <fieldset>
    <legend>Dati Personali</legend>
    <!-- Nome (text, required), Email (email, required), Telefono (tel) -->
  </fieldset>

  <fieldset>
    <legend>Il Vostro Messaggio</legend>
    <!-- Oggetto (text, required, maxlength), Messaggio (textarea, required, minlength), Allegato (file) -->
  </fieldset>

  <!-- Checkbox privacy (required) -->

  <!-- Due pulsanti: Invia e Salva Bozza (formnovalidate) -->
</form>
Mostra Soluzione
<form action="https://guida-form.pages.dev/api/echo"
      method="POST"
      enctype="multipart/form-data"
      target="_blank">
  <p>I campi con * sono obbligatori.</p>

  <fieldset>
    <legend>Dati Personali</legend>
    <p>
      <label for="cont-nome">Nome: *</label>
      <input type="text" id="cont-nome" name="nome" required placeholder="Mario Rossi">
    </p>
    <p>
      <label for="cont-email">Email: *</label>
      <input type="email" id="cont-email" name="email" required placeholder="mario@example.com">
    </p>
    <p>
      <label for="cont-tel">Telefono:</label>
      <input type="tel" id="cont-tel" name="telefono" placeholder="+39 123 456 7890">
    </p>
  </fieldset>

  <fieldset>
    <legend>Il Vostro Messaggio</legend>
    <p>
      <label for="cont-oggetto">Oggetto: *</label>
      <input type="text" id="cont-oggetto" name="oggetto" required maxlength="100"
             placeholder="Oggetto del messaggio">
    </p>
    <p>
      <label for="cont-msg">Messaggio: *</label><br>
      <textarea id="cont-msg" name="messaggio" required minlength="20" rows="5"
                placeholder="Scrivete almeno 20 caratteri..."></textarea>
    </p>
    <p>
      <label for="cont-file">Allegato (PDF, JPG, PNG):</label>
      <input type="file" id="cont-file" name="allegato" accept=".pdf,.jpg,.png">
    </p>
  </fieldset>

  <p>
    <input type="checkbox" id="cont-privacy" name="privacy" value="accepted" required>
    <label for="cont-privacy">Accetto la privacy policy *</label>
  </p>

  <button type="submit">Invia</button>
  <button type="submit" formnovalidate>Salva Bozza</button>
</form>

Riepilogo

Elemento / AttributoScopo
requiredCampo obbligatorio (blocca invio se vuoto)
minlength / maxlengthLimiti lunghezza testo
patternFormato specifico con regex
titleMessaggio errore personalizzato per pattern
Validazione client-sideAiuta l'utente, NON è sicurezza
Validazione server-sideSEMPRE obbligatoria
<input type="file">Upload di file
acceptSuggerisce tipi di file (non è validazione!)
multiple (su file)Selezione multipla di file
captureFotocamera su mobile
enctype="multipart/form-data"Obbligatorio per upload file
<button type="submit">Invia il form (default in form)
<button type="reset">Reset campi (evitare!)
<button type="button">Pulsante neutro (per JS)
novalidate (su form)Disabilita validazione per tutto il form
formnovalidate (su button)Disabilita validazione per quel pulsante
1 / 46