Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Como pegar o valor de um input com JavaScript

value é sempre string, checked resolve checkbox e radio, files carrega upload e valueAsNumber evita a concatenação — cada caso com a saída real no terminal.

Rodolfo Mori6 min de leitura

Para ler o que o usuário digitou, você pega o elemento e lê a propriedade value:

js
const cep = document.querySelector('#cep').value;

Só que value não serve para tudo. Checkbox e radio guardam a resposta em checked; campo de arquivo guarda em files; e num type="number" o value continua sendo texto — o que transforma soma em concatenação. Esta lição percorre cada um desses casos com o formulário de checkout de uma loja.

Cada campo guarda a resposta em uma gaveta diferente

Num formulário de papel, texto fica escrito na linha, uma opção fica marcada e um anexo vem preso à folha. Na página acontece a mesma separação: o texto mora em value, a marcação em checked e os anexos em files. Procurar tudo na mesma gaveta produz um valor enganoso.

Essas gavetas são propriedades específicas dos elementos do DOM. Mesmo no input type="number", value continua sendo string; a conversão é uma decisão do seu código. Na primeira conta do checkout, preveja o resultado mantendo o tipo string e depois repita mentalmente após Number(). A saída deixa visível o ponto exato em que concatenação vira soma.

js
const cep = document.querySelector('#cep').value;
console.log(cep, typeof cep);

const quantidade = document.querySelector('#quantidade').value;
console.log(quantidade, typeof quantidade);
01310100 string 3 string

O #quantidade é um <input type="number"> com 3 digitado. typeof diz string. O type do HTML controla o teclado do celular, as setinhas de incremento e a validação nativa — não o tipo do valor em JavaScript.

Isso é uma decisão da especificação, não um acidente: um campo numérico pode estar vazio, pode estar com 1e5 a meio digitar, pode estar com --. String representa todos esses estados; número, não.

A conta que vira texto

js
const preco = document.querySelector('#preco').value;       // 89.90
const quantidade = document.querySelector('#quantidade').value; // 3

console.log('soma:', preco + quantidade);
console.log('multiplicacao:', preco * quantidade);
soma: 89.903 multiplicacao: 269.70000000000005

O + viu duas strings e fez o que faz com strings: grudou uma na outra. O * não tem versão para texto, então o JavaScript converteu os dois para número e multiplicou. É o mesmo comportamento assimétrico de operadores aritméticos, agora com o formulário como fonte do problema.

O total ficou 269.70000000000005 porque é ponto flutuante binário, não porque veio do input. Para dinheiro, formate na exibição com toFixed(2).

Três formas de virar número — e a diferença entre elas

js
const quantidade = document.querySelector('#quantidade'); // "3"
const preco = document.querySelector('#preco');           // "89.90"
const vazio = document.querySelector('#vazio');           // ""
const desconto = document.querySelector('#desconto');     // "10 reais"

console.log('valueAsNumber:', quantidade.valueAsNumber, preco.valueAsNumber);
console.log('total:', preco.valueAsNumber * quantidade.valueAsNumber);
console.log('campo vazio -> valueAsNumber:', vazio.valueAsNumber);
console.log('campo vazio -> Number(value):', Number(vazio.value));
console.log('desconto -> Number:', Number(desconto.value), '| parseFloat:', parseFloat(desconto.value));
valueAsNumber: 3 89.9 total: 269.70000000000005 campo vazio -> valueAsNumber: NaN campo vazio -> Number(value): 0 desconto -> Number: NaN | parseFloat: 10

Três resultados diferentes para o campo vazio e para o texto sujo. É aqui que a escolha importa:

forma campo vazio "10 reais" disponível em
campo.valueAsNumber NaN NaN number, range, date
Number(campo.value) 0 NaN qualquer campo
parseFloat(campo.value) NaN 10 qualquer campo

Number('') devolver 0 é a armadilha: um campo em branco vira zero, o total fecha em R$ 0,00 e ninguém percebe que o usuário não preencheu nada. valueAsNumber devolve NaN nesse caso, que é a resposta honesta — e NaN você consegue detectar com Number.isNaN.

Checkbox e radio: checked, não value

js
const presente = document.querySelector('#presente'); // marcado
const nota = document.querySelector('#nota');         // desmarcado

console.log('presente.value  :', presente.value);
console.log('presente.checked:', presente.checked);
console.log('nota.value      :', nota.value);
console.log('nota.checked    :', nota.checked);
presente.value : sim presente.checked: true nota.value : sim nota.checked : false

Os dois têm o mesmo value, porque value num checkbox é o rótulo que vai para o servidor se ele estiver marcado. Quem responde “o usuário marcou?” é checked, um booleano de verdade.

Radio é um grupo, e o grupo se identifica pelo name. O jeito direto de saber qual foi escolhido é pedir o marcado no próprio seletor:

js
const tipoFrete = document.querySelector('input[name="frete-tipo"]:checked');
console.log('frete escolhido :', tipoFrete ? tipoFrete.value : null);
frete escolhido : sedex

O tipoFrete ? ... : null não é preciosismo: se nenhum radio estiver marcado, querySelector devolve null e o .value direto quebra.

select: value, selectedOptions e o texto visível

js
const uf = document.querySelector('#uf');
console.log('sem escolha    :', JSON.stringify(uf.value), '| índice:', uf.selectedIndex);

uf.value = 'RJ';
console.log('uf.value       :', uf.value);
console.log('texto visível  :', uf.selectedOptions[0].textContent);
console.log('índice         :', uf.selectedIndex);
sem escolha : "" | índice: 0 uf.value : RJ texto visível : Rio de Janeiro índice : 2

select.value devolve o value da opção marcada — "RJ", não "Rio de Janeiro". Quando você precisa do texto que o usuário viu (para uma mensagem de confirmação, por exemplo), ele está em selectedOptions[0].textContent.

Com multiple, value devolve só o primeiro. A lista inteira sai de selectedOptions:

js
const brindes = document.querySelector('#brindes'); // caneca e camiseta marcadas

console.log('brindes.value    :', brindes.value);
console.log('selectedOptions  :', brindes.selectedOptions.length);
console.log('todos os marcados:', [...brindes.selectedOptions].map((o) => o.value));
brindes.value : caneca selectedOptions : 2 todos os marcados: [ 'caneca', 'camiseta' ]

selectedOptions é uma HTMLCollection, não um array — daí o [...] antes do map.

Upload: files, e o value mente

js
const campo = document.querySelector('#comprovante');

campo.addEventListener('change', () => {
  console.log('arquivos:', campo.files.length);
  for (const arquivo of campo.files) {
    console.log(arquivo.name, '|', arquivo.type, '|', arquivo.size, 'bytes');
  }
});

Escolhendo um arquivo:

arquivos: 1 comprovante-pix.png | image/png | 23 bytes

campo.files é uma FileList com um objeto File por arquivo escolhido — nome, tipo MIME e tamanho em bytes, tudo antes de subir qualquer coisa para o servidor. É com isso que você valida “só imagem, até 2 MB” no navegador.

O value de um campo de arquivo não é o caminho real: por segurança, os navegadores devolvem C:\fakepath\comprovante-pix.png em qualquer sistema operacional. Não tente extrair o caminho dali.

Quando ler: input ou change

js
const busca = document.querySelector('#busca');

busca.addEventListener('input', (e) => console.log('input :', JSON.stringify(e.target.value)));
busca.addEventListener('change', (e) => console.log('change:', JSON.stringify(e.target.value)));

Digitando tec e depois saindo do campo:

input : "t" input : "te" input : "tec" change: "tec"

input disparou três vezes, uma por tecla; change disparou uma só, quando o campo perdeu o foco. A regra prática: input para o que é barato (contador de caracteres, habilitar botão, filtrar lista local), change para o que é caro (consultar CEP, salvar rascunho no servidor).

Ler o valor por evento.target.value em vez de por campo.value deixa o handler reaproveitável entre vários campos — é o mesmo target da lição sobre o objeto event.

Erros comuns

Cannot read properties of null (reading 'value')

js
const campo = document.querySelector('#cep');
console.log(campo.value);
file:///private/tmp/loja/frete.mjs:5 console.log(campo.value); ^

TypeError: Cannot read properties of null (reading ‘value’) at file:///private/tmp/loja/frete.mjs:5:19 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

O HTML tinha id="cep-entrega". querySelector não achou nada, devolveu null, e null.value explodiu. As duas causas de sempre: seletor errado, ou script rodando antes do HTML existir — o que se resolve com defer no <script>. O erro completo, com as variações, está em TypeError: Cannot read properties of undefined.

Repare no ponto do acento circunflexo: ele aponta para .value, não para campo. O erro é ler uma propriedade de null, e a mensagem diz qual propriedade — o que já te leva à linha certa.

O total do carrinho vira uma string enorme

js
const preco = document.querySelector('#preco').value;
const frete = document.querySelector('#frete').value;
console.log('Total: R$ ' + (preco + frete));
Total: R$ 89.9012.50

Nenhum erro, nenhum aviso — só um total absurdo na tela. Converta na leitura, não na exibição: Number(campo.value) ou campo.valueAsNumber na hora de guardar a variável, e o resto do código volta a trabalhar com números.

Ler getAttribute('value') em vez da propriedade

O HTML nasceu com <input id="cupom" value="FRETE5"> e o usuário trocou o conteúdo por FRETE10:

js
const cupom = document.querySelector('#cupom');

console.log('propriedade:', cupom.value);
console.log('atributo   :', cupom.getAttribute('value'));
propriedade: FRETE10 atributo : FRETE5

O atributo é o valor inicial escrito no HTML e não se mexe mais; a propriedade acompanha a digitação. Para ler o que o usuário digitou, é sempre a propriedade. (Se o HTML não trouxer value nenhum, getAttribute devolve null, e aí o bug fica ainda mais confuso.)

O padrão que resolve o formulário inteiro

Quando são muitos campos, parar de escrever um querySelector por campo economiza código e erro de digitação:

js
const form = document.querySelector('#checkout');

form.addEventListener('submit', (evento) => {
  evento.preventDefault();
  const dados = Object.fromEntries(new FormData(form));
  console.log(dados);
});

Enviando o formulário com preço, frete, cupom, o checkbox de presente desmarcado e o radio em “sedex”:

{ preco: '89.90', frete: '12.50', cupom: 'FRETE10', 'frete-tipo': 'sedex' }

FormData lê todos os campos com name, já respeitando checkbox desmarcado (ausente) e radio (só o marcado). O que ele não faz é converter tipo: tudo continua string, e a conversão é sua.

Três hábitos que evitam a maior parte dos bugs desta lição:

  • Converta na entrada. Uma linha const qtd = campo.valueAsNumber; no topo, e o resto do arquivo nunca mais vê string.
  • name em todo campo, mesmo que você use id para pegar. É o name que vai para o servidor e o que faz FormData funcionar.
  • .trim() em campo de texto antes de comparar ou salvar. Espaço no fim de um CEP colado da internet é rotina.

A próxima lição pega esses valores e decide se eles servem: validar formulário com JavaScript, com a API de validação que o navegador já traz pronta. A trilha inteira está no guia completo de JavaScript.

Prefere aprender em vídeo?

Tem aula sobre este assunto no nosso canal.

Ver todos os vídeos do canal
  • input
  • value
  • formulario
  • dom
  • checkbox

Perguntas frequentes

Por que meu input type=number devolve string?
Porque o atributo type controla o teclado do celular, as setinhas e a validação — não o tipo em JavaScript. A propriedade value é definida como string na especificação do HTML. Para número, use valueAsNumber.
Devo ler o valor no evento input ou no change?
input dispara a cada tecla e serve para busca ao vivo e contador de caracteres. change dispara quando o campo perde o foco com o valor alterado, e serve para o que custa caro, como consultar CEP numa API.
Qual a diferença entre value e o atributo value do HTML?
O atributo é o valor inicial e fica congelado no HTML; a propriedade acompanha o que o usuário digitou. Depois da primeira digitação os dois divergem, e getAttribute('value') passa a devolver informação velha.
Preciso do id para pegar o valor de um campo?
Não. Dentro de um formulário, form.elements.nomeDoCampo já devolve o elemento pelo atributo name — e name é obrigatório para o campo ser enviado ao servidor de qualquer jeito.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN — HTMLInputElement — developer.mozilla.org
  2. MDN — O elemento input — developer.mozilla.org
  3. HTML Standard — The input element — html.spec.whatwg.org

Continue por aqui