Capítulo 20
Projeto: calculadora
5 min de leitura 975 palavras 2 gravuras
Hora de juntar HTML, CSS e JavaScript numa peça só. A Casa da Restinga ganha uma calculadora de balcão: visual próximo ao telefone clássico, só inteiros. Sem %. Sem vírgula nem ponto decimal. O C zera tudo, como no carregamento da página. Esta é o capítulo 20 de 23.
O exemplo pronto está em exemplos/20-calculadora/. Abra index.html. Os botões são <button>. O layout das linhas é Flex. O cérebro está em calculadora.js.
Trabalhe na cópia inicio da etapa. Leia e digite o código; mude uma coisa. Antes de recarregar, anote sua previsão. Depois confira com a referência completa. Se nada mudar, confira o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo podem ser digitados no console; o exemplo completo mostra a aplicação daquela etapa.
O que você vai aprender
- Montar a grade com
buttone Flex, não comdivclicável. - Guardar a tela num buffer de texto.
- Converter com
parseIntna hora da conta. - Ouvir o pai dos botões com um
clicksó. - Fazer
Cvoltar ao estado inicial. - Quebrar o programa em funções pequenas.
O combinado do balcão
Regras desta calculadora:
- Visual escuro, operadores à direita, fonte monoespaçada na tela.
- Inteiros apenas. Não implemente
%nem.. Climpa buffer, total e operador. A tela volta a0.+,-,×e÷guardam a operação e esperam o próximo número.=fecha a conta.←é extra: apaga o último dígito. Se restar um, a tela vira0.- Depois do
=você pode seguir simples: a pessoa apertaCpara recomeçar.
A tela deve continuar dentro da página: números longos podem rolar dentro dela.
Pare e experimente. Abra o exemplo. Some 2 + 3 =. Preveja 5. Recarregue. Confira a tela.
Erro comum: começar pelo JavaScript com a grade ainda em div. Sem button, teclado e leitor de tela sofrem. Faça HTML e CSS primeiro.

HTML: button e linhas
Cada tecla é um <button type="button">. C ocupa duas colunas. 0 ocupa três. O script entra no fim do body.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Calculadora — Casa da Restinga</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="calc">
<section class="tela" aria-live="polite">0</section>
<section class="botoes">
<div class="linha">
<button type="button" class="duplo">C</button>
<button type="button">←</button>
<button type="button">÷</button>
</div>
<div class="linha">
<button type="button">7</button>
<button type="button">8</button>
<button type="button">9</button>
<button type="button">×</button>
</div>
<div class="linha">
<button type="button">4</button>
<button type="button">5</button>
<button type="button">6</button>
<button type="button">-</button>
</div>
<div class="linha">
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">+</button>
</div>
<div class="linha">
<button type="button" class="triplo">0</button>
<button type="button">=</button>
</div>
</section>
</main>
<script src="calculadora.js"></script>
</body>
</html>
Há outras marcações possíveis. Esta funciona e o exemplo a usa.
Pare e experimente. Tire o type="button" de uma tecla e recarregue dentro de um form de teste. Preveja um envio. Restaure type="button".
Erro comum: usar <div onclick> no lugar de button. O volume pede button.
CSS: Flex e calha
Cada .linha é display: flex com justify-content: space-between. Largura do botão comum: cerca de 24.5%, para caberem quatro. A calha (vão preto) nas laterais vem desse espaço. Embaixo, margin-bottom: 0.5%: margem percentual mede em relação à largura, então o vão inferior combina com o lateral.
.linha {
display: flex;
justify-content: space-between;
margin-bottom: 0.5%;
}
.botoes button {
width: 24.5%;
height: 100px;
font-size: 2rem;
}
.duplo { width: 49.7%; }
.triplo { width: 74.8%; }
.linha button:last-child {
background: #9f4b13;
color: #fff;
}
A tela usa monospace (ou "Courier New"), alinhada à direita. Fundo da caixa: preto. Botões claros; o último de cada linha, laranja.
Pare e experimente. Mude 24.5% para 20%. Preveja um vão maior. Recarregue. Confira. Restaure.
Erro comum: gap grande mais largura 25%. A quarta tecla cai na linha de baixo. Some os percentuais com a calha.
Buffer string, conta number
Tudo que sai do DOM é texto. "5" + "5" vira "55". A conta precisa de número.
console.log(typeof "5");
console.log(parseInt("5", 10) + parseInt("5", 10));
parseInt(texto, 10) lê um inteiro na base 10. Três variáveis de estado:
let total = 0;
let buffer = "0";
let operador = null;
const tela = document.querySelector(".tela");
Enquanto a pessoa digita, você cola dígitos no buffer. Na hora de somar, converte.
Pare e experimente. No console, rode typeof document.querySelector(".tela").textContent. Preveja "string". Confira.
Erro comum: buffer + 5 misturando tipos. Se buffer é "3", vira "35" na concatenação e "8" só se você converter os dois lados na conta.
Um ouvinte e funções pequenas
Vários botões, um pai .botoes. Um click. Funções curtas: uma decide se é número ou símbolo; outra trata dígito; outra trata C, =, ← e operadores.
document.querySelector(".botoes").addEventListener("click", function (evento) {
const botao = evento.target.closest("button");
if (!botao) {
return;
}
clicar(botao.textContent.trim());
tela.textContent = buffer;
});
function clicar(valor) {
if (Number.isNaN(parseInt(valor, 10))) {
simbolo(valor);
} else {
numero(valor);
}
}
function numero(valor) {
if (buffer === "0") {
buffer = valor;
} else {
buffer += valor;
}
}
O exemplo completo em calculadora.js tem também simbolo e calcular. Cerca de oitenta linhas, não duzentas. Se passar de duzentas, quebre de novo.
Pare e experimente. Clique 7 e 8. Preveja 78 na tela. Recarregue. Confira.
Erro comum: um ouvinte em cada botão copiando a mesma função. Delegue no pai, como no capítulo 19.

C, operadores e igual
C devolve o estado do carregamento: buffer = "0", total = 0, operador = null.
Quando chega +, -, × ou ÷: transforme o buffer em número; se já havia operação pendente, feche-a; guarde o novo operador; zere o buffer para o próximo dígito.
No =: se não há operador, ignore. Senão aplique, jogue o resultado no buffer, limpe total e operador.
Neste exercício, Math.trunc descarta a parte fracionária da divisão: 5 dividido por 2 dá 2. Isso é uma escolha do exercício, não uma regra da matemática. Divisão por zero mostra Erro; C reinicia.
←: buffer.substring(0, buffer.length - 1). Se o comprimento era 1, buffer = "0".
Pare e experimente. Faça 9 − 4 =, depois C. Preveja 0. Recarregue se o C não limpar o operador.
Erro comum: o C só apagar a tela e deixar operador antigo. O próximo número entra na conta errada.
Atividades
- Por que os dígitos ficam no buffer como string?
- O que o
Cprecisa zerar além da tela? - Por que as teclas são
buttone nãodiv? - Onde fica o
addEventListenerde clique neste projeto?
Respostas comentadas
- A pessoa cola dígitos.
"7"+"8"deve virar"78", não15. A conversão é na conta. buffer,totaleoperador. Só limpar o texto deixa a operação anterior viva.- Tecla com papel de botão. Foco, Enter e leitor de tela já vêm com
button. - No container
.botoes, um ouvinte só.closest("button")acha a tecla.
MDN
O arquivo completo
Referência do exemplo 20-calculadora, reproduzida aqui para que você possa conferir sem sair da página.
styles.css
.linha {
display: flex;
justify-content: space-between;
margin-bottom: 0.5%;
}
.botoes button {
width: 24.5%;
height: 100px;
font-size: 2rem;
}
.botoes .duplo { width: 49.7%; }
.botoes .triplo { width: 74.8%; }
.linha button:last-child {
background: #9f4b13;
color: #fff;
}
*{box-sizing:border-box}body{font-family:Arial;margin:1rem}.calc{width:25rem;max-width:100%;padding:.5rem;background:#000;color:#fff}.tela{text-align:right;overflow:auto;padding:1rem;font-size:2rem}.botoes button{border:0;color:#111;background:#e7e7e7}.botoes button.op{background:#9f4b13;color:#fff}