Skip to content
beginner

Build a Simple Calculator

A calculator is not smart. It just remembers two numbers and one operator between clicks. Once you see that, the whole project becomes simple—and you will…

Published 2026-09-06Updated 2026-09-1212 min read
Businesswoman working on laptop with Android 6.0 Marshmallow webpage open.
Businesswoman working on laptop with Android 6.0 Marshmallow webpage open. Photo by Christina Morillo on Pexels.

A calculator is not smart. It just remembers two numbers and one operator between clicks. Once you see that, the whole project becomes simple—and you will have glued together event listeners, DOM selection, and state tracking into something real you can open in a browser and use.

What You'll Build

By the end of this tutorial, you'll have a working calculator app with:

  • Number buttons for digits 0 through 9
  • Operator buttons for addition, subtraction, multiplication, and division
  • A clear button to reset everything
  • An equals button to run the calculation
  • A display that shows what you've typed and the final result

Think of the calculator as three moving parts working together:

  1. Buttons fire events when clicked.
  2. The display is the part of the page you update.
  3. Logic decides what each click should do.

If you've already built the random quote generator, you've done most of this before. You attached click listeners to buttons. You updated text on the page. The new challenge here is remembering values between clicks—and that's what makes this project worth building.

Setting Up the Project Files

No build tools. No frameworks. No installs. Just three plain files that any browser can open directly.

Create a new folder called calculator and add three files inside it:

  • index.html — the structure
  • style.css — the styling
  • script.js — the behavior

Start with a basic HTML skeleton in index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calculator</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- Calculator markup goes here -->

  <script src="script.js"></script>
</body>
</html>

Notice where the script tag sits: right before the closing </body> tag. That placement matters. When the browser reads your HTML from top to bottom, it builds the page structure first. If the script ran before the buttons existed, JavaScript wouldn't find them. Putting the script at the end guarantees the DOM—the page structure JavaScript reads—is ready.

Knowledge check

Check your understanding

Answer this question before you continue.

Why does the tutorial place the script tag just before the closing body tag?
Single Choice

Focus: Explain why the script element is placed at the end of the body in the calculator page.

Building the Calculator Layout in HTML

Now let's add the visible structure inside the body. You need a display area on top and a grid of buttons below.

<div class="calculator">
  <div id="display" class="display">0</div>
  <div class="buttons">
    <button class="clear">C</button>
    <button class="operator">/</button>
    <button class="operator">*</button>
    <button class="operator">-</button>
    <button>7</button>
    <button>8</button>
    <button>9</button>
    <button class="operator">+</button>
    <button>4</button>
    <button>5</button>
    <button>6</button>
    <button id="equals">=</button>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>0</button>
    <button>.</button>
  </div>
</div>

The display is a simple div with the id display. It starts showing 0. Each button carries either a number, an operator symbol, or a special class.

The clear button gets the class clear. The equals button gets the id equals. Operators get the class operator. These markers let JavaScript treat different buttons differently. A number click should append a digit. An operator click should store the operator. The equals click should run the math.

Common mistake: If you accidentally add two buttons with the same class or id, your JavaScript will behave unpredictably. Keep one clear button and one equals button.

Making the Calculator Look Like a Calculator

Styling is a bonus here. The JavaScript works the same no matter how the page looks. But a little CSS makes the project feel real and helps you read the display clearly.

Add this to style.css:

body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background: #f0f0f0;
  font-family: Arial, sans-serif;
}

.calculator {
  background: #333;
  padding: 20px;
  border-radius: 10px;
  width: 240px;
}

.display {
  background: #e8e8e8;
  padding: 15px;
  text-align: right;
  font-size: 2rem;
  border-radius: 5px;
  margin-bottom: 15px;
  min-height: 50px;
  overflow: hidden;
}

.buttons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

button {
  padding: 15px;
  font-size: 1.2rem;
  border: none;
  border-radius: 5px;
  background: #666;
  color: white;
  cursor: pointer;
}

button:hover {
  background: #888;
}

.operator {
  background: #f39c12;
}

.operator:hover {
  background: #e67e22;
}

.clear {
  background: #e74c3c;
}

#equals {
  background: #2ecc71;
}

The grid-template-columns: repeat(4, 1fr) line arranges the buttons into four equal columns. The display sits on top with right-aligned text, just like a real calculator.

If the layout looks off, don't stress. Adjust spacing, colors, or sizes until it feels right. The styling is yours to play with.

Wiring Up Button Clicks with JavaScript

Now for the fun part. Open script.js and start by selecting the elements you need:

const display = document.getElementById('display');
const buttons = document.querySelectorAll('button');

getElementById grabs the display. querySelectorAll('button') grabs every button on the page and returns a list you can loop through.

Before writing the full logic, let's prove the clicks work. Attach a listener to each button and log what was clicked:

buttons.forEach(button => {
  button.addEventListener('click', () => {
    console.log(button.textContent);
  });
});

Open the page in your browser, press a few buttons, and open the developer console (right-click → Inspect → Console tab). You should see the button labels appear as you click.

7
+
3
=

This is the same event-listener pattern you used in the quote generator. The difference: now you're attaching it to many buttons at once with a loop. Every click fires, and you can see exactly which button was pressed.

This click logger is just a temporary test. You'll replace it with the real handler in the next step.

Knowledge check

Check your understanding

Answer this question before you continue.

With the temporary click logger from this section, what appears in the console when the user clicks 8, then -, then 2?
Output Prediction

Focus: Predict the console output produced by the temporary click logger for a sequence of calculator button clicks.

buttons.forEach(button => {
  button.addEventListener('click', () => {
    console.log(button.textContent);
  });
});

Tracking the Numbers and the Operator

Flowchart showing a calculator button click entering a handler, branching into clear, operator, number or decimal, and equals actions, then updating current number, previous number, operator, and the display.
Every click follows the same path: classify the button, update the calculator’s state, and refresh the display or calculate a result.

Here's the conceptual heart of the project. A calculator must remember three things between clicks:

  1. The number being typed right now
  2. The number typed before the operator
  3. The operator waiting to run

Let's store those in variables:

let currentNumber = '';
let previousNumber = '';
let operator = '';

Why strings for numbers? Because when a user types 1 then 5, you want to build "15" by appending, not add 1 + 5 to get 6. String concatenation builds the display value naturally. You'll convert to real numbers only when it's time to calculate.

Now replace the temporary click logger with the real handler that sorts buttons by type:

buttons.forEach(button => {
  button.addEventListener('click', () => {
    const value = button.textContent;

    if (button.classList.contains('clear')) {
      // Reset everything
      currentNumber = '';
      previousNumber = '';
      operator = '';
      display.textContent = '0';
    } else if (button.classList.contains('operator')) {
      // Store the operator and move the current number aside
      operator = value;
      previousNumber = currentNumber;
      currentNumber = '';
    } else if (button.id === 'equals') {
      // Run the calculation (we'll write this next)
      calculate();
    } else {
      // It's a number or decimal point
      currentNumber += value;
      display.textContent = currentNumber;
    }
  });
});

Walk through what happens when a user presses 1, 2, +, 7:

  1. Press 1: currentNumber becomes "1". Display shows 1.
  2. Press 2: currentNumber becomes "12". Display shows 12.
  3. Press +: operator becomes "+". previousNumber becomes "12". currentNumber resets to "".
  4. Press 7: currentNumber becomes "7". Display shows 7.

The calculator isn't doing math yet. It's just remembering: first number 12, operator +, second number 7. That's the whole trick.

Knowledge check

Check your understanding

Answer this question before you continue.

After the user presses 1, 2, +, and 7, what do the calculator's tracked values represent?
Single Choice

Focus: Identify the state values stored after entering a first number, an operator, and a second number.

Writing the Math Logic

When the user presses equals, it's time to convert those strings into numbers and run the operation.

Add a function that performs the calculation:

function calculate() {
  if (previousNumber === '' || currentNumber === '') return;

  const prev = parseFloat(previousNumber);
  const current = parseFloat(currentNumber);
  let result;

  switch (operator) {
    case '+':
      result = prev + current;
      break;
    case '-':
      result = prev - current;
      break;
    case '*':
      result = prev * current;
      break;
    case '/':
      if (current === 0) {
        display.textContent = 'Error';
        return;
      }
      result = prev / current;
      break;
    default:
      return;
  }

  display.textContent = result;
  previousNumber = String(result);
  currentNumber = '';
}

parseFloat converts a string like "12" into the number 12. The switch statement checks which operator is stored and runs the matching arithmetic.

The guard at the top prevents a crash when the user presses = without typing a second number. Without it, parseFloat('') would produce NaN—short for "Not a Number"—and the display would show a confusing result.

The division check catches division by zero. In JavaScript, dividing by zero gives Infinity, which is technically correct but not useful on a calculator display. Showing Error is friendlier.

Test it with 12 + 7:

19

The display shows 19. Your calculator works.

One note: real calculators handle long chains like 2 + 3 × 4 with proper operator precedence. This project deliberately keeps the scope to two numbers and one operation. That's the right size for a first build.

Knowledge check

Check your understanding

Answer this question before you continue.

A calculator displays Infinity when the user divides by zero. Which change fixes the issue described in the tutorial?
Debugging

Focus: Locate the handling needed to prevent a calculator from displaying Infinity for division by zero.

case '/':
  result = prev / current;
  break;

The Complete script.js

Here's the full JavaScript file with everything assembled. If you've been following along, your file should match this:

const display = document.getElementById('display');
const buttons = document.querySelectorAll('button');

let currentNumber = '';
let previousNumber = '';
let operator = '';

function calculate() {
  if (previousNumber === '' || currentNumber === '') return;

  const prev = parseFloat(previousNumber);
  const current = parseFloat(currentNumber);
  let result;

  switch (operator) {
    case '+':
      result = prev + current;
      break;
    case '-':
      result = prev - current;
      break;
    case '*':
      result = prev * current;
      break;
    case '/':
      if (current === 0) {
        display.textContent = 'Error';
        return;
      }
      result = prev / current;
      break;
    default:
      return;
  }

  display.textContent = result;
  previousNumber = String(result);
  currentNumber = '';
}

buttons.forEach(button => {
  button.addEventListener('click', () => {
    const value = button.textContent;

    if (button.classList.contains('clear')) {
      currentNumber = '';
      previousNumber = '';
      operator = '';
      display.textContent = '0';
    } else if (button.classList.contains('operator')) {
      operator = value;
      previousNumber = currentNumber;
      currentNumber = '';
    } else if (button.id === 'equals') {
      calculate();
    } else {
      currentNumber += value;
      display.textContent = currentNumber;
    }
  });
});

Save the file, open index.html in your browser, and run through this test:

  1. Press 1, 2
  2. Press +
  3. Press 7
  4. Press =
  5. Confirm the display shows 19
19

Then try subtraction, multiplication, and division. Press clear and confirm the display resets to 0.

Handling the Tricky Edges

Every first calculator breaks in the same few places. Here's what to watch for.

The page reload trap

If your buttons somehow sit inside a <form> element, clicking them can submit the form and refresh the page. The fix is to prevent that default behavior:

button.addEventListener('click', (event) => {
  event.preventDefault();
  // rest of your handler
});

If you're not using a form, you won't hit this. But if clicks ever make the page reload, this is why.

Pressing an operator before a second number

What happens if the user presses 5, +, then = without typing a second number? The guard in calculate() catches it. If either number is missing, the function does nothing. The calculator quietly waits for valid input.

Pressing two operators in a row

What about 5, +, -? The current code stores - as the operator and keeps 5 as the previous number. The second operator replaces the first. That's a reasonable beginner behavior—the calculator just uses the last operator you pressed.

Typing multiple decimal points

What about 1, ., 2, ., 3? The current code would build the string "1.2.3", and parseFloat would read it as 1.2, silently dropping the rest. That's confusing. A small guard fixes it:

} else if (value === '.' && currentNumber.includes('.')) {
  // Ignore a second decimal point
} else {
  currentNumber += value;
  display.textContent = currentNumber;
}

Now 1.2.3 stays 1.2, and the second decimal point is ignored.

These edge cases aren't failures. They're the exact spots where every beginner calculator breaks—and fixing them is how you learn to think like a developer.

Testing Your Calculator and Common Fixes

Run through this sequence to verify everything works:

  1. Press 1, 2
  2. Press +
  3. Press 7
  4. Press =
  5. Confirm the display shows 19

Then try subtraction, multiplication, and division. Press clear and confirm the display resets to 0.

If something's broken, here are the usual suspects:

SymptomLikely causeFix
Nothing happens on clickListener not attachedCheck that querySelectorAll ran and addEventListener is inside the loop
Wrong resultOperator not storedLog operator to the console after pressing an operator button
Page reloads on clickButtons inside a formAdd event.preventDefault() to the click handler
Display shows NaNMissing number guardAdd the empty-string check in calculate
Display shows InfinityDivision by zeroAdd the zero check in the division case

Here's my honest advice: break it on purpose. Press buttons in weird orders. Click = first. Press two operators in a row. Watch what happens in the console. Debugging isn't a detour from building—it's how you learn what the code is actually doing.

Next Steps to Push Your Skills Further

Your calculator is complete when you can explain, in plain words, what it remembers between clicks: the current number, the previous number, and the operator.

When you're ready to push further, try these extensions on your own:

  • Add a backspace button that removes the last digit typed
  • Support keyboard input so pressing 7 on your keyboard types 7
  • Allow chained operations so 2 + 3 + 4 works without pressing equals between each step

The natural next project in this learning path is a to-do list, where you'll manage a growing list of items instead of just two numbers. That project introduces arrays and rendering lists—skills that show up constantly in real web development.

For now, though, enjoy this win. You took event listeners, DOM selection, and state tracking—three separate skills—and glued them into something that works. That's what building with JavaScript feels like.

Knowledge check

Final check

Finish the article by checking the ideas you just learned.

What happens in the tutorial's current calculator when the user presses 5, +, and - in that order?
Question 1 of 2Misconception Check

Focus: Explain how the calculator handles two operators pressed consecutively.

Why does calculate return early when previousNumber or currentNumber is an empty string?
Question 2 of 2Misconception Check

Focus: Explain why the calculation guard prevents confusing output when equals is pressed without a second number.

if (previousNumber === '' || currentNumber === '') return;

References

  1. JavaScript DOM Tutorial – How to Build a Calculator App in JSwww.freecodecamp.org
8sources checked
8source domains
5searches run

Research updated Sep 6, 2026

Keep learning

Related tutorials

Continue with nearby JavaScript topics and beginner-friendly explanations.

Dramatic aerial view of Panama City skyline during sunset showcasing modern skyscrapers.
beginner
10 min read

Build a Click Counter

There's a moment in learning JavaScript when things stop being abstract. You've studied variables and functions. You've followed along with examples. But…

Read tutorial
Close-up of a tropical flower with vibrant red and yellow petals in vivid detail.
beginner
13 min read

Build a JavaScript Quiz App

You've learned arrays, conditionals, click events, and DOM updates as separate lessons. Now it's time to see them work together. A quiz app is the perfect…

Read tutorial