12k
All articles

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

Formulários em JavaScript puro podem ser tratados capturando submissões, validando com as restrições do HTML5 e lendo os valores dos campos pela FormData API.

OpenReplay Team
OpenReplay Team
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

Como faço para redefinir um formulário com JavaScript?

Use o método reset integrado no elemento do formulário: document.getElementById('meuFormulario').reset();

Como posso verificar se um formulário é válido antes da submissão?

Use o método checkValidity da API de Validação de Restrições: const isValid = form.checkValidity();

Como submeto dados de formulário de forma assíncrona?

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.

Como manipulo uploads de arquivos com JavaScript vanilla?

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.

Qual é a diferença entre os eventos input e change?

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.

Como posso adicionar campos de formulário dinamicamente com JavaScript?

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

We use cookies to improve your experience. By using our site, you accept cookies.