Back

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

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 e blur 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 preenchido
  • minlength/maxlength: Comprimento mínimo/máximo do texto
  • min/max: Valores mínimo/máximo para entradas numéricas
  • pattern: Padrão de expressão regular para corresponder
  • type="email": Valida formato de email
  • type="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);
});
<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:

  1. Manter gerenciamento de foco: Ao exibir erros, defina o foco no primeiro campo inválido
  2. Usar atributos ARIA: Adicione aria-invalid="true" aos campos inválidos
  3. Associar mensagens de erro: Use aria-describedby para conectar entradas com suas mensagens de erro
  4. Fornecer instruções claras: Use elementos <label> e mensagens de erro descritivas
  5. 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.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers