Nuestra calculadora ya funciona con clicks. En esta última clase vamos a darle dos mejoras que marcan la diferencia entre un proyecto básico y uno bien acabado:
Los usuarios que usan mucho el teclado (es decir, todo el mundo) esperan poder escribir números y operaciones directamente. Vamos a escuchar el evento keydown en el document:
document.addEventListener('keydown', (evento) => {
const tecla = evento.key
// Números y punto decimal
if ('0123456789.'.includes(tecla)) {
evento.preventDefault()
agregarNumero(tecla)
}
// Operadores
else if (tecla === '+') {
evento.preventDefault()
seleccionarOperador('+')
}
else if (tecla === '-') {
evento.preventDefault()
seleccionarOperador('-')
}
else if (tecla === '*') {
evento.preventDefault()
seleccionarOperador('×')
}
else if (tecla === '/') {
evento.preventDefault()
seleccionarOperador('÷')
}
// Igual (Enter o =)
else if (tecla === 'Enter' || tecla === '=') {
evento.preventDefault()
calcular()
}
// Limpiar (Escape)
else if (tecla === 'Escape') {
limpiar()
}
// Borrar (Backspace)
else if (tecla === 'Backspace') {
evento.preventDefault()
if (numeroActual.length > 1) {
numeroActual = numeroActual.slice(0, -1)
} else {
numeroActual = '0'
}
actualizarDisplay()
}
})
Puntos importantes:
evento.key devuelve el carácter de la tecla pulsada: "5", "+", "Enter", etc.evento.preventDefault() evita el comportamiento por defecto del navegador (por ejemplo, que / abra la búsqueda)agregarNumero, seleccionarOperador y calcular que ya teníamos. No duplicamos lógica* y /, pero nuestra calculadora usa × y ÷. Hacemos la conversión aquíPrueba: abre la calculadora y escribe 5+3 seguido de Enter. Debería funcionar igual que con los botones.
Necesitamos ampliar el HTML para incluir el historial. Envuelve la calculadora en un contenedor y añade el panel:
<div class="calculadora-container">
<div class="calculadora">
<!-- ... lo que ya teníamos ... -->
</div>
<div class="historial">
<h3>Historial</h3>
<div id="lista-historial"></div>
<button id="limpiar-historial">Limpiar Historial</button>
</div>
</div>
Y estos estilos adicionales en calculadora.css:
.calculadora-container {
display: flex;
gap: 30px;
align-items: flex-start;
}
.historial {
background: white;
border-radius: 15px;
padding: 20px;
width: 250px;
max-height: 400px;
overflow-y: auto;
}
.historial h3 {
margin-bottom: 15px;
color: #2c3e50;
}
.operacion-historial {
padding: 8px;
margin: 5px 0;
background: #f8f9fa;
border-radius: 5px;
font-family: monospace;
cursor: pointer;
transition: background 0.2s;
}
.operacion-historial:hover {
background: #e9ecef;
}
#limpiar-historial {
width: 100%;
padding: 10px;
background: #e74c3c;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-top: 10px;
}
Necesitamos una variable para almacenar las operaciones y una referencia al contenedor:
const listaHistorial = document.getElementById('lista-historial')
let historial = []
Cada vez que se completa una operación, la añadimos al historial. Creamos elementos del DOM dinámicamente con createElement:
function agregarAlHistorial(operacion) {
historial.unshift(operacion) // Añadir al principio
// Limitar a las últimas 10 operaciones
if (historial.length > 10) {
historial = historial.slice(0, 10)
}
renderizarHistorial()
}
function renderizarHistorial() {
// Limpiar el contenedor
listaHistorial.innerHTML = ''
historial.forEach(operacion => {
const div = document.createElement('div')
div.className = 'operacion-historial'
div.textContent = operacion
// Al hacer click, usar el resultado de esa operación
div.addEventListener('click', () => {
const resultado = operacion.split(' = ')[1]
if (resultado) {
numeroActual = resultado
numeroAnterior = ''
operador = null
debeReset = true
actualizarDisplay()
}
})
listaHistorial.appendChild(div)
})
}
Repasemos lo que hace renderizarHistorial:
innerHTML = ''<div> nuevo con createElementclassName y textContentappendChildAhora necesitamos llamar a agregarAlHistorial desde la función calcular. Añade esta línea justo antes de actualizar el estado:
// Dentro de calcular(), después de obtener el resultado:
agregarAlHistorial(`${anterior} ${operador} ${actual} = ${resultado}`)
Si el usuario cierra el navegador, el historial se pierde. Vamos a guardarlo con localStorage:
function guardarHistorial() {
localStorage.setItem('calculadora-historial', JSON.stringify(historial))
}
function cargarHistorial() {
const guardado = localStorage.getItem('calculadora-historial')
if (guardado) {
historial = JSON.parse(guardado)
renderizarHistorial()
}
}
JSON.stringify convierte el array de strings a un string JSON para poder guardarloJSON.parse hace lo contrario: convierte el string JSON de vuelta a un arrayLlama a guardarHistorial() cada vez que el historial cambie (al agregar y al limpiar) y a cargarHistorial() al inicio:
// Al final de agregarAlHistorial:
guardarHistorial()
// Botón de limpiar historial
document.getElementById('limpiar-historial').addEventListener('click', () => {
historial = []
renderizarHistorial()
guardarHistorial()
})
// Cargar historial al inicio
cargarHistorial()
5 + 3, 10 × 2, 100 ÷ 47*8 y pulsa EnterA lo largo de las tres clases del proyecto de la calculadora hemos puesto en práctica:
getElementById, querySelectorAlldataset.numero, dataset.operadoraddEventListener('click', ...) en botones individuales y gruposaddEventListener('keydown', ...), evento.key, preventDefault()textContent, createElement, appendChild, innerHTMLsetItem, getItem, JSON.stringify, JSON.parsetry/catch, throw new Error()Inicia sesión
Para guardar tu progreso y desbloquear logros