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.
Para ler o que o usuário digitou, você pega o elemento e lê a propriedade
value:
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.
const cep = document.querySelector('#cep').value;
console.log(cep, typeof cep);
const quantidade = document.querySelector('#quantidade').value;
console.log(quantidade, typeof quantidade);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
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);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
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));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
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);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:
const tipoFrete = document.querySelector('input[name="frete-tipo"]:checked');
console.log('frete escolhido :', tipoFrete ? tipoFrete.value : null);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
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);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:
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));selectedOptions é uma HTMLCollection, não um array — daí o [...] antes do
map.
Upload: files, e o value mente
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:
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
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 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')
const campo = document.querySelector('#cep');
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
const preco = document.querySelector('#preco').value;
const frete = document.querySelector('#frete').value;
console.log('Total: R$ ' + (preco + frete));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:
const cupom = document.querySelector('#cupom');
console.log('propriedade:', cupom.value);
console.log('atributo :', cupom.getAttribute('value'));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:
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”:
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. nameem todo campo, mesmo que você useidpara pegar. É onameque vai para o servidor e o que fazFormDatafuncionar..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.
DevClub no YouTubeCONVERSOR DE MOEDAS com HTML, CSS e JAVASCRIPTAssistir a aula
DevClub no YouTubeComo criar um To Do List com HTML, CSS e JavaScript | IniciantesAssistir a aula
Perguntas frequentes
Por que meu input type=number devolve string?
Devo ler o valor no evento input ou no change?
Qual a diferença entre value e o atributo value do HTML?
Preciso do id para pegar o valor de um campo?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
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
- MDN — HTMLInputElement — developer.mozilla.org
- MDN — O elemento input — developer.mozilla.org
- HTML Standard — The input element — html.spec.whatwg.org


