En la clase anterior montamos la estructura HTML y conseguimos que los botones numéricos, el borrar y el limpiar funcionaran. Ahora vamos a hacer que la calculadora realmente calcule.
Vamos a implementar:
Para hacer operaciones necesitamos recordar más cosas. Amplía las variables al principio de calculadora.js:
// Elementos del DOM
const displayActual = document.getElementById('operacion-actual')
const displayAnterior = document.getElementById('operacion-anterior')
// Estado de la calculadora
let numeroActual = '0'
let numeroAnterior = ''
let operador = null
let debeReset = false
Tenemos dos nuevas variables:
operador guarda la operación seleccionada (+, -, × o ÷)debeReset es un flag que indica si al pulsar un número hay que limpiar el display. ¿Por qué? Porque cuando pulsas 5 + , el display muestra 5, pero al pulsar el siguiente número queremos que reemplace el 5, no que se concateneAntes de seguir, creemos una función que actualice ambas líneas del display. La vamos a necesitar en varios sitios:
function actualizarDisplay() {
displayActual.textContent = numeroActual
displayAnterior.textContent = numeroAnterior
}
Y modifiquemos la función de añadir número para usar el flag debeReset:
function agregarNumero(numero) {
if (debeReset) {
numeroActual = '0'
debeReset = false
}
if (numero === '.' && numeroActual.includes('.')) return
if (numeroActual === '0' && numero !== '.') {
numeroActual = numero
} else {
numeroActual += numero
}
actualizarDisplay()
}
Ahora los listeners de los botones numéricos quedan más limpios:
document.querySelectorAll('.btn-numero').forEach(boton => {
boton.addEventListener('click', () => {
agregarNumero(boton.dataset.numero)
})
})
Cuando el usuario pulsa un operador (+, -, ×, ÷), necesitamos:
function seleccionarOperador(op) {
// Si ya hay una operación pendiente, calcularla primero
if (operador && !debeReset) {
calcular()
}
operador = op
numeroAnterior = numeroActual + ' ' + op
debeReset = true
actualizarDisplay()
}
¿Ves el detalle de if (operador && !debeReset)? Si el usuario pulsa 5 + 3 + sin dar a igual, primero calculamos 5 + 3 = 8 y luego preparamos la siguiente operación con 8.
Ahora conectamos los botones:
document.querySelectorAll('.btn-operador').forEach(boton => {
boton.addEventListener('click', () => {
seleccionarOperador(boton.dataset.operador)
})
})
Este es el corazón de la calculadora. Tomamos los dos números, aplicamos la operación y mostramos el resultado:
function calcular() {
if (!operador || debeReset) return
const anterior = parseFloat(numeroAnterior)
const actual = parseFloat(numeroActual)
if (isNaN(anterior) || isNaN(actual)) return
let resultado
try {
switch (operador) {
case '+':
resultado = anterior + actual
break
case '-':
resultado = anterior - actual
break
case '×':
resultado = anterior * actual
break
case '÷':
if (actual === 0) {
throw new Error('No se puede dividir por cero')
}
resultado = anterior / actual
break
default:
return
}
// Redondear para evitar errores de punto flotante
resultado = Math.round((resultado + Number.EPSILON) * 100000000) / 100000000
numeroActual = resultado.toString()
numeroAnterior = ''
operador = null
debeReset = true
} catch (error) {
mostrarError(error.message)
return
}
actualizarDisplay()
}
Repasamos las partes importantes:
parseFloat: convierte los strings a números para poder operar con ellosswitch: ejecuta la operación correspondiente según el operador guardadothrow que capturamos con try/catch0.1 + 0.2 = 0.30000000000000004Cuando hay un error (como dividir por cero), mostramos un mensaje temporal:
function mostrarError(mensaje) {
displayActual.textContent = 'Error'
displayAnterior.textContent = mensaje
setTimeout(() => {
limpiar()
}, 2000)
}
Usamos setTimeout para que el error se muestre durante 2 segundos y luego la calculadora se resetee automáticamente.
Ahora adaptamos los botones para que usen las nuevas variables:
function limpiar() {
numeroActual = '0'
numeroAnterior = ''
operador = null
debeReset = false
actualizarDisplay()
}
document.getElementById('clear').addEventListener('click', limpiar)
document.getElementById('delete').addEventListener('click', () => {
if (numeroActual.length > 1) {
numeroActual = numeroActual.slice(0, -1)
} else {
numeroActual = '0'
}
actualizarDisplay()
})
document.getElementById('igual').addEventListener('click', calcular)
Abre index.html en el navegador y comprueba:
8 + 2 = muestra 1010 ÷ 3 = muestra un resultado redondeado5 ÷ 0 = muestra "Error: No se puede dividir por cero"5 + 3 + 2 = encadena operaciones correctamente (primero 5+3=8, luego 8+2=10)// Elementos del DOM
const displayActual = document.getElementById('operacion-actual')
const displayAnterior = document.getElementById('operacion-anterior')
// Estado
let numeroActual = '0'
let numeroAnterior = ''
let operador = null
let debeReset = false
function actualizarDisplay() {
displayActual.textContent = numeroActual
displayAnterior.textContent = numeroAnterior
}
function agregarNumero(numero) {
if (debeReset) {
numeroActual = '0'
debeReset = false
}
if (numero === '.' && numeroActual.includes('.')) return
if (numeroActual === '0' && numero !== '.') {
numeroActual = numero
} else {
numeroActual += numero
}
actualizarDisplay()
}
function seleccionarOperador(op) {
if (operador && !debeReset) calcular()
operador = op
numeroAnterior = numeroActual + ' ' + op
debeReset = true
actualizarDisplay()
}
function calcular() {
if (!operador || debeReset) return
const anterior = parseFloat(numeroAnterior)
const actual = parseFloat(numeroActual)
if (isNaN(anterior) || isNaN(actual)) return
let resultado
try {
switch (operador) {
case '+': resultado = anterior + actual; break
case '-': resultado = anterior - actual; break
case '×': resultado = anterior * actual; break
case '÷':
if (actual === 0) throw new Error('No se puede dividir por cero')
resultado = anterior / actual
break
default: return
}
resultado = Math.round((resultado + Number.EPSILON) * 100000000) / 100000000
numeroActual = resultado.toString()
numeroAnterior = ''
operador = null
debeReset = true
} catch (error) {
mostrarError(error.message)
return
}
actualizarDisplay()
}
function mostrarError(mensaje) {
displayActual.textContent = 'Error'
displayAnterior.textContent = mensaje
setTimeout(() => limpiar(), 2000)
}
function limpiar() {
numeroActual = '0'
numeroAnterior = ''
operador = null
debeReset = false
actualizarDisplay()
}
// Event listeners
document.querySelectorAll('.btn-numero').forEach(boton => {
boton.addEventListener('click', () => agregarNumero(boton.dataset.numero))
})
document.querySelectorAll('.btn-operador').forEach(boton => {
boton.addEventListener('click', () => seleccionarOperador(boton.dataset.operador))
})
document.getElementById('igual').addEventListener('click', calcular)
document.getElementById('clear').addEventListener('click', limpiar)
document.getElementById('delete').addEventListener('click', () => {
if (numeroActual.length > 1) {
numeroActual = numeroActual.slice(0, -1)
} else {
numeroActual = '0'
}
actualizarDisplay()
})
Ya tienes una calculadora funcional. En la siguiente clase le añadiremos soporte de teclado y un historial de operaciones.
Inicia sesión
Para guardar tu progreso y desbloquear logros