Adicionando máscaras nos inputs HTML (JS vanilla)
Tags: javascript, html, ux, tutorial
Published: 2021-03-01 23:25:03

[UPDATE] Na época da publicação, o código possuia um erro de funcionalidade que o [@vitorluizc](https://dev.to/vitorluizc) apontou. Agradeço demais pela observação, no que corrigi o código.  

Olá a todos!
Neste post pretendo ensinar a inserir máscaras nos inputs HTML, sem uso de bibliotecas externas e compatível com a maioria dos frameworks javascript - se não com todos eles.

##1. O que é uma máscara?
Uma máscara consiste na estilização das informações inseridas pelo usuário no input. isso facilita a leitura e, em nosso exemplo, dará a certeza de que os dados foram inseridos no tamanho correto.
 
##2. Mas se a lib [INSIRA O NOME DE UMA BIBLIOTECA AQUI] faz esse trabalho pra mim, por quê eu deveria criar uma máscara "na mão"?

É uma pergunta muito interessante, a qual comporta várias respostas.

Em primeiro lugar, vale a pena criar sua própria máscara porque, como veremos abaixo, ela se adapta a diversos tipos de input - e muitas libs comportam somente os campos mais comuns, como CPF ou telefone. É muito difícil achar uma lib que, sozinha, crie as máscaras pra todos os campos desejados.

Em segundo lugar, há sempre um grande debate sobre o uso de lib pra tudo. Eu sempre levo em consideração que menos é mais, por isso evito ao máximo o uso de bibliotecas externas, usando-as somente em casos muito específicos em que ela facilita minha vida com uma funcionalidade de lógica muito complexa ou quando ela já possui uma construção sólida, caso da [Yup](https://www.npmjs.com/package/yup).

Em terceiro lugar, é uma boa experiência de aprendizado e manipulação de objetos.

##3. Passadas essas considerações, mãos à obra!

Vamos começar criando um HTML dando olá ao mundo (não acredito em maldições, mas é melhor não contrariar). Teremos também um campo de input destinado ao CPF do usuário:
```
<!DOCTYPE html>
<html lang="pt-br">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mask input</title>
</head>
<body>
  <h1>Hello World</h1>
  <div>
      <label>digite o CPF / insert CPF:</label>
      <input id="CPFInput" maxlength="14">
  </div>
</body>
</html>
```
Atente que o nosso input possui o atributo `maxlength`. Ele é o grande responsável por limitar o campo para que sejam inseridos os caracteres que o nosso campo usa, bem como eventuais traços e pontos - que serão exibidos posteriormente.

Também criamos uma `id` para nosso `input`. Isso será importante para os passos posteriores.

Feito isso, vamos criar a tag `script` e inserir nossa função dentro do input:

```
  <h1>Hello World</h1>
  <div>
    <label>digite o CPF / insert CPF:</label>
    <input id="CPFInput" maxlength="14" oninput="criaMascara()">
    </div>
</body>
<script>
  function criaMascara(){
    console.log('algo foi digitado!')
  };
</script>
</html>
```
(_ocultei algumas linhas pra não ficar repetitivo_)

Ao implementarmos a função `criaMascara`, note que, sempre que algo for digitado dentro do input, surgirá no console a frase `algo foi digitado!`.

Agora vamos apagar esse teste. Nossa manipulação do DOM começa agora.

Vamos começar passando um parâmetro pra nossa função `criaMascara` - neste caso, será a string `'CPF'`. Dentro da função, manipularemos o DOM a partir dessa variável. Observe:

```
      <input id="CPFInput" maxlength="14" oninput="criaMascara('CPF')">
  </div>
</body>
<script>
  function criaMascara(mascaraInput) {
    const tamanhoInput = document.getElementById(`${mascaraInput}Input`).maxLength;
    let valorInput = document.getElementById(`${mascaraInput}Input`).value;
   console.log('tamanho máximo:', tamanhoInput, 'valor do input:', valorInput)
  };
</script>
```
Graças ao uso das [Template literals](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Template_literals), podemos selecionar um elemento do DOM dinamicamente e aplicar esta função para todo o nosso código, bastando dar uma ID com o mesmo padrão da CPFInput - algo como telefoneInput, CNPJInput, CEPInput e etc. E ao digitarmos qualquer coisa, teremos o valor do `input` e o atributo `maxLength` do nosso input escrito no console.

Feitos esses passos, vamos criar um objeto que conterá o formato da nossa máscara, a qual será aplicada tão logo o nosso `input` tiver o tamanho igual ao atributo `maxLength`:

```
<script>
  function criaMascara(mascaraInput) {
    const maximoInput = document.getElementById(`${mascaraInput}Input`).maxLength;
    let valorInput = document.getElementById(`${mascaraInput}Input`).value;
    const mascaras = {
      CPF: valorInput.replace(/[^\d]/g, "").replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4")
    };

    if (valorInput.length === maximoInput) {
      (document.getElementById(`${mascaraInput}Input`).value = mascaras[mascaraInput])
    }
  };
</script>
```
Se você for familiarizado com ternários, pode utilizar essa sintaxe:
```
<script>
  function criaMascara(mascaraInput) {
    const maximoInput = document.getElementById(`${mascaraInput}Input`).maxLength;
    let valorInput = document.getElementById(`${mascaraInput}Input`).value;
    const mascaras = {
      CPF: valorInput.replace(/[^\d]/g, "").replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4")
    };

    valorInput.length === maximoInput && (document.getElementById(`${mascaraInput}Input`).value = mascaras[mascaraInput]);
  };
</script>
```

Chamamos a função SEMPRE que o `input` é alterado, mas, como dissemos acima, ela somente fará algo quando o tamanho do nosso input for igual à propriedade `maxLength`.

No caso concreto, quando nosso input tiver exatos 11 caracteres, o valor do input será igual à chave CPF da nossa variável `mascaras` - ou seja, nosso `input` será afetado por duas funções [replace](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/String/replace).

**[UPDATE] Todavia, precisamos nos certificar que, quando o tamanho do nosso input sem caracteres especiais for menor que o valor máximo, os pontos e traços deverão ser removidos da string. Por isso, tratei de criar uma nova variável chamada _valorSemPonto_ e acrescentei uma nova condição ao nosso ternário (equivalente a um `else`):**

```
<script>
  function criaMascara(mascaraInput) {
    const maximoInput = document.getElementById(`${mascaraInput}Input`).maxLength;
    let valorInput = document.getElementById(`${mascaraInput}Input`).value;
    let valorSemPonto = document.getElementById(`${mascaraInput}Input`).value.replace(/([^0-9])+/g, "");
    const mascaras = {
      CPF: valorInput.replace(/[^\d]/g, "").replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4")
    };

    valorInput.length === maximoInput ? document.getElementById(`${mascaraInput}Input`).value = mascaras[mascaraInput] : document.getElementById(`${mascaraInput}Input`).value = valorSemPonto;
  };
</script>
```
**a variável `valorSemPonto` nada mais é do que uma substituição de todos os caracteres não numéricos por nada - na prática, estamos removendo qualquer coisa que não seja um número**

Graças às REGExp que implementamos em cada função, o primeiro `replace` removerá todos os caracteres não numéricos. O segundo aplicará os pontos após cada grupo de três números e, em seguida, um traço antes dos dois últimos números.

E sua máscara está pronta, reaproveitando tudo com apenas uma função e usando um objeto ao invés de um monte de `if`, reduzindo sobremaneira a complexidade do código!

Agora você pode criar quantos inputs quiser, e no formato que quiser, basta atentar pro nome do `id` de cada `input`, atribuir um número máximo de caracteres com `maxLength` e inserir a REGExp adequada. Abaixo temos o código pronto com mais três exemplos (clique em HTML para ver os demais inputs).

{% codepen https://codepen.io/laurolyra/pen/KKNRaVo %}

Espero que tenham gostado desta postagem. Até a próxima!