quinta-feira, 21 de maio de 2009

Validação de formulários HTML com JavaScript

O uso de expressões regulares em JavaScript pode tornar bem compacta, flexível e poderosa a validação cliente (no navegador web) de campos de formulário, algo muito recomendado como pré-verificação do conteúdo de um formulário antes de submetê-lo ao servidor. O recurso de expressão regular surgiu no JavaScript 1.2, implementado desde a versão 4 de ambos os principais navegadores: Internet Explorer e Netscape Navigator (atual Mozilla).
Para cada tipo de conteúdo — numérico, data, endereço eletrônico etc. — podemos montar uma expressão regular apropriada para sua validação, de acordo com o formato e características desejadas. A validação do conteúdo de um campo através de uma expressão regular para o seu respectivo tipo consiste simplesmente em aplicar o padrão da expressão regular e testá-lo no texto desejado, com o método test( ).

Número

Começamos com uma validação bem trivial: a validação de entrada de um número inteiro não-negativo sem sinal, ou seja, apenas dígitos.
Use o formulário de exemplo ao lado para validar um número apenas com dígitos (inteiro não-negativo sem sinal).
Em JavaScript, uma expressão regular é delimitada por uma barra / inicial e outra final. Cada expressão de validação sempre começa com ^ (início de linha) e termina com $ (fim de linha), para garantir que abrange o conteúdo inteiro do texto (parâmetro) a ser validado.
Existe uma seqüência de expressão regular que significa um dígito (0 a 9): \d. Adicionando um + após essa seqüência, significa "uma ou mais ocorrências da seqüência precedente". Isto é, um ou mais dígitos. Note que isso implica o preenchimento obrigatório; é melhor que o caso do campo não preenchido (vazio) seja tratado à parte. Assim, temos a expressão regular completa para validar um número apenas composto por dígitos:
reDigits = /^\d+$/;
Apresentamos agora um exemplo mais complexo de uso de expressão regular para a validação de números reais em ponto flutuante (decimal). As expressões definidas permitem, como opcionais: sinal, separadores de milhar e casas decimais. Não é tratada neste exemplo a notação científica (com expoente).
Use o formulário de exemplo ao lado para validar um número real decimal. Escolha pela caixa de seleção o idioma usado na expressão regular de validação, que define os carateres usados como separador de milhar e casas decimais.
As expressões regulares utilizadas nesta validação estão apresentadas no quadro a seguir.
reDecimalPt = /^[+-]?((\d+|\d{1,3}(\.\d{3})+)(\,\d*)?|\,\d+)$/;
reDecimalEn = /^[+-]?((\d+|\d{1,3}(\,\d{3})+)(\.\d*)?|\.\d+)$/;
Agora, vamos entender esse emaranhado de símbolos. Apresentarei a explicação para a expressão regular em Português. A lógica é idêntica para Inglês, trocando apenas os separadores de milhar e decimal (vírgula e ponto).
  • [+-]? indica um caractere opcional (?), de sinal positivo ou negativo ([+-]).
  • A seguir, o número efetivo está agrupado em duas formas de apresentação possíveis: com uma parte inteira, ou apenas com a parte decimal; a sintaxe (forma1|forma2) representa a condição "ou" entre as duas formas.
  • Entender a 2ª forma, apenas com a parte decimal, é mais simples: \,\d+ significa um número iniciado por vírugula (\,) seguido de um ou mais (+) dígitos numéricos (\d). Isso significa que o número ,123 é aceito como válido, ficando o zero da parte inteira subentendido (0,123). Se esta forma não for desejada, basta retirar esta condição e deixar apenas a 1ª forma, que obriga uma parte inteira.
  • Na primeira forma, a parte decimal ao final é opcional. Portanto, ela é exatamente a 2ª forma, mas delimitada pelo operador ? que denota opcionalidade: (\,\d*)?.
  • Para a parte inteira, existe mais uma condição "ou", permitindo duas apresentações possíveis: com ou sem separador de milhar. Se a parte inteira não usar separador de milhar, deve consistir apenas de um ou mais dígitos: \d+.
  • Se houver separador de milhar (isto é, um ponto a cada grupo de 3 dígitos), a parte inteira deve iniciar por um a 3 dígitos — \d{1,3} — seguindo-se um ou mais (+) grupos de um ponto e 3 dígitos: (\.\d{3})+.
Note que a expressão foi montada de forma a não aceitar espaços antes ou depois do texto, nem entre o sinal e o número. Obviamente, isso e tudo o mais pode ser adaptado de acordo com a necessidade.
Finalizamos mostrando um caso específico bem comum da validação de números decimais: valores financeiros.
Use o formulário de exemplo ao lado para validar um valor financeiro.
O valor financeiro é uma simplificação do número decimal visto anteriormente, não permitindo sinal e exigindo sempre 2 casas decimais e separador de milhar obrigatório. Separadores de decimal e milhar estão no formato em português: vírgula e ponto, respectivamente. A expressão regular usada é a seguinte:
reMoeda = /^\d{1,3}(\.\d{3})*\,\d{2}$/;

Data

Use o formulário de exemplo ao lado para validar uma data. Escolha pela caixa de seleção a complexidade da expressão regular usada na validação.
As expressões regulares utilizadas nesta validação estão apresentadas no quadro a seguir. As expressões 4 e 5 estão exibidas em mais de uma linha apenas para melhor legibilidade, mas devem ser consideradas como em uma única linha cada.
reDate1 = /^\d{1,2}\/\d{1,2}\/\d{1,4}$/;
reDate2 = /^[0-3]?\d\/[01]?\d\/(\d{2}|\d{4})$/;
reDate3 = /^(0?[1-9]|[12]\d|3[01])\/(0?[1-9]|1[0-2])\/(19|20)?\d{2}$/;
reDate4 = /^((0?[1-9]|[12]\d)\/(0?[1-9]|1[0-2])|30\/(0?[13-9]|1[0-2])
|31\/(0?[13578]|1[02]))\/(19|20)?\d{2}$/;
reDate5 = /^((0[1-9]|[12]\d)\/(0[1-9]|1[0-2])|30\/(0[13-9]|1[0-2])
|31\/(0[13578]|1[02]))\/\d{4}$/;
Estas expressões regulares vão do mais simples ao mais completo, da seguinte forma:
  1. Simples — valida apenas o uso de dígitos, nas posições e quantidade certas: 1 a 2 dígitos para dia e para mês, 1 a 4 dígitos para ano.
  2. Média — testa os dígitos possíveis em cada posição: o primeiro dígito do dia, se houver, deve ser de 0 a 3 ([0-3]?\d); o primeiro dígito do mês, se houver, deve ser 0 ou 1 ([01]?\d); passamos a aceitar apenas 2 ou 4 dígitos para o ano.
  3. Avançada — garante as faixas de valores corretas para dias 1 a 31 ((0?[1-9]|[12]\d|3[01])) e meses 1 a 12 ((0?[1-9]|1[0-2])). E aqui optamos por forçar os 2 primeiros dígitos do ano (correspondentes ao século), quando fornecidos, a serem 19 ou 20 ((19|20)?\d{2}).
  4. Completa — valida os dias permitidos de acordo com o mês. Para este último, foram criados três grupos alternativos de pares dia/mês:
    • Os dias 1 a 29 ((0?[1-9]|[12]\d)) são aceitos em todos os meses (1 a 12): (0?[1-9]|1[0-2])
    • Dia 30 é válido em todos os meses, exceto fevereiro (02): (0?[13-9]|1[0-2])
    • Dia 31 é permitido em janeiro (01), março (03), maio (05), julho (07), agosto (08), outubro (10) e dezembro (12): (0?[13578]|1[02]).
  5. Tradicional — data no formato DD/MM/AAAA, basicamente é a data Completa, porém sem a opcionalidade do zero à esquerda no dia ou mês menor que 10 e sem a opcionalidade e verificação de século no ano, aceitando qualquer seqüência de 4 dígitos (\d{4}) como ano.
A única coisa que a expressão mais completa (e complexa) não é capaz de testar é a validade do dia 29/fev apenas para anos bissextos.
A obrigatoriedade ou não de dois dígitos no dia e no mês é facilmente controlada com a ausência ou não do operador de opcionalidade ? após o primeiro dígito. As expressões apresentadas também ofereceram diversas possibilidades para a validação dos dígitos do ano.

Tempo

Use o formulário de exemplo ao lado para validar um horário ou tempo. Escolha pela caixa de seleção a complexidade da expressão regular usada na validação.
As expressões regulares utilizadas nesta validação estão apresentadas no quadro a seguir. As possibilidades de variação para horário (hora/minutos/etc. de um dia) ou tempo (período de tempo decorrido, independente de dia) são ilimitadas. Exemplificamos aqui alguns casos típicos, que podem ser combinados e alterados conforme a necessidade.
reTime1 = /^\d{2}:\d{2}$/;
reTime2 = /^([0-1]\d|2[0-3]):[0-5]\d$/;
reTime3 = /^(0[1-9]|1[0-2]):[0-5]\d$/;
reTime4 = /^\d+:[0-5]\d:[0-5]\d$/;
reTime5 = /^\d+:[0-5]\d:[0-5]\.\d{3}\d$/;
  1. Horário HH:MM simples — aceita dois pares de dois dígitos separados por dois-pontos (:); pode ser usado para hora:minutos ou ainda para minutos:segundos, porém não valida as faixas de valor válidos para os dígitos, aceitando assim qualquer valor entre 00 e 99.
  2. Horário HH:MM 24h — aceita horas na faixa 00-23 e minutos 00-59, separados por dois-pontos.
  3. Horário HH:MM 12h — aceita horas na faixa 01-12 e minutos 00-59, separados por dois-pontos.
  4. Tempo horas:MM:SS — aceita qualquer quantidade de horas (0 ou mais, um ou mais dígitos), minutos 00-59 e segundos 00-59, todos separados por dois-pontos.
  5. Tempo horas:MM:SS.mili — similar ao anterior, porém inclui 3 dígitos finais para milisegundos 000-999, separados dos anteriores por ponto (.).

E-mail

Use o formulário de exemplo ao lado para validar um endereço de e-mail (correio eletrônico).
As expressões regulares utilizadas nesta validação estão apresentadas no quadro a seguir. As expressões estão exibidas em mais de uma linha apenas para melhor legibilidade, mas devem ser consideradas como em uma única linha cada.
reEmail1 = /^[\w!#$%&'*+\/=?^`{|}~-]+(\.[\w!#$%&'*+\/=?^`{|}~-]+)*
@(([\w-]+\.)+[A-Za-z]{2,6}|\[\d{1,3}(\.\d{1,3}){3}\])$/;
reEmail2 = /^[\w-]+(\.[\w-]+)*@(([\w-]{2,63}\.)+[A-Za-z]{2,6}
|\[\d{1,3}(\.\d{1,3}){3}\])$/;
reEmail3 = /^[\w-]+(\.[\w-]+)*@(([A-Za-z\d][A-Za-z\d-]{0,61}[A-Za-z\d]\.)+
[A-Za-z]{2,6}|\[\d{1,3}(\.\d{1,3}){3}\])$/;
A principal especificação para regras endereço de correio eletrônico (e-mail) é o padrão Internet RFC 2822: Internet Message Format, seção 3.4.1 sobre especificação de endereços. Basicamente, o endereço segue a forma nome-local@domínio, onde nome-local especifica o usuário, e domínio especifica o seu endereço ou servidor.
As regras de nome-local são definidas de forma muito ampla na RFC 2822 e portanto são bastante dependentes de restrições específicas adotadas por cada servidor de domínio e tecnologia implementada. Pela especificação, um nome pode ser uma ou mais "palavras atômicas" sem espaço, contendo caracteres dentre letras (maiúsculas/minúsculas), dígitos (0-9) e os símbolos ! # $ % & ' * + - / = ? ^ ` { | } ~; as palavras são separadas entre si por ponto (.).
Um domínio pode ser especificado por nome ou por número IP. O nome de domínio tem a sintaxe geral subdomínio(s).nome-principal.TLD. Subdomínio(s), separados por ponto, são opcionais. Os terminadores de domínio — conhecidos como domínio primário, nível-raiz, nível-topo ou simplesmente pela sigla TLD (Top-Level Domain) — são definidos pelo organismo internacional Internet Assigned Number Authority (IANA) e podem ser:
As regras de nome de domínio principal variam de país para país; a grande maioria aceita de 2 a 63 caracteres, que podem ser letras, algarismos ou o hífen (desde que este não seja nem o primeiro nem o último caractere), segundo a recomendação de sintaxe especificada nas RFC 1034 (3.5) e RFC 1035 (2.3.1). Não é considerada diferenciação entre letras maiúsculas e minúsculas. Navegadores Internet antigos podem ter problema em reconhecer nomes de domínio com mais de 26 caracteres; por isso, vários países (inclusive o Brasil) limitam nomes a este tamanho. Com algumas restrições, podem ser aceitos nomes com caracteres internacionais (acentuados, Unicode etc.).
Na especificação de um domínio no endereço de e-mail, também pode ser aceito um número IP diretamente, entre colchetes, na forma [n1.n2.n3.n4] onde n1 a n4 são números entre 0 e 255.
As especificações e regras para endereços de correio eletrônico e nomes de domínio são amplas, imprecisas e cheias de situações específicas, de forma que uma expressão regular de validação pode ser desde muito flexível até bastante restritiva, conforme a necessidade.
Nos exemplos de expressão regular acima, temos:
  1. Livre — ReEmail1 aceita nome-local com todos os caracteres permitidos na RFC 2822: [\w!#$%&'*+/=?^`{|}~-]; e o domínio tem definição bem livre, por nome basicamente fixando apenas que o TLD deve ter entre 2 e 6 caracteres: [A-Za-z]{2,6}; ou por número IP entre colchetes: \[\d{1,3}(\.\d{1,3}){3}\].
  2. Compacto — ReEmail2 limita os caracteres permitidos no nome-local de forma mais compacta e restritiva, porém cobre os casos mais comuns. Aceita como nome-local uma ou mais palavras separadas por ponto ([\w-]+(\.[\w-]+)*), onde cada palavra é definida por [\w-]+ permitindo assim letra, dígito, sublinhado e hífen. Também limita o tamanho de nomes de domínio entre 2 e 63 caracteres apenas com letras, dígitos, sublinhado e hífen: [\w-]{2,63}.
  3. Restrito — ReEmail3 é uma variação da ReEmail2, mas força nomes de domínio entre 2 e 63 caracteres, deixa de usar a seqüência \w para não permitir o sublinhado e garante que não há hífen nem na primeira nem na última posição, conforme RFC 1034/1035. O resultado é o seguinte para representar um nome de domínio: [A-Za-z\d][A-Za-z\d-]{0,61}[A-Za-z\d].
Uma última dica: para os domínios como número IP, foram usadas seqüências de 1 a 3 dígitos (\d{1,3}) para cada um dos 4 números componentes do IP. Para validar precisamente cada número entre 0 e 255, o padrão de expressão regular poderia ser ([01]?\d?\d|2[0-4]\d|25[0-5]). Fica como "exercício" para o leitor construir uma expressão que represente um TLD apenas de duas letras (país), ou um dos TLDs genéricos ou de infra-estrutura atualmente existentes.

Regra geral

Função de Validação

Escolhida a expressão regular mais adequada para a validação de certo tipo de dado, criar uma função JavaScript de validação para esse tipo fica trivial assim:
function isTipo(pVal)
{
var reTipo = /^...$/; // Onde ... é a expressão regular apropriada
return reTipo.test(pVal);
}

Expressões Regulares

Para entender e construir melhor expressões regulares adequadas a cada necessidade, sugiro as seguintes referências em português:
E estas em inglês:

Considerações finais

É importante ressaltar que a validação de formulários que são submetidos ao servidor web jamais deve depender apenas de JavaScript no lado cliente, pois não se pode assegurar que o recurso esteja ativado e funcionando corretamente em todos os navegadores utilizados no acesso. Implementada ou não uma validação em JavaScript, deve sempre haver uma validação dos dados recebidos no servidor de aplicação. O uso de validação via JavaScript cliente serve essencialmente como um facilitador para pré-validação no lado cliente, possibilitando que o usuário tenha seus dados verificados ainda antes de submeter o formulário, evitando assim ter de aguardar o processamento pelo servidor para só então ser informado de que eventualmente errou algo e deve corrigir.
Expressão regular é um recurso também disponível em linguagens utilizadas em servidores de aplicação web, como Java, ASP.NET, PHP, Perl e outras, de forma que essa técnica pode ser usada analogamente para validação no lado servidor, observadas as diferenças de sintaxe e implementação de expressão regular em cada linguagem.
Além da validação de máscaras/formatos de texto como visto aqui, expressões regulares são também muito úteis na própria aplicação ou remoção de máscaras e formatação de texto. Veja um exemplo disto no artigo sobre Tratamento de CPF e CNPJ em JavaScript.

Fonte: Márcio d'Ávila, 21 de outubro de 2003. Revisão 5, 23 de junho de 2007.