Por que a validação avançada de formulários importa

A validação de formulários vai muito além dos campos simples e . Enquanto os atributos HTML5 fornecem uma linha de base sólida, eles muitas vezes ficam aquém quando você precisa aplicar regras complexas de negócios, fornecer feedback em tempo real ou criar uma experiência de usuário suave em diferentes navegadores. A validação baseada em JavaScript lhe dá controle total sobre quando e como a validação é executada, permitindo que você implemente regras personalizadas como medidores de força de senha, correspondência de padrões para nomes de usuários, lógica de campo dependente e verificações assíncronas, como se um nome de usuário já está sendo tomado.

A validação do lado do cliente também reduz a carga do servidor e acelera o loop de feedback para os usuários. Em vez de enviar um formulário e esperar por uma resposta do servidor, erros são capturados imediatamente. No entanto, é fundamental lembrar que a validação do lado do cliente é uma conveniência , não uma medida de segurança. Usuários maliciosos podem ignorar qualquer verificação JavaScript, então sempre emparelhe validação do lado do cliente com validação robusta do lado do servidor.

Neste guia, vamos percorrer a construção de um sistema de validação avançado completo usando o JavaScript baunilha. Você vai aprender como definir regras personalizadas, exibir mensagens de erro dinâmicas, validar vários campos juntos e manter seu código mantendível. Cada seção inclui trechos de código prontos para produção que você pode adaptar imediatamente.

Compreendendo os limites da validação HTML5

Atributos HTML5 como , , e são ótimos para verificações simples, mas eles não têm flexibilidade. Por exemplo:

  • Sem validação de campo cruzado – Você não pode confirmar que uma senha e um campo de confirmação correspondem usando HTML5 sozinho.
  • Mensagens de erro limitadas – As dicas incorporadas do navegador são inconsistentes entre navegadores e não podem ser totalmente desenhadas.
  • Nenhuma atualização em tempo real – A validação HTML5 normalmente dispara apenas no envio de formulários, não em cada tecla.
  • Validação de Constraint API – Embora poderoso, ele ainda depende das regras integradas do navegador e não tem a capacidade de definir lógica personalizada complexa facilmente.

O JavaScript preenche estas lacunas. Com ouvintes de eventos e funções personalizadas, você pode criar validação que seja expressiva e amigável.

Configurar a Estrutura de Formulário HTML

Comece com um formulário semântico limpo. Aqui está um exemplo que inclui campos para nome de usuário, e-mail, senha e senha de confirmação. Nós adicionamos atributos para acessibilidade e elementos vazios que conterão mensagens de erro.

<form id="registrationForm" novalidate>
 <div class="form-group">
 <label for="username">Username</label>
 <input type="text" id="username" name="username" placeholder="e.g. johndoe" required>
 <span id="usernameError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="email">Email address</label>
 <input type="email" id="email" name="email" placeholder="[email protected]" required>
 <span id="emailError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="password">Password</label>
 <input type="password" id="password" name="password" placeholder="At least 8 characters" required>
 <span id="passwordError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="confirmPassword">Confirm Password</label>
 <input type="password" id="confirmPassword" name="confirmPassword" placeholder="Repeat password" required>
 <span id="confirmPasswordError" class="error-message" role="alert"></span>
 </div>

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

Observe o atributo no formulário. Isto diz ao navegador para desligar sua própria validação incorporada para que possamos lidar com tudo com JavaScript. Nós usaremos CSS para mostrar ou ocultar mensagens de erro e adicionar feedback visual (por exemplo, bordas vermelhas) para campos inválidos.

Desenhando uma lógica de validação central

Em vez de espalhar código de validação através de manipuladores de eventos, criamos um objeto central que contém regras personalizadas. Cada regra é uma função que retorna (válido) ou (inválido) junto com uma mensagem de erro opcional. Esta abordagem torna o código fácil de estender e manter.

const validators = {
 required: (value) => value.trim() !== '' || 'This field is required.',
 email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || 'Please enter a valid email address.',
 minLength: (min) => (value) => value.length >= min || `Must be at least ${min} characters.`,
 passwordStrength: (value) => {
 const errors = [];
 if (value.length < 8) errors.push('At least 8 characters.');
 if (!/[A-Z]/.test(value)) errors.push('One uppercase letter.');
 if (!/[0-9]/.test(value)) errors.push('One number.');
 if (!/[!@#$%^&*]/.test(value)) errors.push('One special character.');
 return errors.length === 0 || errors.join(' ');
 },
 match: (otherFieldId) => (value, formData) => {
 const otherValue = formData.get(otherFieldId);
 return value === otherValue || 'Passwords do not match.';
 }
};

Repare que os validadores podem ser parametrizados – devolve uma função que espera o valor. O validador recebe o ID de outro campo e usa (um ] objeto) para comparar valores. Este padrão é altamente reutilizável.

Criando regras personalizadas para cada campo

Defina quais os validadores aplicáveis a cada campo. Este mapeamento é um objeto simples onde as chaves são nomes de campos e os valores são arrays de funções de validação.

const fieldRules = {
 username: [
 validators.required,
 validators.minLength(3),
 (value) => /^[a-zA-Z0-9_]+$/.test(value) || 'Usernames can only contain letters, numbers, and underscores.'
 ],
 email: [
 validators.required,
 validators.email
 ],
 password: [
 validators.required,
 validators.passwordStrength
 ],
 confirmPassword: [
 validators.required,
 validators.match('password')
 ]
};

Você pode facilmente adicionar uma nova regra personalizada, como uma verificação de que o nome de usuário não está já tomado (validação de sincronização), adicionando uma função async à lista. Por enquanto, manteremos todas as regras sincronizadas.

Validação em execução na submissão do formulário

Anexar um ouvinte de eventos ao formulário que previne a submissão, executa todos os validadores e coleta erros. Se não forem encontrados erros, o formulário é permitido enviar.

const form = document.getElementById('registrationForm');

form.addEventListener('submit', (event) => {
 event.preventDefault();
 const formData = new FormData(form);
 const errors = validateForm(formData);

 if (Object.keys(errors).length === 0) {
 // All valid – you can submit programmatically or send via fetch
 console.log('Form is valid. Submitting...');
 // form.submit();
 } else {
 displayErrors(errors);
 }
});

A função itera sobre cada campo, executa seus validadores e coleta mensagens de erro. Aqui está a implementação:

function validateForm(formData) {
 const errors = {};
 for (const [fieldName, rules] of Object.entries(fieldRules)) {
 const value = formData.get(fieldName) || '';
 for (const rule of rules) {
 const result = rule(value, formData);
 if (result !== true) {
 errors[fieldName] = result; // result is the error string
 break; // stop after first failure for this field
 }
 }
 }
 return errors;
}

Mostrando Feedback de Validação em Tempo Real

Os usuários se beneficiam de ver erros ao digitar, não apenas no envio. Adicione ouvintes de eventos para e em cada campo. Para evitar usuários esmagadoras, um bom padrão é validar no borrão (quando eles saem do campo) e então revalidar em cada entrada subsequente até que o erro seja resolvido.

document.querySelectorAll('#registrationForm input').forEach((input) => {
 input.addEventListener('blur', () => {
 validateField(input.id, input.value);
 });
 input.addEventListener('input', () => {
 // If the field currently has an error, re-validate on each keystroke
 const errorSpan = document.getElementById(input.id + 'Error');
 if (errorSpan.textContent !== '') {
 validateField(input.id, input.value);
 }
 });
});

A função verifica apenas as regras para um único campo e atualiza o intervalo de erro correspondente.

function validateField(fieldId, value) {
 const formData = new FormData(form);
 formData.set(fieldId, value);
 const rules = fieldRules[fieldId];
 if (!rules) return;
 for (const rule of rules) {
 const result = rule(value, formData);
 if (result !== true) {
 setFieldError(fieldId, result);
 return;
 }
 }
 clearFieldError(fieldId);
}

Erros e Estados de Sucesso

Use o CSS para alterar a aparência visual dos campos válidos/inválidos. As funções e adicionam ou removem as classes CSS e atualizam o atributo para acessibilidade.

function setFieldError(fieldId, message) {
 const input = document.getElementById(fieldId);
 const errorSpan = document.getElementById(fieldId + 'Error');
 input.classList.add('is-invalid');
 input.classList.remove('is-valid');
 input.setAttribute('aria-invalid', 'true');
 errorSpan.textContent = message;
}

function clearFieldError(fieldId) {
 const input = document.getElementById(fieldId);
 const errorSpan = document.getElementById(fieldId + 'Error');
 input.classList.remove('is-invalid');
 input.classList.add('is-valid');
 input.setAttribute('aria-invalid', 'false');
 errorSpan.textContent = '';
}

O CSS correspondente poderia ser:

.is-invalid {
 border-color: #dc3545;
}
.is-valid {
 border-color: #28a745;
}
.error-message {
 color: #dc3545;
 font-size: 0.875rem;
 min-height: 1.2em;
}

Construindo um medidor de força de senha

Um medidor de força de senhas fornece feedback visual que incentiva os usuários a criar senhas mais fortes. Ao invés de um simples passe/falha, você pode calcular uma pontuação e mostrar uma barra de progresso.

function passwordStrengthScore(password) {
 let score = 0;
 if (password.length >= 8) score += 1;
 if (password.length >= 12) score += 1;
 if (/[A-Z]/.test(password)) score += 1;
 if (/[a-z]/.test(password)) score += 1;
 if (/[0-9]/.test(password)) score += 1;
 if (/[^A-Za-z0-9]/.test(password)) score += 1;
 return score; // 0-6
}

Anexar um evento ao campo de senha que atualiza um elemento de medidor. Você pode mapear a pontuação para uma etiqueta como Fraco (0-2), Justo (3-4), Forte (5-6).

const passwordMeter = document.getElementById('passwordStrengthMeter');
const passwordInput = document.getElementById('password');

passwordInput.addEventListener('input', () => {
 const score = passwordStrengthScore(passwordInput.value);
 const percentage = (score / 6) * 100;
 passwordMeter.value = percentage;
 passwordMeter.style.accentColor = score < 3 ? '#dc3545' : score < 5 ? '#ffc107' : '#28a745';
});

Regras Personalizadas Assíncronas

Algumas verificações de validação requerem uma viagem de ida e volta ao servidor, como verificar se um nome de usuário ou e-mail já está registrado. Para lidar com isso sem bloquear a interface, você pode fazer do seu validador uma função assincronizada e ajustar a lógica de validação para aguardar o resultado.

const asyncValidators = {
 uniqueUsername: async (value) => {
 try {
 const response = await fetch(`/api/check-username?username=${encodeURIComponent(value)}`);
 const data = await response.json();
 return data.available || 'Username is already taken.';
 } catch {
 return 'Could not verify username availability.';
 }
 }
};

Em seguida, atualize para suportar os validadores assync. Você também deve lidar com o estado de carregamento para evitar várias solicitações simultâneas (denunciar a solicitação).

Melhores práticas para formulários de produção

  • Sempre valida o lado do servidor. A validação do lado do cliente é uma conveniência, nunca uma camada de segurança. Use as mesmas regras na infra- estrutura para rejeitar dados maliciosos ou malformados.
  • Denuncie verificações de assincronização. Se você validar em teclas para chamadas de servidor, use um temporizador de desembolso (por exemplo, 300 ms) para evitar martelar o servidor.
  • Use a API de validação de restrições quando possível. Você pode misturar JavaScript personalizado com os métodos incorporados e para uma abordagem híbrida. Isto é útil para campos que têm restrições padrão, mas precisam de mensagens personalizadas.
  • Tornar a validação acessível. Use para associar mensagens de erro com entradas. Atualizar e regiões para que os leitores de tela anunciem erros.
  • Teste entre navegadores e dispositivos. Lidar com eventos de toque, teclados virtuais e diferentes tamanhos de tela é essencial. Também teste com o preenchimento automático do navegador, que pode disparar eventos em ordens inesperadas.
  • Mantenha o desempenho em mente. Para formulários complexos com muitos campos, validação em lote ou usando para atualizações de UI pode evitar o jank.
  • Fornecer mensagens de erro claras e construtivas . Diga ao usuário exatamente o que está errado e como corrigi-lo. Evite mensagens genéricas como "input inválido". Em vez disso, diga "Passeword deve incluir pelo menos um número."

Indo mais longe: Validação Modular com Bibliotecas

Se o seu projeto requer um alto nível de regras personalizadas e validação em tempo real, você pode considerar uma biblioteca leve como Validate.js ou a incorporada Validate.js. No entanto, a abordagem de Javascript baunilha demonstrada neste artigo lhe dá controle completo e dependências zero, que é ideal para aplicações sensíveis ao desempenho ou quando você precisa seguir diretrizes específicas de design.

Você também pode combinar validação personalizada com frameworks modernos como React ou Vue, mas os conceitos centrais de separar regras da apresentação permanecem os mesmos.

Pistácios comuns a evitar

  • Validando apenas no envio. Os usuários preferem feedback imediato. No mínimo, valide em borrão para cada campo.
  • Não é necessário redefinição de erros quando os campos se tornarem válidos. Uma vez que um usuário corrija um erro, limpe a mensagem. A lógica em tempo real neste artigo lida com isso automaticamente.
  • Assumindo que o usuário obedecerá à validação. O JavaScript pode ser desativado completamente. Sempre faça backup de cada verificação com a lógica do servidor.
  • Regras de sobrecomplicação. Mantenha os validadores simples e focados. Uma função deve testar uma preocupação. Combine-os para aplicar vários critérios.

Conclusão

A validação personalizada de formulários JavaScript é uma habilidade essencial para criar aplicações web polidas e fáceis de usar. Ao estruturar o seu código em torno das funções de validação reutilizáveis e do feedback em tempo real, você pode lidar com tudo, desde campos básicos necessários a medidores complexos de força de senha e comparações de campo cruzado. Os exemplos fornecidos neste artigo fornecem-lhe uma base sólida para se adaptar aos seus próprios projetos.

Lembre-se que a validação é uma parte crítica da experiência do usuário. Teste seus formulários completamente, ouça o feedback do usuário, e sempre emparelhe validação do lado do cliente com verificações robustas do lado do servidor. Para mais leitura, consulte o guia W3C Web Accessibility Initiative sobre validação do formulário e o artigo MDN sobre validação do formulário.

Seguindo os padrões e as melhores práticas aqui descritas, você criará formulários que são seguros e agradáveis de usar. Comece experimentando os trechos de código, personalize as regras para corresponder à lógica de seu negócio e itere com base em interações reais do usuário.