Criando um validador de senha em JavaScript

Como montar um medidor de força de senha com HTML, CSS e JavaScript puro, validando maiúsculas, minúsculas, números, caracteres especiais e tamanho mínimo com expressões regulares.


Validar senha parece complicado até você abrir o código e ver que é basicamente uma lista de regras sendo checadas uma por uma. Aqui a base é um HTML e um CSS simples, só para dar suporte visual, e o JavaScript inteiro construído do zero, regra por regra, até chegar numa barra de progresso que mostra o quanto da senha já está válida.

O que o validador precisa fazer

O objetivo final: conforme o usuário digita, quatro regras vão sendo marcadas como cumpridas ou não, e uma barra de progresso preenche proporcionalmente. Quando as quatro regras estão satisfeitas, a senha é considerada válida e dá para liberar o que vier depois, enviar o formulário, habilitar um botão, o que fizer sentido pro seu caso.

As quatro regras:

  • Pelo menos uma letra maiúscula e uma minúscula.
  • Pelo menos um número.
  • Pelo menos um caractere especial.
  • Pelo menos seis caracteres no total.

Selecionando os elementos

Primeiro, o campo de senha e a barra de progresso, os dois elementos que o JavaScript vai manipular o tempo todo:

const passwordInput = document.querySelector(".password");
const progressBar = document.querySelector(".progress .bar");

E o gatilho de tudo: sempre que o usuário soltar uma tecla dentro do campo, a validação roda de novo:

passwordInput.addEventListener("keyup", checkPassword);

Calculando a força regra por regra

Dentro de checkPassword, a lógica começa com a força zerada e vai somando um ponto para cada regra satisfeita:

function checkPassword() {
  let strength = 0;
  const value = passwordInput.value;
 
  strength += renderRule(".lower-upper-case", value.match(/[a-z].*[A-Z]|[A-Z].*[a-z]/));
  strength += renderRule(".one-number", value.match(/[0-9]/));
  strength += renderRule(".one-special-char", value.match(/[!@#$%^&*_]/));
  strength += renderRule(".min-char", value.length >= 6);
 
  const pct = Math.floor((strength / 4) * 100);
  progressBar.style.width = `${pct}%`;
 
  // liberar envio, mostrar botão, etc, quando pct === 100
}

A primeira regra usa duas expressões regulares unidas por | (ou), uma checando minúscula seguida de maiúscula em qualquer ordem e outra checando o inverso, porque não importa qual vem primeiro, só importa que as duas apareçam. As outras duas regras usam expressões bem mais diretas: um intervalo de números de 0 a 9 e uma lista de caracteres especiais entre colchetes. A última regra nem precisa de regex, é só conferir o length da string.

A função que pinta a regra de verde ou vermelho

renderRule recebe a classe do elemento da regra e o resultado da verificação, e faz duas coisas: muda a cor da bolinha na tela e devolve 1 ou 0 para entrar na soma da força:

function renderRule(ruleClass, valid) {
  const ruleArea = document.querySelector(ruleClass);
  ruleArea.querySelector(".valid").style.backgroundColor = valid ? "green" : "red";
 
  return valid ? 1 : 0;
}

Cada regra no HTML tem uma classe própria (.lower-upper-case, .one-number, .one-special-char, .min-char) e, dentro dela, um elemento .valid que é a bolinha colorida. match retorna um resultado truthy quando encontra a expressão regular no texto e null quando não encontra, então dá para usar o retorno direto como o valid da função, sem precisar de um if separado para cada regra.

A barra de progresso

Com quatro regras no total, cada uma vale 25% da barra. O cálculo é simples: força dividida pelo total de regras, vezes 100, arredondado para baixo com Math.floor para garantir que só bate 100% quando as quatro regras realmente estão completas:

const pct = Math.floor((strength / 4) * 100);
progressBar.style.width = `${pct}%`;

Arredondar para baixo evita que 99.9% apareça como barra cheia sem a senha estar de fato válida.

Resultado

Com isso, o campo vai ganhando cor nas regras conforme o usuário digita e a barra de progresso acompanha em tempo real. A partir do pct === 100, dá para tomar qualquer decisão: liberar um botão de envio, desbloquear o próximo passo de um cadastro, mostrar uma mensagem de sucesso. A lógica em si, checar regra por regra e somar uma força, é a parte que vale levar para qualquer linguagem ou framework, independente de como a interface é montada.