Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Build a working four-operation calculator with three files: HTML supplies the form controls, JavaScript performs validated arithmetic, and CSS provides optional presentation. This beginner version uses two number fields, a <select> for the operator, a result output, and no unsafe eval().
What you will build
- Two numeric inputs for decimal or whole numbers
- Addition, subtraction, multiplication, and division
- Calculate and Clear controls
- Validation for blank values, invalid numbers, unsupported operations, and division by zero
- An accessible result area that updates without reloading the page
A two-input calculator is a better first project than a keypad calculator. A keypad requires state for the current operand, previous operand, operator, decimal points, backspace, repeated operators, and expression precedence.
How HTML and JavaScript divide the work
HTML creates labels, inputs, buttons, and the <output> element. JavaScript listens for submission, reads values from the DOM, converts input strings to numbers, selects an allowed operation, and writes the answer back into the page. The DOM is the browser’s programming interface for document elements; see MDN’s DOM overview.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsHTML provides browser-level behavior such as the constraints of type="number", but it does not replace script-side checks. Number-input controls and mobile keyboards can behave differently across browsers, and client-side validation is not a security boundary. See MDN’s number-input reference.
#1 Best Overall
Create the project files
Make a folder named simple-calculator with this structure:
simple-calculator/
├── index.html
├── styles.css
└── script.js
- Create the three files in the same folder.
- Paste the HTML, CSS, and JavaScript from the sections below.
- Save the files and open
index.htmlin a browser.
Build the HTML interface
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple JavaScript Calculator</title>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
</head>
<body>
<main class="calculator">
<h1>Simple Calculator</h1>
<form id="calculator-form">
<div>
<label for="first-number">First number</label>
<input id="first-number" type="number" step="any" required>
</div>
<div>
<label for="operation">Operation</label>
<select id="operation">
<option value="+">Add (+)</option>
<option value="-">Subtract (−)</option>
<option value="*">Multiply (×)</option>
<option value="/">Divide (÷)</option>
</select>
</div>
<div>
<label for="second-number">Second number</label>
<input id="second-number" type="number" step="any" required>
</div>
<div class="actions">
<button type="submit">Calculate</button>
<button type="reset">Clear</button>
</div>
</form>
<p>Result: <output id="result" aria-live="polite">—</output></p>
<p id="error" class="error" role="alert"></p>
</main>
</body>
</html>
Each control has a matching label and ID. step="any" permits decimal values. The HTML Standard documents <output> as a suitable destination for calculation results, including in a simple calculator example: WHATWG form elements.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
The defer attribute lets the browser parse the document before executing the external script. It is a loading convenience, not a calculator requirement.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Add optional styling
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 1rem;
font-family: system-ui, sans-serif;
background: #f4f4f5;
color: #18181b;
}
.calculator {
width: min(100%, 28rem);
padding: 1.5rem;
border-radius: 0.75rem;
background: white;
box-shadow: 0 0.5rem 2rem rgb(0 0 0 / 10%);
}
.calculator form {
display: grid;
gap: 1rem;
}
.calculator label {
display: block;
margin-bottom: 0.35rem;
font-weight: 600;
}
.calculator input,
.calculator select,
.calculator button {
width: 100%;
min-height: 2.75rem;
padding: 0.5rem;
font: inherit;
}
.actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
margin-top: 0.5rem;
}
.error {
min-height: 1.5rem;
color: #b91c1c;
}
Implement the calculation in JavaScript
const form = document.querySelector("#calculator-form");
const firstNumberInput = document.querySelector("#first-number");
const secondNumberInput = document.querySelector("#second-number");
const operationInput = document.querySelector("#operation");
const resultOutput = document.querySelector("#result");
const errorOutput = document.querySelector("#error");
form.addEventListener("submit", (event) => {
event.preventDefault();
const firstValue = firstNumberInput.value.trim();
const secondValue = secondNumberInput.value.trim();
errorOutput.textContent = "";
resultOutput.textContent = "—";
if (firstValue === "" || secondValue === "") {
errorOutput.textContent = "Enter both numbers.";
return;
}
const firstNumber = Number(firstValue);
const secondNumber = Number(secondValue);
const operation = operationInput.value;
if (!Number.isFinite(firstNumber) || !Number.isFinite(secondNumber)) {
errorOutput.textContent = "Enter valid numbers.";
return;
}
let result;
switch (operation) {
case "+":
result = firstNumber + secondNumber;
break;
case "-":
result = firstNumber - secondNumber;
break;
case "*":
result = firstNumber * secondNumber;
break;
case "/":
if (secondNumber === 0) {
errorOutput.textContent = "You cannot divide by zero.";
return;
}
result = firstNumber / secondNumber;
break;
default:
errorOutput.textContent = "Choose a valid operation.";
return;
}
resultOutput.textContent = formatResult(result);
});
form.addEventListener("reset", () => {
errorOutput.textContent = "";
resultOutput.textContent = "—";
});
function formatResult(value) {
if (Number.isInteger(value)) {
return String(value);
}
return String(Number(value.toFixed(10)));
}
Why this code validates before conversion
Form values are strings. Converting them is essential: "2" + "3" produces "23", while 2 + 3 produces 5. Check for blank strings first because Number("") returns 0; otherwise an empty field could look like a real zero.
Rank #3
Number() is appropriate here because the complete field must represent a number. parseFloat() can accept a valid numeric prefix from a longer string, which is less strict for this form. The arithmetic operators and conversion rules are covered in MDN’s JavaScript math guide.
Why use a switch and addEventListener()
The switch maps exactly four permitted operator values to arithmetic code. It avoids treating user text as executable code. MDN warns that eval() dynamically parses and executes JavaScript and can create injection risks when untrusted strings reach it: MDN’s eval() reference.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
addEventListener() keeps behavior out of the markup and lets the form respond to the Enter key. Current MDN guidance recommends it over inline attributes such as onclick; see Introduction to events.
Why preventDefault() matters
A form normally reloads or navigates when submitted. event.preventDefault() stops that default action so the result remains on the current page.
Test the calculator
| Test | Input 1 | Operation | Input 2 | Expected result |
|---|---|---|---|---|
| Addition | 12 | + |
8 | 20 |
| Subtraction | 12 | - |
8 | 4 |
| Multiplication | 12 | * |
8 | 96 |
| Division | 12 | / |
4 | 3 |
| Decimal addition | 0.1 | + |
0.2 | Displayed approximately as 0.3 |
| Negative number | -5 | * |
3 | -15 |
| Large value | 1e10 |
+ |
2 | 10000000002 |
| Empty input | blank | + |
3 | Error message |
| Division by zero | 10 | / |
0 | Error message |
JavaScript uses binary floating-point numbers, so some decimal fractions cannot be represented exactly. The formatting function limits displayed noise to ten decimal places; it does not provide exact decimal or monetary arithmetic, and the code keeps full calculation precision until display.
Troubleshoot common failures
- Nothing happens: open browser developer tools, inspect the Console, and confirm that
script.jsis in the same folder and loaded withdefer. - “Cannot read properties of null”: compare every JavaScript selector with the corresponding HTML
id. - The page reloads: verify that the handler is attached to the form’s
submitevent and callsevent.preventDefault(). - Blank input becomes zero: check trimmed strings before calling
Number(). - Unexpected division result: confirm the explicit zero check and test the operation selector’s values.
- Clear leaves an old result: keep the
resetlistener that clears both custom output elements.
Where to take it next
Once this version is clear, you can add a keypad, keyboard shortcuts, calculation history, percentage or square-root operations, dark mode, localized labels, or automated tests. A chained-expression calculator is a separate design: it needs a defined grammar or parser and careful handling of precedence, repeated operators, decimals, backspace, and state transitions. Do not turn unrestricted display text into JavaScript with eval().
Client-side and production considerations
This calculator needs only a browser and a text editor; it requires no server or database. For billing, pricing, or security-sensitive decisions, do not treat a browser-calculated value as authoritative. Users can modify client-side HTML and JavaScript, and client-side validation is not a security control. Recalculate and validate sensitive values on a trusted server, using decimal-safe arithmetic where the domain requires it.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

