Build a Pocket Calculator in JavaScript
Build a responsive browser calculator with vanilla JavaScript, HTML, and CSS.
Introduction
30 Second Summary
Quick calculations often interrupt whatever you are doing. A small calculator keeps the answer close without adding clutter.
In this project, you will build a pocket calculator with JavaScript. You will connect a responsive keypad to calculation logic that handles decimals, clears its state, formats results, and catches division by zero.
What You'll Build
You will open your calculator in a browser, tap its buttons, and watch each calculation appear instantly on a large display.
By the end of this project, you'll have:
- A responsive calculator with a dark card, a large display, and a four-column keypad.
- Clickable number controls that build decimal values on screen and reset with the clear button.
- Four arithmetic operations that show formatted results and display Cannot divide by zero for invalid division.
- Secret Mission: Add number, operator, Enter, and Escape keyboard controls to the finished calculator.
Are there any prerequisites?
Basic JavaScript familiarity helps with the calculator logic.
You only need Visual Studio Code and a web browser. No additional installation is required.
Before We Start
Before you start building, take a moment to commit to the pocket calculator you want to create. Connecting its purpose to event-driven JavaScript state gives every coding choice a clear goal.
Set Up the Project Files
Your calculator needs a workspace where every part has a clear home. A mixed file makes later changes harder to find.
HTML holds the page structure. CSS holds the styling.
JavaScript holds the behavior. A three-file workspace in Visual Studio Code keeps each change easy to locate.
In this step, get ready to:
- Create a trusted pocket-calculator workspace in Visual Studio Code.
- Create the three project files for structure, styling, and behavior.
- Open the starter page in a web browser to confirm it runs locally.
Create the trusted project folder
A Visual Studio Code workspace keeps the calculator files together. You will place it on your Desktop so it stays easy to find.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter to open Visual Studio Code.
- Click File in the top menu bar.
- Select Open Folder....
- Select your Desktop in the folder picker.
- Create a folder named pocket-calculator with the new-folder control in the folder picker.
The trust prompt protects you from unfamiliar code. This folder is safe to trust because you created it yourself.
- Choose the pocket-calculator folder to open it.
- Select Yes, I trust the authors when the trust prompt appears.
That is your workspace ready. You should see pocket-calculator at the top of the Explorer sidebar.
Folder Not Opening?
- Confirm that you created pocket-calculator on your Desktop.
- Return to Open Folder... if Visual Studio Code opened without the workspace name.
- Reopen the folder if you closed the trust prompt before making a choice.
- Still stuck? Help me open my calculator folder in Visual Studio Code.
Create the three project files
The starter page needs one file for HTML. The empty CSS and JavaScript files establish the structure you will build on later.
- Use New File... in the Explorer sidebar to create index.html.
- Use New File... in the Explorer sidebar to create style.css.
You should now see index.html plus style.css in the Explorer sidebar.
- Use New File... in the Explorer sidebar to create script.js.
All three files should now appear inside pocket-calculator.
The HTML shell gives the browser a complete page to read. It also connects the empty styling file and the empty behavior file.
- Add the starter page to index.html by pasting this code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pocket Calculator</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<h1>Pocket Calculator</h1>
</body>
</html>
What Does This HTML Shell Do?
- The <title> element names the browser tab.
- The <link rel="stylesheet" href="style.css"> element connects the page to style.css.
- The <script src="script.js" defer></script> element loads script.js after the browser reads the page.
- The <h1> element gives you a visible result to verify.
- Save index.html.
Before you open the page, ask yourself what visible result this small HTML shell should produce.
- Right-click index.html in the Explorer sidebar.
- Select Open in Integrated Browser.
You will see the heading Pocket Calculator on the page. Your local browser page is working.
Heading Missing From the Browser?
- Confirm that you saved index.html before opening it.
- Check that the body contains <h1>Pocket Calculator</h1>.
- Confirm that every opening HTML tag has its matching closing tag.
- Still stuck? Help me find why my Pocket Calculator heading is missing.
✔️ Awesome, I've got everything!
Your starter page is complete. The two companion files stay empty for now.
ⓧ I'd like to double check the full code
The complete starter index.html file is shown below for comparison.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pocket Calculator</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<h1>Pocket Calculator</h1>
</body>
</html>
This page links the project files before displaying the heading. The style.css and script.js files remain empty.
Your three-file workspace is ready. Next, you will turn the plain heading into a recognizable calculator interface.
Build the Calculator Interface
Your trusted Visual Studio Code workspace now loads all three project files in the browser. The heading confirms that the foundation works.
The page still needs the controls people expect from a calculator. This step uses semantic HTML elements plus CSS Grid to turn that heading into a responsive pocket calculator.
In this step, get ready to:
- Build the calculator display plus its complete keypad.
- Style the interface as a responsive four-column calculator.
- Test the finished keypad to identify the next behavior gap.
Build the semantic keypad
The output element gives calculation results a dedicated display. Each button uses type="button" so its behavior can be connected to click events later.
- Select index.html in the Explorer sidebar.
- Select everything between <body> and </body>.
- Replace the selected heading with the calculator structure by pasting this code:
<main class="calculator">
<h1>Pocket Calculator</h1>
<output id="display">0</output>
<div class="keys">
<button id="clear" class="clear wide" type="button">C</button>
<button class="operator" type="button" data-operator="÷">÷</button>
<button class="operator" type="button" data-operator="×">×</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 class="operator" type="button" data-operator="−">−</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 class="operator" type="button" data-operator="+">+</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 id="equals" class="equals" type="button">=</button>
<button class="wide" type="button" data-number="0">0</button>
<button id="decimal" type="button">.</button>
</div>
</main>
What does this structure do?
- The calculator class identifies the complete card for styling.
- The display element starts at 0. It becomes the visible result screen.
- The data-number attributes identify digit controls for later click handling.
- The data-operator attributes store the arithmetic symbol represented by each operator control.
- Save index.html.
- Reload index.html in your local browser.
You should see a 0 display above the complete keypad. The controls still use the browser's default appearance.
Missing a button or display?
- Confirm that the new main element remains between the existing <body> tags.
- Check that every opening button tag has a matching closing button tag.
- Still stuck? Help me compare my calculator HTML with the required keypad structure.
✔️ Awesome, I've got everything!
Your calculator structure is complete. Keep index.html saved before styling the interface.
ⓧ I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pocket Calculator</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<main class="calculator">
<h1>Pocket Calculator</h1>
<output id="display">0</output>
<div class="keys">
<button id="clear" class="clear wide" type="button">C</button>
<button class="operator" type="button" data-operator="÷">÷</button>
<button class="operator" type="button" data-operator="×">×</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 class="operator" type="button" data-operator="−">−</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 class="operator" type="button" data-operator="+">+</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 id="equals" class="equals" type="button">=</button>
<button class="wide" type="button" data-number="0">0</button>
<button id="decimal" type="button">.</button>
</div>
</main>
</body>
</html>
Style the calculator card
The structure identifies each control. The stylesheet now turns those controls into a centered card with a four-column keypad.
- Switch to style.css in the Visual Studio Code editor.
- Add the page foundation by pasting this code:
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
padding: 1rem;
background: #eef2f7;
font-family: system-ui, sans-serif;
}
How does the page foundation work?
- The universal selector keeps padding inside each element's measured size.
- The body fills the browser height. Its grid placement centers the calculator.
- The page padding preserves space around the card on narrow screens.
- Save style.css.
- Reload index.html in your local browser.
You should see the calculator content centered on a pale background.
Calculator not centered?
- Confirm that style.css still contains the complete body selector.
- Still stuck? Help me find why my calculator content is not centered.
- Add the dark calculator shell below the existing body styles by pasting this code:
.calculator {
width: min(100%, 22rem);
padding: 1.25rem;
border-radius: 1.25rem;
background: #172033;
box-shadow: 0 1.25rem 3rem rgb(15 23 42 / 0.25);
}
What shapes the card?
- The width can shrink with the browser. Its maximum keeps the calculator compact.
- The dark background groups the display with the keypad.
- The rounded corners plus shadow lift the card from the pale page.
- Save style.css.
- Reload index.html in your local browser.
You should see a compact dark card surrounding the calculator controls.
Dark card not showing?
- Confirm that class="calculator" is still present on the main element.
- Still stuck? Help me connect my calculator class to its CSS selector.
- Style the calculator heading below the card styles by pasting this code:
h1 {
margin: 0 0 1rem;
color: #f8fafc;
font-size: 1.25rem;
text-align: center;
}
Why style the heading separately?
- The light text gives the heading enough contrast against the dark card.
- The bottom margin separates the title from the display.
- The centered alignment keeps the title level with the keypad.
- Save style.css.
- Reload index.html in your local browser.
You should see the Pocket Calculator heading centered in light text.
Heading hard to read?
- Check that the closing brace for the .calculator selector appears before the h1 selector.
- Still stuck? Help me fix the calculator heading styles.
- Turn the output into a calculator screen by pasting this code below the heading styles:
#display {
display: block;
width: 100%;
min-height: 5rem;
margin-bottom: 1rem;
padding: 1rem;
overflow-wrap: anywhere;
border-radius: 0.75rem;
background: #f8fafc;
color: #0f172a;
font-size: 2rem;
text-align: right;
}
What makes this a display?
- The full width creates a screen across the calculator card.
- The large right-aligned text follows the layout of a pocket calculator.
- The wrapping rule prevents a long result from escaping the display.
- Save style.css.
- Reload index.html in your local browser.
You should see the 0 inside a large light display at the top of the card.
Display still looks like plain text?
- Confirm that the HTML display uses id="display".
- Still stuck? Help me match my display element to its CSS selector.
- Arrange the controls into four columns by pasting this code below the display styles:
.keys {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.65rem;
}
How does the keypad grid work?
- The keys container becomes a grid.
- The four equal columns create the calculator's keypad layout.
- The gap keeps neighboring controls visually separate.
- Save style.css.
- Reload index.html in your local browser.
You should see the keypad arranged into four consistent columns.
Buttons still in one row?
- Confirm that the keypad wrapper uses class="keys".
- Still stuck? Help me fix the four-column keypad grid.
- Give every control the same button foundation by pasting this code below the grid styles:
button {
min-height: 3.5rem;
border: 0;
border-radius: 0.75rem;
background: #e2e8f0;
color: #0f172a;
font: inherit;
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
}
What creates the button foundation?
- The minimum height creates large controls that are easy to select.
- The shared colors plus rounded corners give every control a consistent starting style.
- The pointer cursor signals that each control is interactive.
- Save style.css.
- Reload index.html in your local browser.
You should see large gray buttons with rounded corners across the keypad.
Buttons still use browser styling?
- Check that the button selector appears outside the closing brace for .keys.
- Still stuck? Help me find why my shared button styles are missing.
- Add hover states plus keyboard focus styling by pasting this code below the button foundation:
button:hover {
background: #cbd5e1;
}
button:focus-visible {
outline: 3px solid #38bdf8;
outline-offset: 2px;
}
.operator {
background: #f59e0b;
}
.operator:hover {
background: #fbbf24;
}
How do the interaction states help?
- The hover colors provide visual feedback when the pointer is over a control.
- The focus outline makes keyboard navigation visible.
- The orange background separates arithmetic operators from number controls.
- Save style.css.
- Reload index.html in your local browser.
- Move your pointer over the ÷ button.
You should see the orange operator become lighter under the pointer.
Operator colors not changing?
- Confirm that each arithmetic control uses class="operator".
- Still stuck? Help me troubleshoot the operator hover styles.
- Finish the special control colors plus wide key layout by pasting this code at the bottom of style.css:
.clear {
background: #fb7185;
}
.clear:hover {
background: #fda4af;
}
.equals {
grid-row: span 2;
background: #22c55e;
}
.equals:hover {
background: #4ade80;
}
.wide {
grid-column: span 2;
}
What finishes the keypad layout?
- The pink clear control stands apart from number entry.
- The green equals control spans two rows to emphasize calculation.
- The wide class lets the clear control plus zero occupy two columns.
- Save style.css.
- Reload index.html in your local browser.
You should see a pink wide clear control plus a green equals control. The zero control should also span two columns.
Special keys have the wrong size?
- Confirm that the clear control has both clear plus wide classes.
- Confirm that the equals control uses the equals class.
- Still stuck? Help me fix the clear and equals key layout.
✔️ Awesome, I've got everything!
Your complete calculator stylesheet is saved. The card is ready for its behavior layer.
ⓧ I'd like to double check the full code
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
padding: 1rem;
background: #eef2f7;
font-family: system-ui, sans-serif;
}
.calculator {
width: min(100%, 22rem);
padding: 1.25rem;
border-radius: 1.25rem;
background: #172033;
box-shadow: 0 1.25rem 3rem rgb(15 23 42 / 0.25);
}
h1 {
margin: 0 0 1rem;
color: #f8fafc;
font-size: 1.25rem;
text-align: center;
}
#display {
display: block;
width: 100%;
min-height: 5rem;
margin-bottom: 1rem;
padding: 1rem;
overflow-wrap: anywhere;
border-radius: 0.75rem;
background: #f8fafc;
color: #0f172a;
font-size: 2rem;
text-align: right;
}
.keys {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.65rem;
}
button {
min-height: 3.5rem;
border: 0;
border-radius: 0.75rem;
background: #e2e8f0;
color: #0f172a;
font: inherit;
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
}
button:hover {
background: #cbd5e1;
}
button:focus-visible {
outline: 3px solid #38bdf8;
outline-offset: 2px;
}
.operator {
background: #f59e0b;
}
.operator:hover {
background: #fbbf24;
}
.clear {
background: #fb7185;
}
.clear:hover {
background: #fda4af;
}
.equals {
grid-row: span 2;
background: #22c55e;
}
.equals:hover {
background: #4ade80;
}
.wide {
grid-column: span 2;
}
- Predict whether the display will change when you select a number.
- Click the 7 button.
You should see the display stay at 0. This intended shortfall proves the visual layer is complete while JavaScript behavior is still missing.
Why does the keypad stay still?
The controls provide visible interaction states through CSS. The empty script.js file has no click listeners to update the display.
This clean boundary lets the next step connect number entry to the interface you just tested.
That visual layer is complete: your browser now shows a responsive pocket calculator with a full keypad. Next, you will connect its digit controls to visible calculator state.
Make Number Entry Interactive
Your calculator now has a polished keypad that looks ready to use. However, every button leaves the display unchanged.
Your JavaScript uses DOM selection to connect the page to your script. Click events then update the calculator state behind the display.
In this step, get ready to:
- Select the display plus the digit controls.
- Manage number entry with reusable functions.
- Connect decimal plus clear clicks to the calculator state.
Connect digit clicks to the display
The script needs references to the existing HTML controls before it can react to them. It also needs state that records the number currently shown.
- In the Visual Studio Code workspace from earlier, select script.js from the Explorer sidebar.
- Replace the empty file with the display selectors plus the number-entry logic below:
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll("[data-number]");
const decimalButton = document.getElementById("decimal");
const clearButton = document.getElementById("clear");
let currentInput = "0";
let waitingForSecondOperand = false;
let hasError = false;
function updateDisplay() {
display.textContent = currentInput;
}
function inputNumber(number) {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = number;
waitingForSecondOperand = false;
} else {
currentInput = currentInput === "0" ? number : currentInput + number;
}
updateDisplay();
}
What does this code do?
- The display constant points to the calculator screen.
- The numberButtons constant holds every element with a data-number attribute.
- The currentInput variable stores the text shown on the screen.
- The updateDisplay() function copies that stored text into the output element.
- The inputNumber() function replaces the initial zero before appending later digits.
- Register one click listener for every digit button by adding this code at the bottom of script.js:
numberButtons.forEach((button) => {
button.addEventListener("click", () => inputNumber(button.dataset.number));
});
How do digit clicks work?
- The forEach() loop gives every digit button the same click behavior.
- Each click reads the button's data-number value through dataset.number.
- That value passes into inputNumber() before the display updates.
Before you test the first live input, what number do you expect after pressing 1 followed by 2?
- Save script.js.
- Switch back to the browser from earlier.
- Reload index.html.
- Click 1.
- Click 2.
You should see 12 on the display. Your calculator now responds to its first button sequence.
Digits not updating the display?
- Confirm that you saved script.js before reloading the page.
- Check that numberButtons uses the selector [data-number].
- Check that the listener passes button.dataset.number into inputNumber().
- Still stuck? Help me debug digit clicks in my pocket calculator.
Make clear restore the starting state
The clear button needs one reset function that restores every piece of input state. Keeping that reset in one place makes the starting state consistent.
- In script.js, locate the closing brace of updateDisplay().
- Insert the clear function immediately below that brace:
function clearCalculator() {
currentInput = "0";
waitingForSecondOperand = false;
hasError = false;
updateDisplay();
}
How does the reset work?
- The clearCalculator() function restores currentInput to 0.
- It also restores both state flags to false.
- The final call to updateDisplay() makes the reset visible immediately.
- Connect the clear button by adding this listener below the digit listener at the bottom of script.js:
clearButton.addEventListener("click", clearCalculator);
How does the clear click work?
The listener passes clearCalculator as the function to run after a click. One click now resets both the stored state and the visible output.
Before you press C, what value do you expect the display to return to?
- Save script.js.
- Switch back to the browser.
- Reload index.html.
- Click 4.
- Click 2.
- Click C.
You should see 0 again. The clear button now restores a clean starting point.
Clear button leaving the old number?
- Confirm that clearButton selects the element with the clear ID.
- Check that clearCalculator() calls updateDisplay() after resetting the state.
- Still stuck? Help me debug the clear button in my pocket calculator.
Accept one decimal point
A decimal can appear only once in the current input. The decimal handler checks the stored string before adding another point.
- In script.js, locate the closing brace of inputNumber().
- Insert the decimal function immediately below that brace:
function inputDecimal() {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = "0.";
waitingForSecondOperand = false;
} else if (!currentInput.includes(".")) {
currentInput += ".";
}
updateDisplay();
}
How does decimal entry work?
- The error check prepares the calculator to recover before accepting new input.
- The second-operand check starts a fresh decimal value with 0..
- The includes() check prevents a second decimal point from entering the same number.
- The final display update shows an accepted decimal immediately.
- Connect the decimal button by inserting this listener above the existing clear listener:
decimalButton.addEventListener("click", inputDecimal);
How does the decimal click work?
The decimal listener calls inputDecimal() for every click. The function decides whether the new point belongs in currentInput.
Before you run the full check, what do you expect after pressing the decimal button twice in one number?
- Save script.js.
- Switch back to the browser.
- Reload index.html.
- Click 1.
- Click 2.
- Click ..
- Click 5.
- Click . once more.
You should see 12.5 on the display. The extra decimal point is ignored.
Before you press +, do you think the display will change?
- Click +.
The display should stay at 12.5. This is the intended limitation because the operator buttons do not have listeners yet.
- Click C.
You should see 0. Digit entry, decimal entry, plus clearing now work from the keypad.
Seeing the wrong decimal or clear result?
- Check that inputDecimal() tests currentInput.includes(".") before adding a point.
- Confirm that the decimal listener appears above the clear listener near the bottom of script.js.
- Treat the inactive operator buttons as the expected result for this step.
- Still stuck? Help me debug decimal entry in my pocket calculator.
✔️ Awesome, I've got everything!
Your script.js file now matches the number-entry version of the calculator.
ⓧ I'd like to double check the full code
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll("[data-number]");
const decimalButton = document.getElementById("decimal");
const clearButton = document.getElementById("clear");
let currentInput = "0";
let waitingForSecondOperand = false;
let hasError = false;
function updateDisplay() {
display.textContent = currentInput;
}
function clearCalculator() {
currentInput = "0";
waitingForSecondOperand = false;
hasError = false;
updateDisplay();
}
function inputNumber(number) {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = number;
waitingForSecondOperand = false;
} else {
currentInput = currentInput === "0" ? number : currentInput + number;
}
updateDisplay();
}
function inputDecimal() {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = "0.";
waitingForSecondOperand = false;
} else if (!currentInput.includes(".")) {
currentInput += ".";
}
updateDisplay();
}
numberButtons.forEach((button) => {
button.addEventListener("click", () => inputNumber(button.dataset.number));
});
decimalButton.addEventListener("click", inputDecimal);
clearButton.addEventListener("click", clearCalculator);
Your calculator can now collect clean numeric input from button clicks. Next, you will give the operator buttons the state plus calculation logic needed to produce results.
Calculate Results and Handle Errors
Your calculator now accepts digits, decimals, and clear clicks. Its operator buttons still cannot turn those inputs into a result.
This step adds the calculator state needed to remember an operation between clicks. Explicit arithmetic functions complete each calculation without using eval().
In this step, get ready to:
- Store the calculation state between button clicks.
- Calculate four arithmetic operations with explicit rules.
- Present each calculation outcome clearly.
Remember the selected operation
Each calculation starts with a displayed number plus an operator. The calculator needs references for those buttons before it can store that choice.
- In script.js, select the declarations from const display through let hasError.
- Replace the selected declarations with this code:
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll("[data-number]");
const operatorButtons = document.querySelectorAll("[data-operator]");
const decimalButton = document.getElementById("decimal");
const clearButton = document.getElementById("clear");
const equalsButton = document.getElementById("equals");
let currentInput = "0";
let firstOperand = null;
let operator = null;
let waitingForSecondOperand = false;
let hasError = false;
What does this state remember?
- The operatorButtons collection represents the four arithmetic buttons.
- The equalsButton reference represents the button that requests a result.
- The firstOperand variable stores the first displayed number.
- The operator variable stores the selected arithmetic symbol.
- Save script.js.
- Reload index.html in the local browser.
- Click C.
You should still see 0. The new references have loaded without disrupting the existing controls.
Did the buttons stop responding?
Check each element ID in the declarations. Confirm that every quote plus closing parenthesis matches the code above.
Still stuck? Help me debug the calculator declarations.
Clearing the display must also discard any stored operation. This keeps an old calculation from affecting the next one.
- In script.js, select the existing clearCalculator() function.
- Replace the selected function with this version:
function clearCalculator() {
currentInput = "0";
firstOperand = null;
operator = null;
waitingForSecondOperand = false;
hasError = false;
updateDisplay();
}
What does clearing reset?
The function restores every piece of calculation state. The next number starts from a clean display with no stored operator.
- Save script.js.
- Reload index.html in the local browser.
- Enter 42 with the number buttons.
- Click C.
You should see 0 again. The clear flow now resets the expanded state.
Does clear leave the old number?
Confirm that clearCalculator() ends with updateDisplay(). Check that currentInput returns to "0".
Still stuck? Help me debug the clear function.
An operator click must convert the displayed string into a number. It must also prepare the display for the second operand.
- In script.js, find the closing brace of inputDecimal().
- Add this function below inputDecimal():
function chooseOperator(nextOperator) {
if (hasError) {
return;
}
firstOperand = Number(currentInput);
operator = nextOperator;
waitingForSecondOperand = true;
}
How does operator selection work?
- The Number() conversion turns the displayed string into a numeric operand.
- The nextOperator value records the selected symbol.
- The waitingForSecondOperand flag makes the next digit replace the first number.
- Near the bottom of script.js, find the closing brace of the numberButtons.forEach listener.
- Add this operator listener below that listener:
operatorButtons.forEach((button) => {
button.addEventListener("click", () => chooseOperator(button.dataset.operator));
});
How do four buttons share one listener?
Each operator button provides its symbol through dataset.operator. Every symbol passes into the same chooseOperator() function.
Before you test the operator flow, which number do you think will remain on the display?
- Save script.js.
- Reload index.html in the local browser.
- Click 8.
- Click ×.
- Click 7.
You should see 7. The operator stores the first operand before the next digit starts the second one.
Does the display show 87?
Check that chooseOperator() sets waitingForSecondOperand to true. Confirm that the operator listener passes button.dataset.operator.
Still stuck? Help me debug the operator flow.
Define the arithmetic rules
The selected symbol now needs a matching arithmetic rule. A switch keeps all four supported operations visible inside one function.
Why use explicit arithmetic?
An explicit calculation function accepts only the four supported symbols. Each case shows exactly how its result is produced.
Using eval() would dynamically execute a string as JavaScript. The explicit function keeps the calculation flow controlled.
- In script.js, find the closing brace of chooseOperator().
- Add this function below chooseOperator():
function calculate(firstNumber, secondNumber, selectedOperator) {
switch (selectedOperator) {
case "+":
return firstNumber + secondNumber;
case "−":
return firstNumber - secondNumber;
case "×":
return firstNumber * secondNumber;
case "÷":
return secondNumber === 0 ? null : firstNumber / secondNumber;
default:
return secondNumber;
}
}
What does this function return?
- Each case returns the result for one calculator symbol.
- The division case returns null when the second operand is zero.
- The default case returns the second operand when no supported symbol matches.
- Save script.js.
- Reload index.html in the local browser.
- Click 4.
- Click +.
- Click 2.
You should see 2. The first operand remains stored while the second operand replaces the display.
Did the calculator stop responding?
Check the braces around the switch cases. Confirm that the operator symbols match the symbols used by the buttons.
Still stuck? Help me debug the arithmetic function.
Display results and handle division by zero
The result flow must reject incomplete calculations before it changes the display. It must also format valid numbers plus turn division by zero into a readable message.
- In script.js, find the closing brace of calculate().
- Add this result function below calculate():
function calculateResult() {
if (
firstOperand === null ||
operator === null ||
waitingForSecondOperand ||
hasError
) {
return;
}
const secondOperand = Number(currentInput);
const result = calculate(firstOperand, secondOperand, operator);
if (result === null) {
currentInput = "Cannot divide by zero";
hasError = true;
} else {
currentInput = Number(result.toFixed(10)).toString();
}
firstOperand = null;
operator = null;
waitingForSecondOperand = true;
updateDisplay();
}
How does a result reach the display?
- The opening condition ignores an incomplete calculation.
- The second displayed string passes through Number() before calculation.
- The toFixed(10) call rounds floating-point noise to ten decimal places.
- A null result changes the display to Cannot divide by zero.
- The final state reset prepares the calculator for the next input.
The result function needs one final connection to the equals button. Replacing the listener section keeps every button binding together.
- Near the bottom of script.js, select the listener code from numberButtons.forEach through the clearButton listener.
- Replace the selected listener code with this complete group:
numberButtons.forEach((button) => {
button.addEventListener("click", () => inputNumber(button.dataset.number));
});
operatorButtons.forEach((button) => {
button.addEventListener("click", () => chooseOperator(button.dataset.operator));
});
decimalButton.addEventListener("click", inputDecimal);
clearButton.addEventListener("click", clearCalculator);
equalsButton.addEventListener("click", calculateResult);
What completes the click flow?
The number plus operator listeners pass button data into reusable functions. The final listener calls calculateResult whenever you click equals.
Before you run the final checks, what result do you expect from each calculation?
- Save script.js.
- Reload index.html in the local browser.
- Complete 8 × 7 = with the calculator buttons.
You should see 56.
- Complete 0.1 + 0.2 = with the calculator buttons.
You should see 0.3. The result formatting removes the familiar floating-point noise.
- Complete 9 ÷ 0 = with the calculator buttons.
You should see Cannot divide by zero.
- Click C.
You should see 0 again. That confirms the calculator recovers from its error state.
Is a result missing or incorrect?
- Check that every operator symbol in calculate() matches the corresponding button symbol.
- Confirm that the equals listener passes calculateResult without parentheses.
- Verify that the division case checks whether secondNumber equals 0.
- Still stuck? Help me debug the calculator results.
✔️ Awesome, I've got everything!
Your script.js file now contains the complete click-controlled calculation flow. Make sure you saved it before moving on.
ⓧ I'd like to double check the full code
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll("[data-number]");
const operatorButtons = document.querySelectorAll("[data-operator]");
const decimalButton = document.getElementById("decimal");
const clearButton = document.getElementById("clear");
const equalsButton = document.getElementById("equals");
let currentInput = "0";
let firstOperand = null;
let operator = null;
let waitingForSecondOperand = false;
let hasError = false;
function updateDisplay() {
display.textContent = currentInput;
}
function clearCalculator() {
currentInput = "0";
firstOperand = null;
operator = null;
waitingForSecondOperand = false;
hasError = false;
updateDisplay();
}
function inputNumber(number) {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = number;
waitingForSecondOperand = false;
} else {
currentInput = currentInput === "0" ? number : currentInput + number;
}
updateDisplay();
}
function inputDecimal() {
if (hasError) {
clearCalculator();
}
if (waitingForSecondOperand) {
currentInput = "0.";
waitingForSecondOperand = false;
} else if (!currentInput.includes(".")) {
currentInput += ".";
}
updateDisplay();
}
function chooseOperator(nextOperator) {
if (hasError) {
return;
}
firstOperand = Number(currentInput);
operator = nextOperator;
waitingForSecondOperand = true;
}
function calculate(firstNumber, secondNumber, selectedOperator) {
switch (selectedOperator) {
case "+":
return firstNumber + secondNumber;
case "−":
return firstNumber - secondNumber;
case "×":
return firstNumber * secondNumber;
case "÷":
return secondNumber === 0 ? null : firstNumber / secondNumber;
default:
return secondNumber;
}
}
function calculateResult() {
if (
firstOperand === null ||
operator === null ||
waitingForSecondOperand ||
hasError
) {
return;
}
const secondOperand = Number(currentInput);
const result = calculate(firstOperand, secondOperand, operator);
if (result === null) {
currentInput = "Cannot divide by zero";
hasError = true;
} else {
currentInput = Number(result.toFixed(10)).toString();
}
firstOperand = null;
operator = null;
waitingForSecondOperand = true;
updateDisplay();
}
numberButtons.forEach((button) => {
button.addEventListener("click", () => inputNumber(button.dataset.number));
});
operatorButtons.forEach((button) => {
button.addEventListener("click", () => chooseOperator(button.dataset.operator));
});
decimalButton.addEventListener("click", inputDecimal);
clearButton.addEventListener("click", clearCalculator);
equalsButton.addEventListener("click", calculateResult);
This reference matches the finished script.js file for the click-controlled calculator.
That is the full click-controlled calculator working. It now performs four operations, formats decimal results, and recovers cleanly from division by zero.
Secret mission
Add Keyboard Controls
Your calculator responds to clicks, but quick calculations still require a mouse. Add keyboard controls for numbers, decimals, operators, results, and clearing while preserving every original button control.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no cloud resources or ongoing costs. Your options are to keep the folder, pause by closing the local windows, or delete the folder.
Resources you used:
- The local pocket-calculator folder containing your finished calculator project.
Keep everything running
No action is needed. Choose this option if you want to keep testing or improving your calculator.
- Leave the trusted pocket-calculator workspace open in Visual Studio Code.
- Keep the calculator open in your browser for more click or keyboard testing.
Pause - I'll come back to this later
Closing the local windows pauses your work without removing anything. The project files remain ready for your next session.
- Close the calculator browser tab.
- Close the pocket-calculator workspace window in Visual Studio Code.
- Keep the pocket-calculator folder on your computer.
Delete - I don't want to use this again
Deleting the local folder permanently removes your calculator code. A separate backup is your only recovery point after removal.
- In the Explorer sidebar of the trusted workspace, right-click pocket-calculator.
- Copy the folder's full path from the context menu.
- Record the copied path here: full path to your pocket-calculator folder.
- Close the calculator browser tab.
- Close Visual Studio Code.
- Start a Windows Command Prompt window from system search.
- Delete the pocket-calculator folder by running this command:
rmdir /s /q "[[PROJECT_FOLDER="full path to your pocket-calculator folder"]]"
What does this command do?
This removes the pocket-calculator folder at the recorded path. Every project file inside that folder is deleted with it.
- Return to the folder that previously contained pocket-calculator in your file browser.
You should no longer see the pocket-calculator folder listed.
Folder still there?
- Check that the recorded path ends with pocket-calculator.
- Close any file from that folder before running the command again.
- Help me troubleshoot this folder deletion.
Nice Work!
Nice Work!
Your pocket calculator is complete! It now responds to button clicks and keyboard input while keeping every calculation visible in the browser.
You've learned how to:
- Build a responsive calculator interface with semantic HTML buttons. Arrange its keypad with CSS Grid.
- Connect button clicks to reusable JavaScript event handlers. Synchronize the visible display with calculator state.
- Convert displayed strings into numbers for explicit arithmetic. Format floating-point results. Recover from division by zero with a visible message.
- Complete the optional Secret Mission by adding keyboard controls. Reuse the calculator's existing functions for every handled key. Translate physical operator keys into the matching display symbols.
Ready to quiz yourself?