Manipulação de Formulários com JavaScript Vanilla: Sem Necessidade de Frameworks

Os formulários são a base da interação do usuário na web. Embora frameworks como React e Vue ofereçam abstrações convenientes, compreender como manipular formulários com JavaScript vanilla oferece controle completo e elimina dependências desnecessárias.
Este guia aborda tudo o que você precisa saber sobre manipulação nativa de formulários em JavaScript—desde a seleção de elementos e captura de valores até a validação de entrada e processamento de submissões.
Principais Pontos
- JavaScript vanilla fornece todas as ferramentas necessárias para manipulação completa de formulários sem frameworks
- Use
event.preventDefault()
para controlar o comportamento de submissão de formulários - Aproveite a validação de restrições HTML5 para validação básica com código mínimo
- Implemente validação em tempo real com os eventos
input
eblur
para melhor experiência do usuário - A API FormData simplifica a coleta e processamento de valores de formulários
- Sempre considere acessibilidade ao implementar validação personalizada de formulários
Manipulação Básica de Formulários com JavaScript
Vamos começar com um formulário HTML simples:
<form id="signup-form">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username">
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email">
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password">
</div>
<button type="submit">Sign Up</button>
</form>
Selecionando Elementos de Formulário
Você pode acessar um formulário e seus elementos de várias maneiras:
// Selecionar por ID (recomendado para formulários específicos)
const form = document.getElementById('signup-form');
// Selecionar usando querySelector
const form = document.querySelector('#signup-form');
// Acessar coleção de elementos do formulário
const forms = document.forms;
const signupForm = forms['signup-form']; // por id/nome
const firstForm = forms[0]; // por índice
Capturando o Evento de Submissão
Para processar a submissão do formulário, adicione um event listener ao formulário:
const form = document.getElementById('signup-form');
form.addEventListener('submit', function(event) {
// Previne a submissão padrão do formulário
event.preventDefault();
// Processa os dados do formulário aqui
console.log('Formulário submetido!');
});
O método preventDefault()
impede que o navegador submeta o formulário e recarregue a página, dando a você controle sobre o que acontece em seguida.
Acessando Valores de Entrada do Formulário
Existem múltiplas maneiras de acessar valores de entrada do formulário:
Método 1: Acessar Elementos Individuais
form.addEventListener('submit', function(event) {
event.preventDefault();
// Obter valores de elementos individuais do formulário
const username = document.getElementById('username').value;
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
console.log(username, email, password);
});
Método 2: Usando form.elements
A propriedade elements
fornece acesso a todos os controles do formulário:
form.addEventListener('submit', function(event) {
event.preventDefault();
// Acessar via coleção elements
const username = form.elements.username.value;
const email = form.elements.email.value;
const password = form.elements.password.value;
console.log(username, email, password);
});
Método 3: Usando a API FormData
A API FormData
fornece uma maneira limpa de capturar todos os valores do formulário:
form.addEventListener('submit', function(event) {
event.preventDefault();
// Criar objeto FormData a partir do formulário
const formData = new FormData(form);
// Acessar valores individuais
const username = formData.get('username');
const email = formData.get('email');
// Converter para um objeto regular
const formObject = Object.fromEntries(formData);
console.log(formObject); // {username: '...', email: '...', password: '...'}
});
Validação de Formulários com Restrições HTML5
O HTML5 moderno fornece atributos de validação integrados que funcionam sem JavaScript:
<form id="signup-form">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required minlength="3">
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required minlength="8"
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$">
<small>A senha deve conter pelo menos 8 caracteres, incluindo maiúsculas, minúsculas e números</small>
</div>
<button type="submit">Sign Up</button>
</form>
Atributos de validação comuns incluem:
required
: Campo deve ser preenchidominlength
/maxlength
: Comprimento mínimo/máximo do textomin
/max
: Valores mínimo/máximo para entradas numéricaspattern
: Padrão de expressão regular para correspondertype="email"
: Valida formato de emailtype="url"
: Valida formato de URL
Aproveitando a API de Validação de Restrições
JavaScript pode acessar o sistema de validação integrado do navegador:
form.addEventListener('submit', function(event) {
// Verificar se o formulário é válido usando a API de Validação de Restrições
if (!form.checkValidity()) {
// Formulário é inválido - deixar o navegador lidar com a exibição de erros
return;
}
// Se chegamos aqui, o formulário é válido
event.preventDefault();
// Processar os dados válidos do formulário
console.log('Formulário é válido, processando dados...');
});
Você também pode verificar a validade de entradas específicas:
const emailInput = document.getElementById('email');
// Verificar se o email é válido
if (emailInput.validity.valid) {
console.log('Email é válido');
} else {
// Verificar falhas específicas de validação
if (emailInput.validity.typeMismatch) {
console.log('Formato do email está incorreto');
}
if (emailInput.validity.valueMissing) {
console.log('Email é obrigatório');
}
}
Validação Personalizada e Mensagens de Erro
Embora a validação HTML5 seja conveniente, você frequentemente precisa de lógica de validação personalizada e mensagens de erro:
form.addEventListener('submit', function(event) {
event.preventDefault();
const username = document.getElementById('username');
const email = document.getElementById('email');
const password = document.getElementById('password');
// Limpar mensagens de erro anteriores
clearErrors();
let isValid = true;
// Validação personalizada de username
if (username.value.trim() === '') {
displayError(username, 'Username é obrigatório');
isValid = false;
} else if (username.value.length < 3) {
displayError(username, 'Username deve ter pelo menos 3 caracteres');
isValid = false;
}
// Validação personalizada de email
if (!isValidEmail(email.value)) {
displayError(email, 'Por favor, insira um endereço de email válido');
isValid = false;
}
// Validação personalizada de senha
if (password.value.length < 8) {
displayError(password, 'Senha deve ter pelo menos 8 caracteres');
isValid = false;
} else if (!/[A-Z]/.test(password.value)) {
displayError(password, 'Senha deve conter pelo menos uma letra maiúscula');
isValid = false;
}
// Se válido, processar o formulário
if (isValid) {
console.log('Formulário é válido, submetendo...');
// Submeter dados do formulário
}
});
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function displayError(input, message) {
const formControl = input.parentElement;
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
input.classList.add('error-input');
}
function clearErrors() {
document.querySelectorAll('.error-message').forEach(error => error.remove());
document.querySelectorAll('.error-input').forEach(input => {
input.classList.remove('error-input');
});
}
Validação em Tempo Real com Eventos de Entrada
Para uma melhor experiência do usuário, valide a entrada conforme os usuários digitam:
const emailInput = document.getElementById('email');
emailInput.addEventListener('input', function() {
if (this.value && !isValidEmail(this.value)) {
this.setCustomValidity('Por favor, insira um endereço de email válido');
} else {
this.setCustomValidity('');
}
});
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
O evento input
dispara sempre que o usuário altera o valor, enquanto o evento change
dispara quando a entrada perde o foco após ser alterada.
Manipulando Diferentes Tipos de Entrada
Botões de Rádio
<div>
<p>Plano de assinatura:</p>
<label>
<input type="radio" name="plan" value="free" checked> Gratuito
</label>
<label>
<input type="radio" name="plan" value="premium"> Premium
</label>
</div>
// Obter valor do botão de rádio selecionado
const selectedPlan = document.querySelector('input[name="plan"]:checked').value;
// Escutar mudanças
document.querySelectorAll('input[name="plan"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log('Plano selecionado:', this.value);
});
});
Caixas de Seleção
<div>
<p>Interesses:</p>
<label>
<input type="checkbox" name="interests" value="javascript"> JavaScript
</label>
<label>
<input type="checkbox" name="interests" value="html"> HTML
</label>
<label>
<input type="checkbox" name="interests" value="css"> CSS
</label>
</div>
// Obter todos os valores selecionados
function getSelectedInterests() {
const checkboxes = document.querySelectorAll('input[name="interests"]:checked');
const values = Array.from(checkboxes).map(cb => cb.value);
return values;
}
// Na submissão do formulário
form.addEventListener('submit', function(event) {
event.preventDefault();
const interests = getSelectedInterests();
console.log('Interesses selecionados:', interests);
});
Menus Suspensos Select
<div>
<label for="country">País:</label>
<select id="country" name="country">
<option value="">Selecione um país</option>
<option value="us">Estados Unidos</option>
<option value="ca">Canadá</option>
<option value="uk">Reino Unido</option>
</select>
</div>
// Obter valor selecionado
const country = document.getElementById('country').value;
// Escutar mudanças
document.getElementById('country').addEventListener('change', function() {
console.log('País selecionado:', this.value);
});
Exemplo Completo de Processamento de Formulário
Vamos juntar tudo em um exemplo completo:
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('signup-form');
// Adicionar validação em tempo real
setupRealTimeValidation();
form.addEventListener('submit', function(event) {
event.preventDefault();
if (validateForm()) {
// Criar objeto com dados do formulário
const formData = new FormData(form);
const userData = Object.fromEntries(formData);
// Aqui você normalmente enviaria os dados para um servidor
console.log('Submetendo dados do usuário:', userData);
// Simular chamada de API
submitFormData(userData)
.then(response => {
showSuccessMessage('Conta criada com sucesso!');
form.reset();
})
.catch(error => {
showErrorMessage('Falha ao criar conta. Tente novamente.');
});
}
});
function validateForm() {
// Limpar erros anteriores
clearErrors();
let isValid = true;
const username = form.elements.username;
const email = form.elements.email;
const password = form.elements.password;
// Validar username
if (username.value.trim() === '') {
displayError(username, 'Username é obrigatório');
isValid = false;
} else if (username.value.length < 3) {
displayError(username, 'Username deve ter pelo menos 3 caracteres');
isValid = false;
}
// Validar email
if (email.value.trim() === '') {
displayError(email, 'Email é obrigatório');
isValid = false;
} else if (!isValidEmail(email.value)) {
displayError(email, 'Por favor, insira um endereço de email válido');
isValid = false;
}
// Validar senha
if (password.value === '') {
displayError(password, 'Senha é obrigatória');
isValid = false;
} else if (password.value.length < 8) {
displayError(password, 'Senha deve ter pelo menos 8 caracteres');
isValid = false;
} else if (!/[A-Z]/.test(password.value) || !/[a-z]/.test(password.value) || !/[0-9]/.test(password.value)) {
displayError(password, 'Senha deve incluir maiúsculas, minúsculas e números');
isValid = false;
}
return isValid;
}
function setupRealTimeValidation() {
const inputs = form.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('input', function() {
// Limpar erro quando o usuário começar a digitar
const errorElement = this.parentElement.querySelector('.error-message');
if (errorElement) {
errorElement.remove();
this.classList.remove('error-input');
}
});
});
// Validação específica de email
form.elements.email.addEventListener('blur', function() {
if (this.value && !isValidEmail(this.value)) {
displayError(this, 'Por favor, insira um endereço de email válido');
}
});
}
// Funções auxiliares
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function displayError(input, message) {
const formControl = input.parentElement;
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
input.classList.add('error-input');
}
function clearErrors() {
document.querySelectorAll('.error-message').forEach(error => error.remove());
document.querySelectorAll('.error-input').forEach(input => {
input.classList.remove('error-input');
});
}
function showSuccessMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'success-message';
messageElement.textContent = message;
form.parentElement.insertBefore(messageElement, form);
// Remover após 3 segundos
setTimeout(() => {
messageElement.remove();
}, 3000);
}
function showErrorMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'error-banner';
messageElement.textContent = message;
form.parentElement.insertBefore(messageElement, form);
// Remover após 3 segundos
setTimeout(() => {
messageElement.remove();
}, 3000);
}
// Simular submissão de API
function submitFormData(data) {
return new Promise((resolve, reject) => {
// Simular requisição de rede
setTimeout(() => {
// Taxa de sucesso de 90% para demonstração
if (Math.random() > 0.1) {
resolve({ success: true, message: 'Usuário criado' });
} else {
reject({ success: false, message: 'Erro do servidor' });
}
}, 1500);
});
}
});
Considerações de Acessibilidade para Formulários
Ao manipular formulários com JavaScript, certifique-se de que permaneçam acessíveis:
- Manter gerenciamento de foco: Ao exibir erros, defina o foco no primeiro campo inválido
- Usar atributos ARIA: Adicione
aria-invalid="true"
aos campos inválidos - Associar mensagens de erro: Use
aria-describedby
para conectar entradas com suas mensagens de erro - Fornecer instruções claras: Use elementos
<label>
e mensagens de erro descritivas - Suportar navegação por teclado: Certifique-se de que todos os controles de formulário sejam acessíveis via teclado
Exemplo de tratamento de erro acessível:
function displayError(input, message) {
const formControl = input.parentElement;
const errorId = `${input.id}-error`;
const errorElement = document.createElement('div');
errorElement.id = errorId;
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
// Adicionar atributos de acessibilidade
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', errorId);
// Definir foco na primeira entrada inválida
if (!document.querySelector('.error-input')) {
input.focus();
}
input.classList.add('error-input');
}
Perguntas Frequentes
Use o método reset integrado no elemento do formulário: document.getElementById('meuFormulario').reset();
Use o método checkValidity da API de Validação de Restrições: const isValid = form.checkValidity();
Para submeter dados de formulário de forma assíncrona, use a API Fetch junto com o objeto FormData. Primeiro, crie uma instância FormData a partir do seu formulário. Em seguida, envie-a usando fetch com o método POST. Certifique-se de lidar com a resposta e capturar quaisquer erros durante a submissão.
Para manipular uploads de arquivos, acesse a propriedade files do elemento de entrada de arquivo, recupere o arquivo selecionado e anexe-o a um objeto FormData antes de submetê-lo usando fetch ou outro método.
O evento `input` dispara sempre que o valor da entrada muda, enquanto o evento `change` só dispara quando a entrada perde o foco e o valor mudou desde que ganhou o foco.
Para adicionar campos de formulário dinamicamente, crie novos elementos de entrada usando document.createElement, defina seu tipo e nome, e anexe-os a um elemento container no formulário.
Conclusão
Ao dominar a manipulação de formulários com JavaScript vanilla, você ganha controle completo sobre seus formulários sem depender de bibliotecas externas. Esta abordagem reduz dependências, melhora a performance e aprofunda sua compreensão de como os formulários web realmente funcionam.