Máscara de CPF com JavaScript Puro (Sem Dependências)
Função JavaScript pura para formatar CPF no padrão XXX.XXX.XXX-XX em tempo real, como a usada na página de geração de CPF. Sem jQuery, sem dependências, apenas a API nativa do DOM.
Código
function formatCPF(value) {
var digits = value.replace(/\D/g, '').slice(0, 11);
if (digits.length <= 3) return digits;
if (digits.length <= 6) return digits.slice(0, 3) + '.' + digits.slice(3);
if (digits.length <= 9)
return digits.slice(0, 3) + '.' + digits.slice(3, 6) + '.' + digits.slice(6);
return (
digits.slice(0, 3) +
'.' +
digits.slice(3, 6) +
'.' +
digits.slice(6, 9) +
'-' +
digits.slice(9)
);
}
function applyMask(input) {
input.addEventListener('input', function () {
var pos = input.selectionStart;
var prev = input.value.length;
input.value = formatCPF(input.value);
var diff = input.value.length - prev;
input.setSelectionRange(pos + diff, pos + diff);
});
}Análise do código
formatCPF remove todos os caracteres não numéricos com replace(/\D/g, ''), limita a 11 dígitos com slice(0, 11) e insere pontos e traço de acordo com o comprimento da string. A formatação é progressiva: 3 dígitos → sem separador, 4 a 6 → primeiro ponto, 7 a 9 → segundo ponto, 10 a 11 → traço.
applyMask registra um listener no evento input. A cada digitação, salva a posição do cursor (selectionStart), aplica a formatação, e corrige a posição do cursor com a diferença de comprimento gerada pelos separadores inseridos. Isso evita que o cursor salte para o final do campo.
Uso
HTML:
<input type="text" id="cpf" inputmode="numeric" placeholder="000.000.000-00" />
<script>
applyMask(document.getElementById('cpf'));
</script>Resultado ao digitar:
formatCPF('529'); // "529"
formatCPF('529982'); // "529.982"
formatCPF('52998224725'); // "529.982.247-25"Também funciona para formatar um valor já existente:
formatCPF('52998224725'); // "529.982.247-25"
formatCPF('000.000.001-91'); // "000.000.001-91"Testes
Testes com Vitest ou Jest:
import { describe, it, expect } from 'vitest';
describe('formatCPF', () => {
it('retorna vazio para string sem dígitos', () => {
expect(formatCPF('')).toBe('');
expect(formatCPF('abc')).toBe('');
});
it('não formata até 3 dígitos', () => {
expect(formatCPF('5')).toBe('5');
expect(formatCPF('52')).toBe('52');
expect(formatCPF('529')).toBe('529');
});
it('insere primeiro ponto após 3 dígitos', () => {
expect(formatCPF('5299')).toBe('529.9');
expect(formatCPF('529982')).toBe('529.982');
});
it('insere segundo ponto após 6 dígitos', () => {
expect(formatCPF('5299822')).toBe('529.982.2');
expect(formatCPF('529982247')).toBe('529.982.247');
});
it('insere traço após 9 dígitos', () => {
expect(formatCPF('5299822472')).toBe('529.982.247-2');
expect(formatCPF('52998224725')).toBe('529.982.247-25');
});
it('limita a 11 dígitos', () => {
expect(formatCPF('529982247251234')).toBe('529.982.247-25');
});
it('remove caracteres não numéricos da entrada', () => {
expect(formatCPF('529.982.247-25')).toBe('529.982.247-25');
expect(formatCPF('abc529def982ghi')).toBe('529.982');
});
});Precisa de CPFs para testar a máscara? O gerador de CPF cria números válidos com ou sem formatação.
Veja também: validar CPF em JavaScript.