Contact form: name field letters-only validation, email field lowercase + format validation with error messages (ES/EN)

This commit is contained in:
Mauricio López Coria
2026-07-03 20:53:26 -03:00
parent dff830b386
commit ff3e57051f
2 changed files with 126 additions and 0 deletions
+63
View File
@@ -84,9 +84,13 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
required
autocomplete="name"
maxlength="100"
pattern="[A-Za-záéíóúüñÁÉÍÓÚÜÑçÇ\s]+"
inputmode="text"
aria-describedby="name-error"
class="w-full px-3.5 py-2.5 rounded-md outline-none transition-colors"
style="background: var(--hds-bg); color: var(--hds-fg); border: 1px solid var(--hds-line);"
/>
<div id="name-error" class="hidden text-xs mt-1" style="color: var(--color-hds-error);" aria-live="polite"></div>
</div>
<div>
@@ -100,9 +104,11 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
required
autocomplete="email"
maxlength="200"
aria-describedby="email-error"
class="w-full px-3.5 py-2.5 rounded-md outline-none transition-colors"
style="background: var(--hds-bg); color: var(--hds-fg); border: 1px solid var(--hds-line);"
/>
<div id="email-error" class="hidden text-xs mt-1" style="color: var(--color-hds-error);" aria-live="polite"></div>
</div>
<div>
@@ -153,6 +159,47 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
const successMsg = isEn ? 'Message sent. We will reply soon.' : 'Mensaje enviado. Te respondemos pronto.';
const errorMsg = isEn ? 'Could not send. Please try again or contact us via WhatsApp.' : 'No se pudo enviar. Probá de nuevo o contactanos por WhatsApp.';
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const nameError = document.getElementById('name-error');
const emailError = document.getElementById('email-error');
const nameRegex = /[^a-záéíóúüñç\s]/gi;
const emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/;
const nameErrMsg = isEn ? 'Letters and spaces only' : 'Solo letras y espacios';
const emailErrMsg = isEn ? 'Invalid email address' : 'Dirección de correo no válida';
function showFieldError(input, errorEl, msg) {
errorEl.textContent = msg;
errorEl.classList.remove('hidden');
input.style.borderColor = 'var(--color-hds-error)';
}
function clearFieldError(input, errorEl) {
errorEl.classList.add('hidden');
errorEl.textContent = '';
input.style.borderColor = '';
}
nameInput.addEventListener('beforeinput', (e) => {
if (e.data && nameRegex.test(e.data)) {
e.preventDefault();
}
});
nameInput.addEventListener('input', () => {
const clean = nameInput.value.replace(nameRegex, '');
if (clean !== nameInput.value) {
nameInput.value = clean;
}
});
emailInput.addEventListener('input', () => {
emailInput.value = emailInput.value.toLowerCase();
});
[nameInput, emailInput].forEach(input => {
input.addEventListener('focus', () => {
const err = document.getElementById(input.id + '-error');
if (err) clearFieldError(input, err);
});
});
window.addEventListener('load', () => {
if (window.grecaptcha && window.grecaptcha.ready) {
window.grecaptcha.ready();
@@ -169,6 +216,22 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
status.textContent = '';
status.style.color = 'var(--hds-fg-soft)';
let hasError = false;
if (nameInput.value.trim() && nameRegex.test(nameInput.value)) {
showFieldError(nameInput, nameError, nameErrMsg);
hasError = true;
}
const emailVal = emailInput.value.trim();
if (emailVal && !emailRegex.test(emailVal)) {
showFieldError(emailInput, emailError, emailErrMsg);
hasError = true;
}
if (hasError) {
label.textContent = originalLabel;
submit.disabled = false;
return;
}
try {
let token = '';
if (window.grecaptcha && window.grecaptcha.execute) {
+63
View File
@@ -84,9 +84,13 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
required
autocomplete="name"
maxlength="100"
pattern="[A-Za-záéíóúüñÁÉÍÓÚÜÑçÇ\s]+"
inputmode="text"
aria-describedby="name-error"
class="w-full px-3.5 py-2.5 rounded-md outline-none transition-colors"
style="background: var(--hds-bg); color: var(--hds-fg); border: 1px solid var(--hds-line);"
/>
<div id="name-error" class="hidden text-xs mt-1" style="color: var(--color-hds-error);" aria-live="polite"></div>
</div>
<div>
@@ -100,9 +104,11 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
required
autocomplete="email"
maxlength="200"
aria-describedby="email-error"
class="w-full px-3.5 py-2.5 rounded-md outline-none transition-colors"
style="background: var(--hds-bg); color: var(--hds-fg); border: 1px solid var(--hds-line);"
/>
<div id="email-error" class="hidden text-xs mt-1" style="color: var(--color-hds-error);" aria-live="polite"></div>
</div>
<div>
@@ -153,6 +159,47 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
const successMsg = isEn ? 'Message sent. We will reply soon.' : 'Mensaje enviado. Te respondemos pronto.';
const errorMsg = isEn ? 'Could not send. Please try again or contact us via WhatsApp.' : 'No se pudo enviar. Probá de nuevo o contactanos por WhatsApp.';
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const nameError = document.getElementById('name-error');
const emailError = document.getElementById('email-error');
const nameRegex = /[^a-záéíóúüñç\s]/gi;
const emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/;
const nameErrMsg = isEn ? 'Letters and spaces only' : 'Solo letras y espacios';
const emailErrMsg = isEn ? 'Invalid email address' : 'Dirección de correo no válida';
function showFieldError(input, errorEl, msg) {
errorEl.textContent = msg;
errorEl.classList.remove('hidden');
input.style.borderColor = 'var(--color-hds-error)';
}
function clearFieldError(input, errorEl) {
errorEl.classList.add('hidden');
errorEl.textContent = '';
input.style.borderColor = '';
}
nameInput.addEventListener('beforeinput', (e) => {
if (e.data && nameRegex.test(e.data)) {
e.preventDefault();
}
});
nameInput.addEventListener('input', () => {
const clean = nameInput.value.replace(nameRegex, '');
if (clean !== nameInput.value) {
nameInput.value = clean;
}
});
emailInput.addEventListener('input', () => {
emailInput.value = emailInput.value.toLowerCase();
});
[nameInput, emailInput].forEach(input => {
input.addEventListener('focus', () => {
const err = document.getElementById(input.id + '-error');
if (err) clearFieldError(input, err);
});
});
window.addEventListener('load', () => {
if (window.grecaptcha && window.grecaptcha.ready) {
window.grecaptcha.ready();
@@ -169,6 +216,22 @@ const reCAPTCHA_site_key = '6LeOxaEaAAAAAPD8rQQLqw4woxn1dMjvF_Ooy0BG';
status.textContent = '';
status.style.color = 'var(--hds-fg-soft)';
let hasError = false;
if (nameInput.value.trim() && nameRegex.test(nameInput.value)) {
showFieldError(nameInput, nameError, nameErrMsg);
hasError = true;
}
const emailVal = emailInput.value.trim();
if (emailVal && !emailRegex.test(emailVal)) {
showFieldError(emailInput, emailError, emailErrMsg);
hasError = true;
}
if (hasError) {
label.textContent = originalLabel;
submit.disabled = false;
return;
}
try {
let token = '';
if (window.grecaptcha && window.grecaptcha.execute) {