Per rendere accessibile un form multipasso in HTML5, associa ogni campo alla propria label, raggruppa i passaggi con <fieldset> e valida i controlli prima di cambiare schermata. Il punto critico è il pulsante finale: i campi obbligatori nei passaggi nascosti possono bloccare l’invio senza essere visibili. Una gestione JavaScript mirata individua il primo errore e mostra il passaggio corretto.
Sintassi rapida (TL;DR):
<fieldset data-step="0">
<legend>Dati di contatto</legend>
<label for="email">Indirizzo email</label>
<input id="email" name="email" type="email" required>
</fieldset>
<button type="button" data-next>Continua</button>
<button type="submit">Invia richiesta</button> Form multipasso HTML5: perché validare il passaggio visibile
Un wizard riduce il carico cognitivo, ma può introdurre un problema pratico: la validazione HTML nativa considera anche i campi obbligatori nei passaggi non mostrati. Se l’utente preme “Invia” prima di averli compilati, il browser può impedire l’invio senza portare il focus al controllo nascosto. Il risultato è un errore difficile da individuare, soprattutto con tastiera o screen reader.
La soluzione è validare ogni passaggio prima di avanzare e, all’invio finale, controllare tutti i campi. Se manca un dato, si rende visibile il relativo passaggio e si porta il focus sul primo controllo non valido. Gli attributi nativi come required, type="email" e minlength restano la fonte primaria delle regole: JavaScript coordina l’interfaccia, non sostituisce la validazione del browser o quella lato server.
Implementare un form multipasso con label e validazione nativa
L’esempio seguente raccoglie contatto e preferenza di callback. I passaggi inattivi usano l’attributo nativo hidden; il pulsante finale dimostra il controllo e mostra un messaggio di conferma, senza inviare dati a un server.
<form id="request-form" novalidate>
<fieldset data-step="0">
<legend>Dati di contatto</legend>
<label for="full-name">Nome completo</label>
<input id="full-name" name="fullName" autocomplete="name"
required minlength="2">
<label for="email">Indirizzo email</label>
<input id="email" name="email" type="email"
autocomplete="email" required>
<button type="button" data-next>Continua</button>
</fieldset>
<fieldset data-step="1" hidden>
<legend>Preferenze di contatto</legend>
<label for="phone">Numero di telefono</label>
<input id="phone" name="phone" type="tel"
autocomplete="tel" required>
<label for="contact-time">Orario preferito</label>
<select id="contact-time" name="contactTime" required>
<option value="">Seleziona un orario</option>
<option value="morning">Mattina</option>
<option value="afternoon">Pomeriggio</option>
</select>
<button type="button" data-back>Indietro</button>
<button type="submit">Invia richiesta</button>
</fieldset>
<p id="form-status" role="status" aria-live="polite"></p>
</form>
<script>
const form = document.querySelector("#request-form");
const steps = [...form.querySelectorAll("[data-step]")];
const status = form.querySelector("#form-status");
let currentStep = 0;
function showStep(index) {
currentStep = index;
steps.forEach((step, stepIndex) => {
step.hidden = stepIndex !== index;
});
steps[index].querySelector("legend").focus();
}
form.querySelector("[data-next]").addEventListener("click", () => {
const current = steps[currentStep];
if (!current.reportValidity()) return;
showStep(currentStep + 1);
});
form.querySelector("[data-back]").addEventListener("click", () => {
showStep(currentStep - 1);
});
form.addEventListener("submit", (event) => {
event.preventDefault();
const invalidField = [...form.elements].find((field) =>
field.willValidate && !field.checkValidity()
);
if (invalidField) {
const invalidStep = steps.findIndex((step) =>
step.contains(invalidField)
);
showStep(invalidStep);
invalidField.reportValidity();
return;
}
status.textContent = "Richiesta verificata e pronta per l'invio.";
});
</script> Risultato nel Browser e Comportamento Accessibilità / DOM
Stato iniziale:
- È visibile il passaggio "Dati di contatto".
- Il passaggio "Preferenze di contatto" ha l'attributo hidden.
Se Nome completo è vuoto e si preme Continua:
- Il browser segnala che il campo è obbligatorio.
- Il passaggio non cambia.
Se tutti i campi del primo passaggio sono validi:
- Si mostra "Preferenze di contatto".
- Il focus passa al titolo del passaggio, cioè a <legend>.
Se l'invio trova un campo non valido:
- Viene mostrato il passaggio che contiene il campo.
- Il browser presenta il messaggio di validazione nativo.
Se tutti i campi sono validi:
- Il messaggio annunciato è "Richiesta verificata e pronta per l'invio." Come funzionano focus, label e validazione nel wizard HTML
1. Label esplicite e nomi accessibili
Ogni <label> usa un attributo for che corrisponde esattamente all’id del controllo. In questo modo un clic sull’etichetta attiva il campo e le tecnologie assistive ne ricavano il nome accessibile. Gli id devono essere univoci nella pagina: duplicarli in componenti riutilizzati può collegare l’etichetta al controllo sbagliato.
2. Passaggi nascosti e gestione del focus
La funzione showStep() aggiorna hidden su ciascun fieldset e sposta il focus sulla <legend> del passaggio attivo. Questo rende percepibile il cambio di contesto anche a chi naviga senza vedere la pagina. Perché il focus programmatico funzioni, il titolo deve poter ricevere focus: nel markup dell’esempio la <legend> è focalizzata dallo script; in base ai browser supportati, si può aggiungere tabindex="-1" per renderne esplicita la focalizzabilità.
3. Validazione nativa e invio controllato
reportValidity() verifica i vincoli e chiede al browser di mostrare il messaggio associato al primo campo non valido. Sul pulsante “Continua” il controllo riguarda il passaggio corrente. L’attributo novalidate disattiva la validazione automatica al submit, così il gestore può prima rivelare il passaggio con l’errore; checkValidity() controlla i campi e willValidate esclude i controlli che non partecipano alla validazione.
Validazione per passaggio o invio HTML5: confronto dei metodi
La scelta dipende dal modo in cui il wizard gestisce gli errori e il submit. Questi approcci non sono equivalenti: la validazione automatica standard è semplice, mentre il controllo manuale consente di mostrare il passaggio corretto prima di segnalare l’errore.
| Metodo / Approccio | Sintassi / Caratteristica | Performance / Complessità | Quando sceglierlo |
|---|---|---|---|
| Validazione per passaggio | fieldset.reportValidity() | Basso costo; un controllo sul passaggio attivo | Per impedire di avanzare con campi incompleti |
| Validazione manuale finale | checkValidity() e willValidate | Scansione lineare dei controlli, O(n) | Per rivelare il passaggio del primo errore |
| Submit nativo automatico | required senza novalidate | Implementazione minima | Per form senza passaggi nascosti o gestione personalizzata |
Errori comuni nei form multipasso HTML5 e pratiche di produzione
- Campi obbligatori nascosti: la validazione automatica può bloccare il submit su un controllo non visibile. Prima di mostrare l’errore, attiva il passaggio che contiene il campo non valido.
- Usare
disabledper nascondere i campi: i controlli disabilitati non vengono inviati con il form e non partecipano alla validazione. Se i valori devono restare disponibili, preferiscihiddensul contenitore. - Affidarsi solo a JavaScript: i vincoli HTML migliorano l’esperienza, ma non proteggono il server. Ripeti la validazione lato server e tratta i dati ricevuti come input non attendibile.
- Focus senza contesto: cambiare schermata senza spostare il focus può disorientare chi usa screen reader. Porta il focus al titolo del nuovo passaggio e verifica il comportamento con tastiera.
Domande frequenti sulla validazione dei form multipasso
Perché un form non si invia se il campo obbligatorio è nascosto?
La validazione HTML può rilevare un controllo obbligatorio non valido anche quando il suo passaggio non è visibile. Il browser non sempre può spostare il focus su un elemento nascosto, rendendo l’errore poco chiaro. Individua il campo non valido, mostra il relativo passaggio e poi chiama reportValidity().
È meglio usare hidden o disabled nei passaggi inattivi?
Usa hidden se vuoi conservare i valori e includere i controlli nel controllo finale. disabled esclude invece i campi dalla validazione e dai dati inviati. La scelta deve corrispondere al comportamento richiesto, non essere solo un espediente visivo.
La validazione HTML5 sostituisce quella lato server?
No. Attributi come required e type="email" aiutano l’utente e forniscono controlli lato client, ma possono essere aggirati. Il server deve convalidare nuovamente ogni valore prima di usarlo o salvarlo.
Documentazione ufficiale su label e validazione HTML
- Documentazione ufficiale HTML5 — riferimento agli elementi e agli attributi HTML.
- MDN: validazione dei vincoli nei form — API e comportamento della validazione HTML.
- MDN: elemento label — associazione tra etichette e controlli del form.
