Aplicação web para cálculo do Índice de Massa Corporal (IMC), com interface visual cuidada, feedback dinâmico por cores e lógica organizada em módulos JavaScript.
- Cálculo do IMC em tempo real a partir de peso e altura
- Classificação por faixa: Abaixo do peso, Peso normal, Acima do peso, Obesidade 1/2/3
- Feedback visual com troca dinâmica de cor de fundo conforme o resultado
- Sugestão interativa de quanto peso ganhar ou perder para atingir o peso ideal
- Máscara automática nos campos de peso e altura (apenas números, ponto inserido automaticamente na altura)
- Remoção do placeholder ao focar no input e restauração ao sair
- Reset completo do formulário com cancelamento de timers pendentes
calculadora_IMC/
├── index.html
├── css/
│ └── styles.css
├── js/
│ ├── main.js # Orquestrador: seleciona elementos, registra eventos
│ ├── imcCalculator.js # Lógica de cálculo e classificação do IMC
│ ├── inputHandler.js # Formatação e comportamento dos inputs
│ └── timeoutManager.js # Controle de timers para a sugestão de peso ideal
└── assets/
O projeto adota ES Modules nativos no browser, com cada arquivo responsável por uma única preocupação (Single Responsibility Principle). O main.js age como orquestrador, importando funções dos demais módulos sem acoplamento direto entre eles.
Seleção eficiente com getElementById, querySelector e querySelectorAll. Manipulação direta de textContent, className, classList.add/remove e dataset para controle de estado visual dos elementos.
Múltiplos event listeners registrados com addEventListener: submit, click, focus, blur, keydown e input. Uso de event.target para reaproveitamento da mesma função em múltiplos inputs (ex.: handleFocus / handleBlur).
- Campo peso: remoção de caracteres não numéricos via
replace(/\D/g, "")no eventoinput - Campo altura: inserção automática do ponto decimal após o primeiro dígito (ex.:
"170"→"1.70") com lógica de reset ao apagar com backspace - Prevenção de teclas inválidas: bloqueio de vírgula e ponto via
event.preventDefault()nokeydown
O placeholder é removido ao focar (handleFocus) usando dataset para armazenar o valor original, e restaurado ao sair do foco (handleBlur). Pequeno detalhe, grande diferença na percepção visual do formulário.
Fórmula do IMC: peso / Math.pow(altura, 2), arredondada com toFixed(2) e convertida para número com o operador unário +. Classificação por faixas com if/else if encadeados, cada faixa mapeando para uma string de cor ("verde", "amarelo", "vermelho1", etc.).
A área de resultado é inicialmente invisível (visibility: hidden). Ao calcular, a classe "visible" é adicionada e a classe de cor correspondente é aplicada via mostraResultado.className = \result ${corClassificacao}``, substituindo todas as classes anteriores de uma vez.
A sugestão de peso ideal aparece 2 segundos após o cálculo, usando setTimeout. A referência ao timer é armazenada na variável timeout com escopo de módulo para que cancelarTimeout() possa limpá-la com clearTimeout ao clicar em "Resetar" — evitando que a sugestão apareça depois do reset.
- Background com gradiente linear (
linear-gradient) desenhado para criar transição visual de branco para azul - Transições suaves em inputs (
transition: background-color 0.3s ease) e no card de resultado (transition: transform 0.8s) com efeito de escala ao hover (scale(1.1)) - Layout centralizado com Flexbox
- Estados de foco estilizados com
outlinecolorido nos inputs
Como o projeto usa ES Modules nativos, é necessário servir os arquivos via servidor HTTP (não basta abrir o index.html diretamente no navegador).
Opção 1 — VS Code Live Server:
Clique com o botão direito em index.html → Open with Live Server
Opção 2 — Python:
python -m http.server 3000
# Acesse: http://localhost:3000Opção 3 — Node.js (npx):
npx serve .IMC = peso (kg) / altura² (m)
| Faixa de IMC | Classificação |
|---|---|
| < 18.5 | Abaixo do peso |
| 18.5 – 24.9 | Peso normal |
| 25.0 – 29.9 | Acima do peso |
| 30.0 – 34.9 | Obesidade grau 1 |
| 35.0 – 39.9 | Obesidade grau 2 |
| ≥ 40.0 | Obesidade grau 3 |
Vinicius Matos de Mendonça github.com/Vinnizius1 | linkedin.com/in/vinmm