Skip to content
WANCODE
BM

Build an Interactive Calculator

20 minBeginnerHTML · CSS · JS

In this lesson

  • Keep an app's state in a few variables and update the page from it
  • Separate pure logic (calculate) from DOM code, and handle edge cases like dividing by zero
  • Connect many buttons with data attributes and add keyboard shortcuts

The big idea

An interactive app is a loop: the user does something → an event handler updates the state → the page is redrawn from the state. Keep those three parts separate and even a calculator stays simple.

This is your capstone for the track so far. The HTML and CSS are ready; you'll write all the behaviour. You'll use functions, conditions, strings, arrays, loops, the DOM and events — everything at once.

1. Plan the state

Before writing code, ask: what does the calculator need to remember? For 12 + 3 = it needs the number being typed, the first number, and the chosen operator. That's the state:

JavaScript
let current = "0";   // what's being typed — a STRING, so "1" then "2" makes "12"
let previous = null; // the first number, saved when you press an operator
let operator = null; // "+", "-", "*" or "/"
  1. Press 1, 2 → current becomes "12".
  2. Press + → previous = 12, operator = "+", current resets to "0".
  3. Press 3 → current is "3".
  4. Press = → calculate(12, "+", 3) gives 15, which becomes the new current.

2. Keep the maths separate

calculate(a, op, b) should only do maths: numbers in, answer out. No document, no events. A function like that is easy to test and easy to reuse — the buttons and the keyboard will both call it.

A pure function is a machine: the same inputs always give the same output.

Two edge cases real calculators handle

Dividing by zero: in JavaScript 8 / 0 is Infinity, which isn't a helpful answer — show Error instead. Floating-point noise: computers store decimals in binary, so 0.1 + 0.2 is 0.30000000000000004. Rounding to about 10 decimal places — Math.round(result * 1e10) / 1e10 — hides it.

3. Wire up many buttons at once

Each digit button carries its value in a data attribute, like data-digit="7". JavaScript reads it as button.dataset.digit. So instead of ten separate listeners, one loop does it:

JavaScript
// <button data-digit="7">7</button>
for (const button of document.querySelectorAll("[data-digit]")) {
  button.addEventListener("click", () => inputDigit(button.dataset.digit));
}

Build in small steps and press Run often: get one digit showing, then several, then the operator, then =. When something breaks, console.log(current, previous, operator) inside your functions shows you the state.

Write calculate(a, op, b). It returns the answer for +, -, * and /, and the string "Error" when dividing by zero or when op is anything else.

Round the result so calculate(0.1, "+", 0.2) gives exactly 0.3.

Checks

  • All four operators work
  • Divide by zero and unknown operators give "Error"
  • 0.1 + 0.2 gives 0.3

Check understanding

Q1

Why is current stored as a string ("12") rather than a number while typing?

Q2

What is 8 / 0 in JavaScript?

Q3

What's the main benefit of keeping calculate free of any DOM code?

What you learned

  • Plan the state first: what must the app remember between clicks?
  • Keep pure logic (calculate) separate from DOM and event code.
  • Data attributes + one loop connect many similar buttons; dataset reads them.
  • Handle edge cases on purpose: division by zero, repeated dots, floating-point noise.
Preview

Console output from your code appears here.

Step 1 of 3