What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
You can build a working four-operation calculator with three files: HTML for the interface, CSS for the layout, and JavaScript for input, state, arithmetic, and error handling. This tutorial creates a calculator with decimal numbers, addition, subtraction, multiplication, division, Clear, Delete, accessible buttons, and optional keyboard support—without using eval().
What you will build
- Number buttons from 0 to 9
- Decimal input
- Addition, subtraction, multiplication, and division
- Equals, Clear, and Delete controls
- Division-by-zero and invalid-result handling
- Optional keyboard controls
The finished calculator evaluates one operation at a time, like a basic pocket calculator. It is not a full expression parser, so an input such as 2 + 3 × 4 is processed sequentially rather than with normal mathematical precedence.
Prerequisites
You need a modern browser, a text editor, and basic HTML knowledge. No framework, package manager, build tool, or dependency is required. You can use a local editor such as Visual Studio Code, or experiment immediately in CodePen.
Create the project files
calculator/
├── index.html
├── styles.css
└── script.js
HTML creates the structure, CSS controls its appearance, and JavaScript supplies behavior through the browser DOM. Keeping the files separate makes the relationship between these technologies easier to understand. JavaScript can also be embedded with a <script> element, but an external file is preferable once the example grows. See MDN’s guide to adding JavaScript to a web page.
#1 Best Overall
- RELIABLE PROCESSOR: Adopts Core i3 processor with dual core quad thread design and 2.0 GHz base frequency delivers steady running performance to support smooth daily office web browsing and multitasking operation
- 15.6 INCH HD SCREEN & ULTRA PORTABLE BODY: Features 15.6 inch high definition screen for clear daily viewing experience comes with lightweight 1.6 kg body easy to carry around for commuting business trips and outdoor study anytime
- FULL SIZE KEYBOARD: Built in full size keyboard with independent numeric keypad equipped with backlight design for dark environment typing supports fingerprint unlock for private data protection and matches a large sensitive touchpad for smooth control
- COMPLETE RICH EXPANSION PORTS: Built in sufficient side interfaces, including 2 USB 3.0 ports, 3.5mm audio jack, HDMI interface, MicroSD (TF) card slot, and DC power port to meet all daily needs
- STABLE WIRELESS CONNECTION: Built in Bluetooth 4.2 for quick pairing with wireless peripherals supports 5G WiFi network to realize faster transmission speed and more stable network signal for daily online work and entertainment
1. Build the HTML interface
Create index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simple Calculator</title>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
</head>
<body>
<main class="calculator" aria-labelledby="calculator-title">
<h1 id="calculator-title">Simple Calculator</h1>
<output id="display" class="display" aria-live="polite" aria-label="Calculator result">0</output>
<div class="keys" id="calculator-keys">
<button type="button" data-action="clear" class="function-key">Clear</button>
<button type="button" data-action="delete" class="function-key">Delete</button>
<button type="button" data-operator="/" class="operator-key" aria-label="Divide">÷</button>
<button type="button" data-operator="*" class="operator-key" aria-label="Multiply">×</button>
<button type="button" data-number="7">7</button>
<button type="button" data-number="8">8</button>
<button type="button" data-number="9">9</button>
<button type="button" data-operator="-" class="operator-key" aria-label="Subtract">−</button>
<button type="button" data-number="4">4</button>
<button type="button" data-number="5">5</button>
<button type="button" data-number="6">6</button>
<button type="button" data-operator="+" class="operator-key" aria-label="Add">+</button>
<button type="button" data-number="1">1</button>
<button type="button" data-number="2">2</button>
<button type="button" data-number="3">3</button>
<button type="button" data-action="equals" class="equals-key">=</button>
<button type="button" data-number="0" class="zero-key">0</button>
<button type="button" data-action="decimal">.</button>
</div>
</main>
</body>
</html>
Each control is a real <button>, which provides built-in keyboard and touch behavior. The data-number, data-operator, and data-action attributes give JavaScript a clear description of each button’s purpose. Symbol-only controls have accessible labels.
2. Add basic styling
Create styles.css. CSS is not needed for arithmetic, but it makes the controls usable and recognizable as a calculator.
:root {
font-family: system-ui, sans-serif;
color-scheme: light dark;
}
* { box-sizing: border-box; }
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
background: #eef2f7;
}
.calculator {
width: min(92vw, 360px);
padding: 1rem;
border-radius: 1rem;
background: #1f2937;
box-shadow: 0 1rem 2rem rgb(0 0 0 / 20%);
}
h1 {
margin: 0 0 1rem;
color: white;
font-size: 1.25rem;
text-align: center;
}
.display {
display: block;
width: 100%;
min-height: 4rem;
margin-bottom: 1rem;
padding: .75rem;
overflow-x: auto;
border-radius: .5rem;
background: #111827;
color: white;
font-size: 2rem;
line-height: 1.5;
text-align: right;
white-space: nowrap;
}
.keys {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: .5rem;
}
button {
min-height: 3.25rem;
border: 0;
border-radius: .5rem;
background: #e5e7eb;
color: #111827;
cursor: pointer;
font: inherit;
font-size: 1.25rem;
}
button:hover { background: #d1d5db; }
button:focus-visible { outline: 3px solid #93c5fd; outline-offset: 2px; }
.operator-key { background: #f59e0b; }
.function-key { background: #9ca3af; }
.equals-key { grid-row: span 2; background: #22c55e; }
.zero-key { grid-column: span 2; }
3. Represent the calculator’s state
The calculator needs to remember what is displayed, the first number in a pending operation, the selected operator, and whether the next digit should replace the display. Add this beginning to script.js:
const display = document.querySelector("#display");
const keys = document.querySelector("#calculator-keys");
let currentValue = "0";
let storedValue = null;
let operator = null;
let waitingForOperand = false;
currentValueis the number currently shown. It remains a string while the user is typing, so values such as0.can be represented.storedValueis the first number in an operation.operatorstores+,-,*, or/.waitingForOperandtells the calculator that the next digit begins a new number.
4. Handle display updates, numbers, and decimals
function updateDisplay() {
display.textContent = currentValue;
}
function formatResult(value) {
if (!Number.isFinite(value)) {
return "Error";
}
// Hide common floating-point display artifacts.
return String(Number(value.toFixed(10)));
}
function inputNumber(number) {
if (currentValue === "Error" || waitingForOperand) {
currentValue = number;
waitingForOperand = false;
} else if (currentValue === "0") {
currentValue = number;
} else {
currentValue += number;
}
updateDisplay();
}
function inputDecimal() {
if (currentValue === "Error" || waitingForOperand) {
currentValue = "0.";
waitingForOperand = false;
} else if (!currentValue.includes(".")) {
currentValue += ".";
}
updateDisplay();
}
Button text is a string, while arithmetic needs numbers. Convert with Number() only when calculating. This avoids prematurely changing an in-progress value such as 0.. The decimal check prevents malformed input such as 1.2.3, and the replacement rule prevents sequences such as 0007. JavaScript number conversion and arithmetic operators are described in MDN’s math guide.
5. Perform the arithmetic explicitly
function calculate(first, second, selectedOperator) {
switch (selectedOperator) {
case "+":
return first + second;
case "-":
return first - second;
case "*":
return first * second;
case "/":
if (second === 0) {
throw new Error("Cannot divide by zero");
}
return first / second;
default:
return second;
}
}
The function converts no strings itself; its callers pass numeric values. Division by zero is checked before division, so the interface can show an error instead of displaying Infinity.
6. Connect operators and Equals
function handleOperator(nextOperator) {
const inputValue = Number(currentValue);
if (!Number.isFinite(inputValue)) {
resetCalculator();
return;
}
if (operator && storedValue !== null && !waitingForOperand) {
try {
const result = calculate(Number(storedValue), inputValue, operator);
currentValue = formatResult(result);
storedValue = Number(currentValue);
updateDisplay();
} catch {
showError();
return;
}
} else {
storedValue = inputValue;
}
operator = nextOperator;
waitingForOperand = true;
}
function handleEquals() {
if (operator === null || storedValue === null) {
return;
}
const first = Number(storedValue);
const second = Number(currentValue);
try {
const result = calculate(first, second, operator);
currentValue = formatResult(result);
storedValue = null;
operator = null;
waitingForOperand = true;
updateDisplay();
} catch {
showError();
}
}
When the user selects an operator, the displayed value becomes the stored first number. If another operator is selected after a second number has been entered, the pending operation is completed first. Equals then converts both values, calculates, formats, displays, and retains the result as the current value.
7. Add Clear, Delete, and error recovery
function deleteLastCharacter() {
if (currentValue === "Error" || waitingForOperand) {
return;
}
currentValue = currentValue.slice(0, -1);
if (currentValue === "" || currentValue === "-") {
currentValue = "0";
}
updateDisplay();
}
function resetCalculator() {
currentValue = "0";
storedValue = null;
operator = null;
waitingForOperand = false;
updateDisplay();
}
function showError() {
currentValue = "Error";
storedValue = null;
operator = null;
waitingForOperand = true;
updateDisplay();
}
Delete never leaves an empty display. After an error, entering a number starts over; Clear also resets every state variable. Pressing Equals without a stored value or operator simply does nothing.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →8. Register one event listener
Finish script.js with event delegation:
keys.addEventListener("click", (event) => {
const button = event.target.closest("button");
if (!button) {
return;
}
if (button.dataset.number !== undefined) {
inputNumber(button.dataset.number);
return;
}
if (button.dataset.operator !== undefined) {
handleOperator(button.dataset.operator);
return;
}
switch (button.dataset.action) {
case "decimal":
inputDecimal();
break;
case "equals":
handleEquals();
break;
case "clear":
resetCalculator();
break;
case "delete":
deleteLastCharacter();
break;
}
});
Rather than adding a separate inline onclick attribute to every button, this attaches one listener to the button container and identifies the clicked control. MDN recommends addEventListener() for event handling and notes the limitations of inline handlers: MDN events guide.
Complete JavaScript file
For convenience, here is the complete script.js file:
const display = document.querySelector("#display");
const keys = document.querySelector("#calculator-keys");
let currentValue = "0";
let storedValue = null;
let operator = null;
let waitingForOperand = false;
function updateDisplay() { display.textContent = currentValue; }
function formatResult(value) {
if (!Number.isFinite(value)) return "Error";
return String(Number(value.toFixed(10)));
}
function calculate(first, second, selectedOperator) {
switch (selectedOperator) {
case "+": return first + second;
case "-": return first - second;
case "*": return first * second;
case "/":
if (second === 0) throw new Error("Cannot divide by zero");
return first / second;
default: return second;
}
}
function inputNumber(number) {
if (currentValue === "Error" || waitingForOperand) {
currentValue = number;
waitingForOperand = false;
} else if (currentValue === "0") {
currentValue = number;
} else {
currentValue += number;
}
updateDisplay();
}
function inputDecimal() {
if (currentValue === "Error" || waitingForOperand) {
currentValue = "0.";
waitingForOperand = false;
} else if (!currentValue.includes(".")) {
currentValue += ".";
}
updateDisplay();
}
function handleOperator(nextOperator) {
const inputValue = Number(currentValue);
if (!Number.isFinite(inputValue)) { resetCalculator(); return; }
if (operator && storedValue !== null && !waitingForOperand) {
try {
const result = calculate(Number(storedValue), inputValue, operator);
currentValue = formatResult(result);
storedValue = Number(currentValue);
updateDisplay();
} catch { showError(); return; }
} else {
storedValue = inputValue;
}
operator = nextOperator;
waitingForOperand = true;
}
function handleEquals() {
if (operator === null || storedValue === null) return;
try {
const result = calculate(Number(storedValue), Number(currentValue), operator);
currentValue = formatResult(result);
storedValue = null;
operator = null;
waitingForOperand = true;
updateDisplay();
} catch { showError(); }
}
function deleteLastCharacter() {
if (currentValue === "Error" || waitingForOperand) return;
currentValue = currentValue.slice(0, -1);
if (currentValue === "" || currentValue === "-") currentValue = "0";
updateDisplay();
}
function resetCalculator() {
currentValue = "0";
storedValue = null;
operator = null;
waitingForOperand = false;
updateDisplay();
}
function showError() {
currentValue = "Error";
storedValue = null;
operator = null;
waitingForOperand = true;
updateDisplay();
}
keys.addEventListener("click", (event) => {
const button = event.target.closest("button");
if (!button) return;
if (button.dataset.number !== undefined) return inputNumber(button.dataset.number);
if (button.dataset.operator !== undefined) return handleOperator(button.dataset.operator);
switch (button.dataset.action) {
case "decimal": inputDecimal(); break;
case "equals": handleEquals(); break;
case "clear": resetCalculator(); break;
case "delete": deleteLastCharacter(); break;
}
});
Test the calculator
Save all three files and open index.html in your browser. The defer attribute ensures the external script runs after the HTML has been parsed. If you prefer, place the script immediately before </body>.
| Test | Expected result |
|---|---|
2 + 3 = |
5 |
8 - 10 = |
-2 |
4 × 6 = |
24 |
9 ÷ 3 = |
3 |
5 ÷ 0 = |
Error |
0.1 + 0.2 = |
Rounded display result |
| Second decimal point | Ignored |
| Clear | 0 |
| Delete | Removes the final character |
Why this example does not use eval()
A short calculator tutorial often builds a string such as 12+7*3 and passes it to eval(). That is not recommended here. eval() executes JavaScript represented as a string, not merely arithmetic. If untrusted text reaches it, an attacker may be able to execute arbitrary code. It can also be blocked by common Content Security Policy configurations. See MDN’s eval() reference.
The explicit switch is longer, but it limits the calculator to known operations and makes the behavior easier to inspect and extend. It avoids the arbitrary-code-execution risk associated with sending input to eval(); that is not the same as claiming the entire application has undergone a security audit.
If you need parentheses, unary operators, scientific functions, or normal precedence, use a tokenizer and parser—or a well-maintained, security-reviewed math-expression library. Do not replace eval() with another unsafe string-construction trick.
Rank #2
- 【2026 Newest Android 16 Tablet 】 The CUPEISI tablet equipped with the latest android 16 operating system. Powerful 2.0Ghz Octa-core processor, run smoother when open apps and loading the pages. Tablet passed the GMS certification, you can download kids apps from Google play. This tablet support widevine L1, Netflix.
- 【20GB RAM+128GB ROM+2TB Expansion】 Android 16 tablet comes with 20GB RAM (4GB fixed memory, 16GB virtual memory) 128GB ROM capacity and 2TB Micro SD card expansion (Micro SD card not included), large storage meets your daily entertainment and work what you need, for example, store photos, videos, songs, e-books and important files.
- 【Portable 2-in-1 Tablet PC】 Our CP31M tablet has passed GMS certification, tablet comes with bluetooth keyboard, wireless mouse and foldable protective case, it can flexibly turn tablet into a laptop mode or computer mode. By connecting the keyboard and wireless mouse through Bluetooth, it becomes an ultra portable mini laptop, perfect for home, school, and office use. Enables you to work and learn efficiently and quickly handle daily tasks, offers you limitless features and capabilities
- 【10.1 in HD Screen and HD Lens】 The stunning 10 In eye protection full screen has a larger visual area and a wider visual field, adopts a 1280*800 IPS HD touch screen, whether you play games, watch movies, read, take notes and work, it can bring you immersive visual. The tablet 10" inch equipped with a 8MP rear camera with auto focus and flash, shooting is equally clear during the day and night, Capture Your Wonderful Moments. 2MP front camera bring excellent clarity during video calls enjoyment.
- 【2.4G + 5G Dual WIFI + Bluetooth 5.0】 These two features are definitely the best combination if you choose this Android tablet from CUPEISI. With 5G WIFI (which also supports 2.4G WIFI), you can watch smoother Tiktok short videos, live streaming and more on the 10.1 Inch tablet. Bluetooth 5.0 connectivity is more stable and faster.
Floating-point results
JavaScript uses IEEE 754 double-precision numbers for ordinary calculations. As a result, 0.1 + 0.2 can internally produce approximately 0.30000000000000004. This tutorial rounds the displayed result to ten decimal places:
String(Number(value.toFixed(10)))
That is display formatting, not exact decimal arithmetic. A financial calculator should use an appropriate decimal arithmetic strategy instead of relying on ordinary binary floating-point numbers.
Optional keyboard support
Add this code after the click listener to support number keys, operators, Enter, Escape, and Backspace:
document.addEventListener("keydown", (event) => {
if (/^d$/.test(event.key)) {
inputNumber(event.key);
return;
}
if (event.key === ".") {
inputDecimal();
return;
}
if (["+", "-", "*", "/"].includes(event.key)) {
handleOperator(event.key);
return;
}
if (event.key === "Enter" || event.key === "=") {
event.preventDefault();
handleEquals();
return;
}
if (event.key === "Escape") {
resetCalculator();
return;
}
if (event.key === "Backspace") {
deleteLastCharacter();
}
});
Keyboard support supplements the buttons; it should not turn the display into an unrestricted text field. Buttons remain important for mouse, touch, focus, and screen-reader users.
Accessibility and usability checklist
- Use actual buttons rather than clickable
<div>elements. - Keep a visible
:focus-visibleoutline. - Use an
<output>element or a properly labeled read-only input for the result. - Use
aria-live="polite"so result changes can be announced without aggressively interrupting assistive technology. - Give symbols such as
×,÷, and−accessible names. - Maintain sufficient color contrast.
- Do not communicate an error through color alone; display the text
Error. - Test navigation with Tab, Enter, Space, Escape, and Backspace.
Known limitations and behavior
- Sequential evaluation: chained input is calculated one operation at a time.
2 + 3 × 4can become(2 + 3) × 4 = 20, not14. - Parentheses: unsupported; they require an expression parser.
- Negative values: subtraction can produce a negative result, but this interface does not include a dedicated sign-toggle control.
- Repeated Equals: Equals does not repeat the previous operation because the example clears the operator after calculating.
- Scientific notation: not entered through the interface.
- Large results: non-finite values become
Error, while very long finite values may still require improved formatting.
Troubleshooting
Buttons appear but do nothing
Check that script.js is in the same folder, the src filename matches, and the browser console has no syntax errors. Also verify the display and calculator-keys IDs.
querySelector() returns null
Usually the selector is misspelled or the script ran before the element existed. Keep defer in the head, move the script to the end of the body, or wait for DOMContentLoaded.
Recommended Free Tools
Division shows Infinity
Make sure the divisor check is present in calculate(). The intended recovery is a visible Error state followed by a new number or Clear.
Decimal input is malformed
Ensure inputDecimal() checks !currentValue.includes("."). Do not use parseFloat() as validation: it can accept a valid prefix and silently ignore invalid trailing characters.
The result has too many digits
Use display formatting such as toFixed(10), while remembering that formatting does not provide exact financial or scientific precision.
Publish the calculator
Test locally first by opening index.html. For a quick public demo, CodePen’s free plan supports public Pens and up to three files, although free work is public. For a longer-term learning project, place the three files in a GitHub repository and publish the static site with GitHub Pages. GitHub documents Pages availability for public repositories on its Free plan; check the current GitHub plan documentation for private-repository and organization-specific conditions.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsStackBlitz and CodeSandbox are useful when the project grows into a larger JavaScript application, but their cloud tooling is unnecessary for this dependency-free calculator. No paid plan materially improves the basic exercise.
Three calculator designs
- Two-number calculator: Two inputs and one selected operation. This is the easiest design for learning basic DOM events.
- Sequential button calculator: The state-machine approach used here. It is a good balance of usability and understandable JavaScript.
- Expression calculator: Supports complete expressions, parentheses, and precedence, but needs tokenization and parsing rather than blindly evaluating a string.
Once this version works, useful next projects include a tip calculator, unit converter, mortgage calculator, expense tracker, or scientific calculator backed by a real parser.
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.

