Programação Por Equipe DevBras

Capa do artigo: Inputs HTML5 que validam, abrem o teclado certo e preenchem sozinhos

Inputs HTML5 que validam, abrem o teclado certo e preenchem sozinhos

Use type, inputmode, autocomplete e os atributos de validação do HTML para um formulário que funciona no celular antes do JavaScript, e saiba onde o HTML para.

A cliente abre o cadastro da loja no celular, toca no campo de CEP e aparece o teclado de letras. No e-mail, precisa caçar o arroba. No fim, o botão "Enviar" volta com "Data inválida" porque ela digitou 31/02. A resposta curta: escolha o type certo, complete com inputmode e autocomplete, e use required, min, max e pattern. O navegador passa a abrir o teclado adequado, preencher dados salvos e barrar o erro bobo antes do seu JavaScript rodar.

Isso não substitui validação no servidor. Mas tira do caminho metade do código que tenta adivinhar o que a pessoa quis dizer, e melhora a experiência no celular de graça.

Por que o tipo do campo muda tanto

O atributo type diz ao navegador o que o campo espera. Com essa informação, ele faz três coisas: escolhe o teclado virtual, aplica uma validação básica e oferece controles nativos, como o seletor de data. Um campo type="text" não informa nada, e o navegador trata tudo como texto livre.

O autocomplete diz que dado é aquele: nome, e-mail, CEP, telefone. É o que permite ao navegador e ao gerenciador de senhas preencher o formulário com um toque. Um campo chamado campo3 sem autocomplete não é preenchido por ninguém.

Qual tipo usar para cada campo

  • email: teclado com arroba e validação de formato. Use autocomplete="email".
  • tel: teclado numérico de telefone. Não valida formato, porque telefone varia por país. Use autocomplete="tel".
  • url: exige um endereço com esquema, como https://. Quem digita "www.minhaloja.com" recebe erro; se o público não sabe disso, use texto e normalize no servidor.
  • date: seletor nativo e valor no formato AAAA-MM-DD, independente de como a data aparece na tela. Fevereiro com dia 31 não passa.
  • number: só para quantidades de verdade, com min, max e step. Nunca para CEP, CPF, cartão ou telefone.
  • password: com autocomplete="new-password" no cadastro e current-password no login, para o gerenciador de senhas entender a diferença.

Para CEP, CPF e código numérico, o certo é type="text" com inputmode="numeric". Você ganha o teclado numérico sem os efeitos colaterais do number: zeros à esquerda que somem, setinhas de incremento e a roda do mouse alterando o valor quando a pessoa rola a página. Para valores em reais com vírgula, inputmode="decimal" faz o mesmo papel.

Como montar o formulário passo a passo

  1. Para cada campo, decida o dado real (e-mail, CEP, quantidade) e escolha type e inputmode.
  2. Dê a cada campo um name estável e o autocomplete correspondente.
  3. Associe um label visível com for igual ao id do campo. O placeholder é exemplo, não rótulo: ele some quando a pessoa digita.
  4. Adicione as restrições simples: required, minlength, maxlength, min, max, pattern.
  5. Para regras que envolvem dois campos, use JavaScript com setCustomValidity.
  6. Repita todas as regras no servidor.

Exemplo: cadastro com entrega agendada

<form action="/cadastro" method="post">
  <label for="nome">Nome completo</label>
  <input id="nome" name="nome" autocomplete="name" required minlength="3" maxlength="120">

  <label for="email">E-mail</label>
  <input id="email" name="email" type="email" autocomplete="email" required>

  <label for="celular">Celular com DDD</label>
  <input id="celular" name="celular" type="tel" autocomplete="tel" required minlength="10">

  <label for="cep">CEP</label>
  <input id="cep" name="cep" type="text" inputmode="numeric" autocomplete="postal-code"
         required pattern="[0-9]{5}-?[0-9]{3}" maxlength="9" placeholder="01310-100">

  <label for="qtd">Quantidade de cestas</label>
  <input id="qtd" name="qtd" type="number" min="1" max="20" step="1" value="1" required>

  <label for="inicio">Entregar a partir de</label>
  <input id="inicio" name="inicio" type="date" required>

  <label for="fim">Até</label>
  <input id="fim" name="fim" type="date" required>

  <label for="senha">Crie uma senha</label>
  <input id="senha" name="senha" type="password" autocomplete="new-password" required minlength="8">

  <button type="submit">Criar conta</button>
</form>

O pattern do CEP aceita "01310100" e "01310-100". O navegador trata o padrão como se tivesse ^ no começo e $ no fim, então ele precisa casar com o valor inteiro.

Regra entre dois campos com setCustomValidity

O HTML não sabe que a data final precisa ser igual ou posterior à inicial. Para isso, o JavaScript usa a mesma API de validação do navegador, e a mensagem aparece no mesmo lugar das outras:

const inicio = document.getElementById('inicio');
const fim = document.getElementById('fim');

function conferirPeriodo() {
  fim.min = inicio.value;
  const invalido = inicio.value && fim.value && fim.value < inicio.value;
  fim.setCustomValidity(invalido ? 'A data final precisa ser igual ou depois da inicial.' : '');
}

inicio.addEventListener('input', conferirPeriodo);
fim.addEventListener('input', conferirPeriodo);

A comparação de texto funciona porque o valor de um type="date" está sempre em AAAA-MM-DD. A mensagem precisa ser limpa com string vazia quando a regra volta a valer; senão o campo fica inválido para sempre.

Para destacar os erros só depois que a pessoa mexeu no campo, use a pseudo-classe :user-invalid no CSS, em vez de :invalid, que pinta o formulário inteiro de vermelho antes da primeira tecla:

input:user-invalid {
  border-color: #b00020;
}

Onde o HTML para

CPF com dígito verificador, senha que não pode conter o e-mail, e-mail já cadastrado e estoque disponível são regras do seu sistema. Ficam no JavaScript, para dar retorno rápido, e de novo no servidor, porque qualquer requisição pode ser montada na mão, sem passar pelo formulário. O type="email" não protege o banco de nada.

Para posicionar mensagens e botões, veja formulário que não cansa: rótulo, erro e botão no lugar certo. Para levar o cursor ao primeiro campo com erro, foco no campo certo.

Erros comuns

  • Usar type="number" para CEP ou CPF. O zero à esquerda some e a roda do mouse altera o valor sem a pessoa perceber.
  • Usar placeholder como rótulo. A dica some ao digitar, a pessoa esquece o que era o campo e leitores de tela podem não anunciá-la.
  • Escrever pattern com hífen ou parêntese soltos dentro de colchetes. Os navegadores atuais compilam o padrão com a flag v, que rejeita essa sintaxe; o padrão é ignorado em silêncio e só aparece um aviso no console.
  • Esquecer de limpar o setCustomValidity. O campo continua bloqueando o envio mesmo depois de corrigido.
  • Nomes de campo sem significado e sem autocomplete. O preenchimento automático não funciona e a pessoa digita tudo de novo.
  • Confiar só na validação do navegador. Uma requisição feita com curl passa direto e grava dado errado.

Checklist

  • Nenhum campo de dado conhecido ficou como type="text" sem motivo.
  • CEP, CPF e códigos usam inputmode="numeric", não type="number".
  • Todo campo tem label associado e autocomplete adequado.
  • Os padrões em pattern foram testados e não geram aviso no console.
  • Regras entre campos usam setCustomValidity e limpam a mensagem.
  • O servidor repete todas as validações.
  • O formulário foi testado num celular de verdade, campo por campo.

Pegue o formulário mais usado do seu sistema e abra no celular: em quantos campos o teclado que aparece não combina com o dado que você está pedindo?

Avalie este artigo

Referência: http://www.html5rocks.com/pt/tutorials/forms/html5forms/