Javascript

Formulários: validação e coleta de dados Já leu

11 min de leitura

Formulários: validação e coleta de dados
Formulários são a principal forma de comunicação entre o usuário e uma aplicação web. Login, cadastro, checkout

Formulários são a principal forma de comunicação entre o usuário e uma aplicação web. Login, cadastro, checkout, busca, contato — tudo passa por formulários. E trabalhar bem com eles exige muito mais do que um <form> no HTML.

Neste artigo vamos aprender a coletar dados de formulários, validar em tempo real, dar feedback visual e entregar uma experiência de qualidade ao usuário — tudo isso sem usar nenhuma biblioteca externa.


Estrutura HTML base

Vamos trabalhar com este formulário de cadastro ao longo do artigo:

<form id="form-cadastro" novalidate>

  <div class="campo">
    <label for="nome">Nome completo</label>
    <input type="text" id="nome" name="nome" placeholder="Seu nome completo">
    <span class="erro" id="erro-nome"></span>
  </div>

  <div class="campo">
    <label for="email">E-mail</label>
    <input type="email" id="email" name="email" placeholder="seu@email.com">
    <span class="erro" id="erro-email"></span>
  </div>

  <div class="campo">
    <label for="senha">Senha</label>
    <input type="password" id="senha" name="senha" placeholder="Mínimo 8 caracteres">
    <span class="erro" id="erro-senha"></span>
  </div>

  <div class="campo">
    <label for="confirmar">Confirmar senha</label>
    <input type="password" id="confirmar" name="confirmar" placeholder="Repita a senha">
    <span class="erro" id="erro-confirmar"></span>
  </div>

  <div class="campo">
    <label for="perfil">Perfil</label>
    <select id="perfil" name="perfil">
      <option value="">Selecione...</option>
      <option value="estudante">Estudante</option>
      <option value="profissional">Profissional</option>
      <option value="empresa">Empresa</option>
    </select>
    <span class="erro" id="erro-perfil"></span>
  </div>

  <div class="campo campo-check">
    <input type="checkbox" id="termos" name="termos">
    <label for="termos">Li e aceito os termos de uso</label>
    <span class="erro" id="erro-termos"></span>
  </div>

  <button type="submit" id="btn-enviar">Criar conta</button>

</form>

O atributo novalidate no <form> desativa a validação nativa do navegador — queremos controlar tudo nós mesmos.


Coletando dados do formulário

Existem várias formas de coletar dados. Veja as principais:

// Acessando campos individualmente
const nome = document.querySelector("#nome").value;
const email = document.querySelector("#email").value;

// FormData — coleta todos os campos de uma vez
const form = document.querySelector("#form-cadastro");

form.addEventListener("submit", (e) => {
  e.preventDefault();

  const dados = new FormData(form);

  // Acessar um campo pelo name
  console.log(dados.get("nome"));
  console.log(dados.get("email"));

  // Converter para objeto simples
  const objeto = Object.fromEntries(dados.entries());
  console.log(objeto);
  // { nome: "Ana", email: "ana@email.com", senha: "...", ... }
});

FormData é especialmente útil quando o formulário tem muitos campos — você não precisa selecionar cada um individualmente.


Tipos de campos e como ler seus valores

// Input de texto, email, password, number
const valor = document.querySelector("#nome").value; // string

// Checkbox
const aceito = document.querySelector("#termos").checked; // boolean

// Radio buttons — qual está marcado?
const genero = document.querySelector('input[name="genero"]:checked');
const valorGenero = genero ? genero.value : null;

// Select
const select = document.querySelector("#perfil");
const opcaoSelecionada = select.value;        // valor da option
const textoSelecionado = select.options[select.selectedIndex].text;

// Select múltiplo
const selectMultiplo = document.querySelector("#interesses");
const selecionados = Array.from(selectMultiplo.selectedOptions)
  .map(opt => opt.value);

// Input de arquivo
const arquivo = document.querySelector("#foto").files[0];
console.log(arquivo.name, arquivo.size, arquivo.type);

Sistema de validação

Vamos construir um sistema de validação modular e reutilizável:

// ── Regras de validação ────────────────────────────

const regras = {
  nome(valor) {
    if (!valor.trim()) return "Nome é obrigatório.";
    if (valor.trim().length < 3) return "Nome deve ter pelo menos 3 caracteres.";
    if (valor.trim().length > 80) return "Nome deve ter no máximo 80 caracteres.";
    if (!/^[a-zA-ZÀ-ÿ\s]+$/.test(valor)) return "Nome deve conter apenas letras.";
    return null; // null = válido
  },

  email(valor) {
    if (!valor.trim()) return "E-mail é obrigatório.";
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!regex.test(valor)) return "E-mail inválido.";
    return null;
  },

  senha(valor) {
    if (!valor) return "Senha é obrigatória.";
    if (valor.length < 8) return "Senha deve ter pelo menos 8 caracteres.";
    if (!/[A-Z]/.test(valor)) return "Senha deve conter ao menos uma letra maiúscula.";
    if (!/[0-9]/.test(valor)) return "Senha deve conter ao menos um número.";
    return null;
  },

  confirmar(valor) {
    const senha = document.querySelector("#senha").value;
    if (!valor) return "Confirmação de senha é obrigatória.";
    if (valor !== senha) return "As senhas não coincidem.";
    return null;
  },

  perfil(valor) {
    if (!valor) return "Selecione um perfil.";
    return null;
  },

  termos(valor) {
    const checkbox = document.querySelector("#termos");
    if (!checkbox.checked) return "Você deve aceitar os termos de uso.";
    return null;
  },
};

Funções de feedback visual

// ── Funções de UI ──────────────────────────────────

function mostrarErro(campo, mensagem) {
  const input = document.querySelector(`#${campo}`);
  const erroSpan = document.querySelector(`#erro-${campo}`);

  input.classList.remove("valido");
  input.classList.add("invalido");
  erroSpan.textContent = mensagem;
}

function mostrarSucesso(campo) {
  const input = document.querySelector(`#${campo}`);
  const erroSpan = document.querySelector(`#erro-${campo}`);

  input.classList.remove("invalido");
  input.classList.add("valido");
  erroSpan.textContent = "";
}

function limparFeedback(campo) {
  const input = document.querySelector(`#${campo}`);
  const erroSpan = document.querySelector(`#erro-${campo}`);

  input.classList.remove("valido", "invalido");
  erroSpan.textContent = "";
}

// ── Validação de campo individual ─────────────────

function validarCampo(nomeCampo) {
  const input = document.querySelector(`#${nomeCampo}`);
  const valor = input.type === "checkbox" ? input.checked : input.value;
  const erro = regras[nomeCampo](valor);

  if (erro) {
    mostrarErro(nomeCampo, erro);
    return false;
  } else {
    mostrarSucesso(nomeCampo);
    return true;
  }
}

Validação em tempo real no blur

// ── Eventos de validação em tempo real ────────────

const campos = ["nome", "email", "senha", "confirmar", "perfil"];

campos.forEach(campo => {
  const input = document.querySelector(`#${campo}`);

  // Valida quando o campo perde foco
  input.addEventListener("blur", () => {
    validarCampo(campo);
  });

  // Limpa o erro enquanto digita (após ter sido tocado)
  input.addEventListener("input", () => {
    if (input.classList.contains("invalido")) {
      validarCampo(campo);
    }
  });
});

// Checkbox de termos
document.querySelector("#termos").addEventListener("change", () => {
  validarCampo("termos");
});

Validando o formulário completo no submit

// ── Submit ─────────────────────────────────────────

const form = document.querySelector("#form-cadastro");
const btnEnviar = document.querySelector("#btn-enviar");

form.addEventListener("submit", (e) => {
  e.preventDefault();

  // Valida todos os campos
  const todosCampos = ["nome", "email", "senha", "confirmar", "perfil", "termos"];
  const resultados = todosCampos.map(campo => validarCampo(campo));
  const formValido = resultados.every(resultado => resultado === true);

  if (!formValido) {
    // Foca no primeiro campo inválido
    const primeiroInvalido = form.querySelector(".invalido");
    if (primeiroInvalido) primeiroInvalido.focus();
    return;
  }

  // Coleta os dados
  const formData = new FormData(form);
  const dados = Object.fromEntries(formData.entries());

  // Remove a confirmação de senha — não precisamos enviar
  delete dados.confirmar;

  // Simula envio
  enviarFormulario(dados);
});

async function enviarFormulario(dados) {
  const btn = document.querySelector("#btn-enviar");

  // Estado de carregamento
  btn.disabled = true;
  btn.textContent = "Criando conta...";

  // Simula uma requisição (aprenderemos fetch no Módulo 3)
  await new Promise(resolve => setTimeout(resolve, 1500));

  console.log("Dados enviados:", dados);

  // Feedback de sucesso
  form.innerHTML = `
    <div class="sucesso">
      <div class="icone-sucesso">✅</div>
      <h2>Conta criada com sucesso!</h2>
      <p>Bem-vindo, <strong>${dados.nome}</strong>!</p>
      <p>Um e-mail de confirmação foi enviado para <strong>${dados.email}</strong>.</p>
    </div>
  `;
}

Força da senha em tempo real

Um recurso muito comum — e útil para demonstrar como reagir a cada caractere digitado:

const inputSenha = document.querySelector("#senha");
const barraForca = document.createElement("div");
barraForca.id = "barra-forca";

// Insere a barra após o input de senha
inputSenha.after(barraForca);

function calcularForca(senha) {
  let pontos = 0;

  if (senha.length >= 8) pontos++;
  if (senha.length >= 12) pontos++;
  if (/[A-Z]/.test(senha)) pontos++;
  if (/[a-z]/.test(senha)) pontos++;
  if (/[0-9]/.test(senha)) pontos++;
  if (/[^A-Za-z0-9]/.test(senha)) pontos++; // caractere especial

  return pontos;
}

function atualizarBarraForca(senha) {
  const forca = calcularForca(senha);

  const niveis = [
    { min: 0, label: "",         cor: "transparent", largura: "0%" },
    { min: 1, label: "Muito fraca", cor: "#e53935",  largura: "20%" },
    { min: 2, label: "Fraca",    cor: "#fb8c00",     largura: "40%" },
    { min: 3, label: "Razoável", cor: "#fdd835",     largura: "60%" },
    { min: 4, label: "Boa",      cor: "#43a047",     largura: "80%" },
    { min: 5, label: "Forte",    cor: "#1b5e20",     largura: "100%" },
  ];

  const nivel = niveis[Math.min(forca, niveis.length - 1)];

  barraForca.innerHTML = `
    <div style="
      height: 4px;
      background: #eee;
      border-radius: 4px;
      margin-top: .35rem;
      overflow: hidden;
    ">
      <div style="
        height: 100%;
        width: ${nivel.largura};
        background: ${nivel.cor};
        border-radius: 4px;
        transition: width .3s, background .3s;
      "></div>
    </div>
    ${senha ? `<small style="color: ${nivel.cor}; font-weight: 600;">${nivel.label}</small>` : ""}
  `;
}

inputSenha.addEventListener("input", (e) => {
  atualizarBarraForca(e.target.value);
});

O CSS do formulário

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: 'Segoe UI', sans-serif;
  background: #f0f2f5;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  min-height: 100vh;
  padding: 2rem 1rem;
}

form {
  background: white;
  padding: 2rem;
  border-radius: 12px;
  width: 100%;
  max-width: 480px;
  box-shadow: 0 4px 24px rgba(0,0,0,.08);
}

.campo {
  margin-bottom: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: .35rem;
}

label {
  font-size: .9rem;
  font-weight: 600;
  color: #333;
}

input, select {
  padding: .7rem 1rem;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  font-size: 1rem;
  transition: border-color .2s, box-shadow .2s;
}

input:focus, select:focus {
  outline: none;
  border-color: #5c6bc0;
  box-shadow: 0 0 0 3px rgba(92,107,192,.15);
}

input.valido   { border-color: #43a047; }
input.invalido { border-color: #e53935; }

.erro {
  font-size: .8rem;
  color: #e53935;
  min-height: 1rem;
}

.campo-check {
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
}

.campo-check label { font-weight: 400; }

button[type="submit"] {
  width: 100%;
  padding: .85rem;
  background: #5c6bc0;
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  margin-top: .5rem;
  transition: background .2s;
}

button[type="submit"]:hover:not(:disabled) { background: #3949ab; }
button[type="submit"]:disabled { opacity: .6; cursor: not-allowed; }

.sucesso {
  text-align: center;
  padding: 2rem 1rem;
}

.icone-sucesso { font-size: 3rem; margin-bottom: 1rem; }
.sucesso h2 { margin-bottom: .5rem; color: #1b5e20; }
.sucesso p { color: #555; margin-top: .5rem; }

Boas práticas com formulários

// ✅ 1. Sempre use e.preventDefault() no submit
form.addEventListener("submit", (e) => {
  e.preventDefault();
  // ...
});

// ✅ 2. Valide no cliente E no servidor — nunca só no cliente
// A validação no front-end é para UX — a do back-end é para segurança

// ✅ 3. Dê feedback imediato e específico
// ❌ "Dados inválidos." — inútil
// ✅ "O e-mail deve estar no formato nome@dominio.com" — útil

// ✅ 4. Nunca desabilite o botão de submit antes do usuário tentar
// Desabilite apenas durante o envio

// ✅ 5. Preserve os dados do usuário em caso de erro
// Nunca limpe o formulário quando há erro de validação

// ✅ 6. Use o atributo autocomplete apropriado
// <input autocomplete="email"> — ajuda o gerenciador de senhas e preenchimento

// ✅ 7. Associe labels aos inputs com for/id
// <label for="email"> + <input id="email"> — acessibilidade e UX

Tarefa para você

Crie um formulário de cálculo de financiamento com os campos:

- Valor do imóvel (number, mínimo R$ 50.000)
- Entrada (number, mínimo 20% do valor)
- Prazo em meses (select: 60, 120, 180, 240, 360)
- Taxa de juros anual (number, entre 0.1% e 20%)
- Nome do solicitante (text)

Ao submeter (com todos os campos válidos), exiba abaixo do formulário:

Resumo do Financiamento
- Valor financiado: R$ X
- Parcela mensal estimada: R$ X (usando a fórmula Price)
- Total pago: R$ X
- Total de juros: R$ X

A fórmula Price para cálculo de parcela:

// PMT = PV * (i * (1 + i)^n) / ((1 + i)^n - 1)
// PV = valor financiado, i = taxa mensal, n = prazo em meses
const taxaMensal = taxaAnual / 100 / 12;
const parcela = valorFinanciado *
  (taxaMensal * Math.pow(1 + taxaMensal, prazo)) /
  (Math.pow(1 + taxaMensal, prazo) - 1);

Conclusão

Neste artigo você aprendeu:

  • Como coletar dados de diferentes tipos de campos
  • FormData e Object.fromEntries para coleta eficiente
  • Como construir um sistema de validação modular
  • Feedback visual com classes CSS e mensagens de erro
  • Validação em tempo real no blur e input
  • Como implementar indicador de força de senha
  • Estados do botão durante o envio
  • Boas práticas de UX e segurança em formulários

No próximo artigo vamos aprender sobre LocalStorage e SessionStorage — como salvar dados no navegador do usuário e manter o estado da aplicação entre sessões.


📌 Próximo artigo: Aula 15 — LocalStorage e SessionStorage


📚 Fontes e Referências

Comentários

Mais em Javascript

Condicionais: if, else e switch
Condicionais: if, else e switch

No Javascript n&oacute;s guardamos informa&ccedil;&otilde;es em vari&aacute;v...

Tratamento de Erros com try, catch e finally
Tratamento de Erros com try, catch e finally

Todo programa que vai para produ&ccedil;&atilde;o vai encontrar situa&ccedil;...

Eventos: click, input, submit e muito mais
Eventos: click, input, submit e muito mais

No artigo anterior aprendemos a selecionar e modificar elementos do DOM. Mas...