<div class="rtds-background-02 rtds-p-6 rtds-border rtds-border-gray-01">
<div class="rtds-pb-6 rtds-space-y-4 rtds-border-b rtds-border-gray-01">
<p>I campi obbligatori sono contrassegnati da un asterisco
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>
</p>
</div>
<form novalidate id="hasValidationForm" class="has-js-validation-form rtds-py-6 rtds-space-y-4">
<div class="rtds-flex rtds-gap-4">
<div class="rtds-w-1/2">
<div class="rtds-input-field rtds-gap-1 ">
<label for="fieldName" class="rtds-input-field__label">
Nome
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="rtds-input">
<input id="fieldName" class="rtds-text-sm rtds-input-placeholder is-name" type="text" data-label="Nome" aria-required="true" autocomplete="given-name" aria-describedBy="fieldNameError">
</div>
<span class="rtds-input-field__hint">
<span class="rtds-input-field__error" id="fieldNameError" aria-live="assertive">
</span>
</span>
</div>
</div>
<div class="rtds-w-1/2">
<div class="rtds-input-field rtds-gap-1 ">
<label for="fieldDocumentType" class="rtds-input-field__label">
Tipo di documento
</label>
<div class="rtds-input">
<input id="fieldDocumentType" class="rtds-text-sm rtds-input-placeholder is-documentType" type="text" data-label="Tipo di documento">
</div>
</div>
</div>
</div>
<div>
<div class="rtds-input-field rtds-gap-1 ">
<label for="fieldSurname" class="rtds-input-field__label">
Cognome
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="rtds-input">
<input id="fieldSurname" class="rtds-text-sm rtds-input-placeholder is-familyname" type="text" data-label="Cognome" aria-required="true" autocomplete="family-name" aria-describedBy="fieldSurnameError">
</div>
<span class="rtds-input-field__hint">
<span class="rtds-input-field__error" id="fieldSurnameError" aria-live="assertive">
</span>
</span>
</div>
<div class="rtds-input-field rtds-gap-1 ">
<label for="fieldEmail" class="rtds-input-field__label">
Email
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="rtds-input">
<input id="fieldEmail" class="rtds-text-sm rtds-input-placeholder is-email" type="email" data-label="Email" aria-required="true" autocomplete="email" aria-describedBy="fieldEmailError">
</div>
<span class="rtds-input-field__hint">
<span class="rtds-input-field__error" id="fieldEmailError" aria-live="assertive">
</span>
</span>
</div>
<div class="rtds-input rtds-input-field rtds-input-checkbox has-js-validation-required is-privacy">
<div class="rtds-flex rtds-gap-2 rtds-items-center">
<input id="fieldPrivacy" name="fieldPrivacy" data-label="Privacy policy" class=" " type="checkbox" aria-required="true" aria-describedBy="fieldPrivacyError">
<label class="rtds-input-checkbox__label" for="fieldPrivacy">
Dichiaro di aver letto la <a href="" target="_blank" class="rtds-action-link-primary rtds-underline rtds-font-bold">Privacy Policy<span class="rtds-sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr></label>
</div>
<span class="rtds-input-field__hint">
<span class="rtds-input-field__error" id="fieldPrivacyError" aria-live="assertive">
</span>
</span>
</div>
</div>
<div class="rtds-form-feedback">
<p class="rtds-form-feedback-sr rtds-sr-only" aria-live="assertive"><!-- js --></p>
<p class="rtds-form-feedback-msg" aria-hidden="true"></p>
</div>
<div class="rtds-form-actions">
<button type="submit" class="rtds-btn
rtds-btn--primary
rtds-form-submit">
Registrati
</button>
</div>
</form>
</div>
<div class="rtds-background-02 rtds-p-6 rtds-border rtds-border-gray-01">
<div class="rtds-pb-6 rtds-space-y-4 rtds-border-b rtds-border-gray-01">
<p>I campi obbligatori sono contrassegnati da un asterisco
<abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr></p>
</div>
<form novalidate id="hasValidationForm" class="has-js-validation-form rtds-py-6 rtds-space-y-4">
<div class="rtds-flex rtds-gap-4">
<div class="rtds-w-1/2">
{% render '@input-field', { label: 'Nome', inputShortLabel: 'Nome',inputClasses:'is-name', inputId: 'fieldName', inputType: 'text', inputAutocomplete: 'given-name', inputRequired: 'true' }, true %}
</div>
<div class="rtds-w-1/2">
{% render '@input-field', { label: 'Tipo di documento', inputShortLabel: 'Tipo di documento', inputClasses:'is-documentType', inputId: 'fieldDocumentType' }, true %}
</div>
</div>
<div>
{% render '@input-field', { label: 'Cognome', inputShortLabel: 'Cognome',inputClasses:'is-familyname', inputId: 'fieldSurname', inputType: 'text', inputAutocomplete: 'family-name', inputRequired: 'true' }, true %}
{% render '@input-field', { label: 'Email', inputShortLabel: 'Email', inputClasses:'is-email', inputId: 'fieldEmail', inputType: 'email', inputAutocomplete: 'email', inputRequired: 'true' }, true %}
{% render '@input-checkbox', { label: 'Dichiaro di aver letto la <a href="" target="_blank" class="rtds-action-link-primary rtds-underline rtds-font-bold">Privacy Policy<span class="rtds-sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.', shortLabel: 'Privacy policy', classes:'has-js-validation-required is-privacy', id: 'fieldPrivacy', fieldRequired: 'true' }, true %}
</div>
<div class="rtds-form-feedback">
<p class="rtds-form-feedback-sr rtds-sr-only" aria-live="assertive"><!-- js --></p>
<p class="rtds-form-feedback-msg" aria-hidden="true"></p>
</div>
<div class="rtds-form-actions">
{% render '@button', { label: 'Registrati', buttonType: 'submit', classes: 'rtds-form-submit' }, true %}
</div>
</form>
</div>
/* No context defined. */
/**
* FORM WITH VALIDATION
* FEEDBACK MESSAGE
*
*/
@layer components {
.rtds-form-feedback-msg.is-invalid {
@apply rtds-p-3 rtds-bg-error-light rtds-content-error rtds-font-bold rtds-border rtds-border-error-dark rtds-rounded-md;
}
.rtds-form-feedback-msg.is-success {
@apply rtds-p-3 rtds-bg-success-light rtds-content-success rtds-font-bold rtds-border rtds-border-success-dark rtds-rounded-md;
}
}
document.addEventListener("DOMContentLoaded", function () {
///* Regex per verifica email */
const emailRegex = /\S+@\S+\.\S+/; // has @ and .
///* Form */
const elForm = document.getElementById("hasValidationForm");
if (elForm) {
///* Campi form */
const elRequiredName = document.getElementsByClassName("is-name")[0];
const elRequiredFamilyname = document.getElementsByClassName("is-familyname")[0];
const elPrivacy = document.getElementsByClassName("is-privacy")[0];
const elEmail = document.getElementsByClassName("is-email")[0];
///* Errori form da verificare */
const formErrors = {
name: false,
familyname: false,
email: false,
privacy: false
};
let hasSubmitted = false;
validateField({
elField: elEmail,
validateFn: validateFieldEmail
});
validateField({
elField: elRequiredName,
validateFn: validateFieldRequired,
errorKey: 'name'
});
validateField({
elField: elRequiredFamilyname,
validateFn: validateFieldRequired,
errorKey: 'familyname'
});
validateField({
elField: elPrivacy,
validateFn: validateFieldPrivacy
});
// Gestione validazione su 3 eventi: change, blur, keyup
function validateField({ elField, validateFn, errorKey }) {
let touched = false;
elField.addEventListener("change", (e) => {
touched = true; // mark it as touched so that on blur it shows the error.
validateFn(e.target, { live: true, errorKey });
if (hasSubmitted) {
updateSubmitSummary();
}
});
elField.addEventListener("keyup", (e) => {
// remove any error on keyup if existent
validateFn(e.target, { removeOnly: true, errorKey });
if (hasSubmitted) {
updateSubmitSummary();
}
});
elField.addEventListener("blur", (e) => {
if (!touched) return;
// show error if touched
validateFn(e.target, { live: true, errorKey });
});
}
// Controllo email
function validateFieldEmail(el, opts) {
const isEmpty = el.value === "";
updateFieldDOM(el, !isEmpty, "Email obbligatoria.", opts);
if (isEmpty) {
formErrors.email = true;
} else {
const isEmailValid = el.value.match(emailRegex);
updateFieldDOM(el, isEmailValid, "Email non valida.", opts);
formErrors.email = !isEmailValid;
}
}
// Validazione campi obbligatori
function validateFieldRequired(el, opts) {
const isEmpty = el.value === "";
const errorKey = opts?.errorKey;
const elField = el.closest(".rtds-input-field");
const elLabel = elField.querySelector(".rtds-input-field__label-text");
const fieldLabel = elLabel ? elLabel.innerText : "Field";
updateFieldDOM(el, !isEmpty, `${fieldLabel} obbligatorio.`, opts);
if (errorKey) {
formErrors[errorKey] = isEmpty;
}
}
// Validazione campo privacy (checkbox)
function validateFieldPrivacy(el, opts) {
const isNotChecked = el.checked === false;
updateFieldDOM(el, !isNotChecked, "Devi dichiarare di aver letto la Privacy Policy.", opts);
formErrors.privacy = isNotChecked;
}
// Aggiornamento errore nel campo e gestione attributi accessibilità
function updateFieldDOM(el, isValid, errorMessage, opts) {
const removeOnly = opts?.removeOnly;
const isLive = opts?.live;
const elField = el.closest(".rtds-input-field");
const elError = elField.querySelector(".rtds-input-field__error");
if (isValid) {
elField.classList.remove("is-invalid");
elError.innerText = ""; // It's valid
el.removeAttribute("aria-invalid");
} else if (!removeOnly) {
elField.classList.add("is-invalid");
el.setAttribute("aria-invalid", "true");
elError.setAttribute("aria-live", isLive ? "assertive" : "off");
elError.innerText = errorMessage;
}
}
// Aggiornamento feedback form a invio
function updateSubmitSummary({ isSubmit } = {}) {
const elSummary = elForm.querySelector(".rtds-form-feedback");
const elSummaryMsg = elSummary.querySelector(".rtds-form-feedback-msg");
// Clear form feedback
elSummaryMsg.classList.remove("is-invalid");
elSummaryMsg.classList.remove("is-success");
elSummaryMsg.innerText = "";
const errorsState = Object.entries(formErrors);
const invalidFields = errorsState.filter(([key, value]) => value === true).map(([key]) => {
const elField = elForm.querySelector(`.is-${key}`);
return elField ? elField.getAttribute('data-label') : key;
});
if (invalidFields.length > 0) {
// Show error msg
const errorCount = invalidFields.length;
const errorMsg =
errorCount === 1
? `È presente ${errorCount} campo non valido: ${invalidFields.join(', ')}.`
: `Sono presenti ${errorCount} campi non validi: ${invalidFields.join(', ')}.`;
elSummaryMsg.classList.add("is-invalid");
elSummaryMsg.innerText = errorMsg;
elSummary.querySelector(".rtds-form-feedback-sr").innerText = isSubmit
? // Set SR error message only on submit to avoid being re-announced
// every time the error summary visually changes.
errorMsg
: "";
} else if (isSubmit) {
const successMsg = "Form inviata con successo.";
elSummary.querySelector(".rtds-form-feedback-sr").innerText = successMsg;
elSummaryMsg.innerText = successMsg;
elSummaryMsg.classList.add("is-success");
}
}
elForm.addEventListener("submit", (e) => {
e.preventDefault();
hasSubmitted = true;
// Validate again
validateFieldEmail(elEmail);
validateFieldRequired(elRequiredName, { errorKey: 'name' });
validateFieldRequired(elRequiredFamilyname, { errorKey: 'familyname' });
validateFieldPrivacy(elPrivacy);
updateSubmitSummary({ isSubmit: true });
});
}
});
Esempio di form con validazione client-side accessibile. Questo file ha uno scopo indicativo e necessita di customizzazioni per il suo utilizzo.
Il form include:
La validazione avviene su tre eventi:
change: quando l’utente modifica il valoreblur: quando l’utente lascia il campokeyup: per rimuovere gli errori durante la digitazioneIl componente implementa le seguenti caratteristiche di accessibilità:
aria-invalid, aria-live)Per utilizzare il componente: