<div class="n7-background-02 p-6 border n7-border-gray-01">
<div class="pb-6 space-y-4 border-b n7-border-gray-01">
<p>I campi obbligatori sono contrassegnati da un asterisco
<abbr class="font-bold no-underline" title="obbligatorio">*</abbr>
</p>
</div>
<form novalidate id="hasValidationForm" class="has-js-validation-form py-6 space-y-4">
<div class="n7-input-field gap-1 ">
<label for="fieldName" class="n7-input-field__label">
Nome
<abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="n7-input">
<input id="fieldName" class="text-sm n7-input-placeholder is-name" type="text" data-label="Nome" aria-required="true" autocomplete="given-name" aria-describedBy="fieldNameError">
</div>
<span class="n7-input-field__hint">
<span class="n7-input-field__error" id="fieldNameError" aria-live="assertive">
</span>
</span>
</div>
<div class="n7-input-field gap-1 ">
<label for="fieldSurname" class="n7-input-field__label">
Cognome
<abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="n7-input">
<input id="fieldSurname" class="text-sm n7-input-placeholder is-familyname" type="text" data-label="Cognome" aria-required="true" autocomplete="family-name" aria-describedBy="fieldSurnameError">
</div>
<span class="n7-input-field__hint">
<span class="n7-input-field__error" id="fieldSurnameError" aria-live="assertive">
</span>
</span>
</div>
<div class="n7-input-field gap-1 ">
<label for="fieldEmail" class="n7-input-field__label">
Email
<abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>
</label>
<div class="n7-input">
<input id="fieldEmail" class="text-sm n7-input-placeholder is-email" type="email" data-label="Email" aria-required="true" autocomplete="email" aria-describedBy="fieldEmailError">
</div>
<span class="n7-input-field__hint">
<span class="n7-input-field__error" id="fieldEmailError" aria-live="assertive">
</span>
</span>
</div>
<div class="n7-input n7-input-field n7-input-checkbox has-js-validation-required is-privacy">
<div class="flex gap-2 items-center">
<input id="fieldPrivacy" name="fieldPrivacy" data-label="Privacy policy" class=" has-js-validation-required is-privacy" type="checkbox" aria-required="true" aria-describedBy="">
<label class="n7-input-checkbox__label" for="fieldPrivacy">
Dichiaro di aver letto la <a href="" target="_blank" class="n7-link-primary underline font-bold ">Privacy Policy<span class="sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.
<abbr class="font-bold no-underline" title="obbligatorio">*</abbr></label>
</div>
<span class="n7-input-field__hint">
<span class="n7-input-field__error" id="fieldPrivacyError" aria-live="assertive">
</span>
</span>
</div>
<div class="n7-form-feedback">
<p class="n7-form-feedback-sr sr-only" aria-live="assertive"><!-- js --></p>
<p class="n7-form-feedback-msg" aria-hidden="true"></p>
</div>
<div class="n7-form-actions">
<button type="submit" class="n7-btn
n7-btn--primary
form-submit">
Registrati
</button>
</div>
</form>
</div>
<div class="n7-background-02 p-6 border n7-border-gray-01">
<div class="pb-6 space-y-4 border-b n7-border-gray-01">
<p>I campi obbligatori sono contrassegnati da un asterisco
<abbr class="font-bold no-underline" title="obbligatorio">*</abbr></p>
</div>
<form novalidate id="hasValidationForm" class="has-js-validation-form py-6 space-y-4">
{% render '@input-field', {
label: 'Nome',
input: {
id: 'fieldName',
type: 'text',
shortLabel: 'Nome',
classes: 'is-name',
autocomplete: 'given-name',
fieldRequired: true
}
}, true %}
{% render '@input-field', {
label: 'Cognome',
input: {
id: 'fieldSurname',
type: 'text',
shortLabel: 'Cognome',
classes: 'is-familyname',
autocomplete: 'family-name',
fieldRequired: true
}
}, true %}
{% render '@input-field', {
label: 'Email',
input: {
id: 'fieldEmail',
type: 'email',
shortLabel: 'Email',
classes: 'is-email',
autocomplete: 'email',
fieldRequired: true
}
}, true %}
{% render '@input-checkbox', {
label: 'Dichiaro di aver letto la <a href="" target="_blank" class="n7-link-primary underline font-bold ">Privacy Policy<span class="sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.',
id: 'fieldPrivacy',
shortLabel: 'Privacy policy',
classes: 'has-js-validation-required is-privacy',
fieldRequired: true
}, true %}
<div class="n7-form-feedback">
<p class="n7-form-feedback-sr sr-only" aria-live="assertive"><!-- js --></p>
<p class="n7-form-feedback-msg" aria-hidden="true"></p>
</div>
<div class="n7-form-actions">
{% render '@button',{ label: 'Registrati', buttonType: 'submit', classes: 'form-submit' },true %}
</div>
</form>
</div>
/* No context defined. */
/**
* FORM WITH VALIDATION
* FEEDBACK MESSAGE
*
*/
@layer components {
.n7-form-feedback-msg.is-invalid {
@apply p-3 bg-error-light n7-content-error font-bold border border-error-dark rounded-md;
}
.n7-form-feedback-msg.is-success {
@apply p-3 bg-success-light n7-content-success font-bold border border-success-dark 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(".n7-input-field");
const elLabel = elField.querySelector(".n7-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(".n7-input-field");
const elError = elField.querySelector(".n7-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(".n7-form-feedback");
const elSummaryMsg = elSummary.querySelector(".n7-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(".n7-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(".n7-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 });
});
}
});
No notes defined.