JavaScript Tutorial
JavaScript Project: Tip Calculator
Split a bill: bill amount, tip percent, and people. Show the tip and the total per person.
What you will build
A bill splitter. The visitor enters the bill, a tip percent, and how many people share the cost. The page shows the tip in currency and the total each person pays.
Money on a page is still a number in the script. You convert the three fields with Number, guard against empty or invalid input, then format the results with toFixed(2) so cents stay two digits.
Open an example with Try it in JavaScript. That loads /javascript/try — a live page and a console.
Methods you will use
| Method | Job on this page |
|---|---|
Number | Turns each field string into a numeric value |
toFixed | Formats tip and totals to two decimal places |
input / .value | Reads bill, percent, and people |
isFinite | Rejects empty fields, letters, and Infinity |
click | Runs the split when the visitor asks for it |
Number("") is 0. That would look like a free meal. Check the raw string first, then convert. People must be an integer of at least 1 or you would divide by zero.
Build in slices
Three labeled fields and a button. Use type="number" so the keypad on a phone is numeric. The results sit in two paragraphs you can fill later.
Example
<label for="bill">Bill amount</label>
<input id="bill" type="number" min="0" step="0.01" value="48.50">
<label for="percent">Tip percent</label>
<input id="percent" type="number" min="0" value="18">
<label for="people">People</label>
<input id="people" type="number" min="1" step="1" value="2">
<button id="split" type="button">Split the bill</button>
<p id="tip">Tip: —</p>
<p id="each">Each person: —</p>Convert, validate, then compute. Tip is bill times percent over 100. Grand total is bill plus tip. Each person pays the grand total divided by people.
Example
function money(n) {
return "$" + n.toFixed(2);
}
const bill = Number(document.getElementById("bill").value);
const percent = Number(document.getElementById("percent").value);
const people = Number(document.getElementById("people").value);
if (!isFinite(bill) || bill < 0 || !isFinite(percent) || percent < 0) {
throw new Error("Bill and percent must be valid numbers.");
}
if (!Number.isInteger(people) || people < 1) {
throw new Error("People must be a whole number of at least 1.");
}
const tip = bill * (percent / 100);
const each = (bill + tip) / people;
document.getElementById("tip").textContent = "Tip: " + money(tip);
document.getElementById("each").textContent = "Each person: " + money(each);Wrap the math in a click handler and write errors onto the page instead of throwing. A calculator should not go silent when a field is blank.
Example
<label for="bill">Bill amount</label>
<input id="bill" type="number" min="0" step="0.01" value="48.50">
<label for="percent">Tip percent</label>
<input id="percent" type="number" min="0" value="18">
<label for="people">People</label>
<input id="people" type="number" min="1" step="1" value="2">
<button id="split" type="button">Split the bill</button>
<p id="tip">Tip: —</p>
<p id="each">Each person: —</p>
<script>
function money(n) {
return "$" + n.toFixed(2);
}
document.getElementById("split").addEventListener("click", function () {
const bill = Number(document.getElementById("bill").value);
const percent = Number(document.getElementById("percent").value);
const people = Number(document.getElementById("people").value);
const tipLine = document.getElementById("tip");
const eachLine = document.getElementById("each");
if (!isFinite(bill) || bill < 0 || !isFinite(percent) || percent < 0) {
tipLine.textContent = "Enter a valid bill and tip percent.";
eachLine.textContent = "Each person: —";
return;
}
if (!Number.isInteger(people) || people < 1) {
tipLine.textContent = "People must be a whole number of at least 1.";
eachLine.textContent = "Each person: —";
return;
}
const tip = bill * (percent / 100);
const each = (bill + tip) / people;
tipLine.textContent = "Tip: " + money(tip);
eachLine.textContent = "Each person: " + money(each);
});
</script>Try 48.50, 18 percent, and 2 people at /javascript/try. The tip should be $8.73 and each person $28.62. Use Try it in JavaScript.
Complete document
This file is the splitter you would keep. Starting values match a restaurant bill so the first click has something to compute. The card stays in the yellow studio palette.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tip Calculator</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #fefce8;
font-family: "Segoe UI", system-ui, sans-serif;
color: #1c1917;
}
.card {
width: min(22rem, 92vw);
background: #fffbeb;
border: 1px solid #fde68a;
padding: 1.4rem 1.5rem 1.5rem;
}
h1 { margin: 0 0 0.9rem; font-size: 1.4rem; }
label { display: block; font-weight: 700; margin: 0.55rem 0 0.3rem; }
input {
width: 100%;
box-sizing: border-box;
padding: 0.45rem 0.55rem;
border: 1px solid #d6d3d1;
font: inherit;
}
button {
margin-top: 0.9rem;
padding: 0.5rem 0.9rem;
border: 0;
background: #854d0e;
color: #fffbeb;
font: inherit;
font-weight: 700;
cursor: pointer;
}
.out {
margin: 1rem 0 0;
padding-top: 0.85rem;
border-top: 1px solid #fde68a;
}
.out p { margin: 0.25rem 0; }
</style>
</head>
<body>
<article class="card">
<h1>Tip Calculator</h1>
<label for="bill">Bill amount</label>
<input id="bill" type="number" min="0" step="0.01" value="48.50">
<label for="percent">Tip percent</label>
<input id="percent" type="number" min="0" value="18">
<label for="people">People</label>
<input id="people" type="number" min="1" step="1" value="2">
<button id="split" type="button">Split the bill</button>
<div class="out">
<p id="tip">Tip: —</p>
<p id="total">Grand total: —</p>
<p id="each">Each person: —</p>
</div>
</article>
<script>
function money(n) {
return "$" + n.toFixed(2);
}
function showError(text) {
document.getElementById("tip").textContent = text;
document.getElementById("total").textContent = "Grand total: —";
document.getElementById("each").textContent = "Each person: —";
}
document.getElementById("split").addEventListener("click", function () {
const bill = Number(document.getElementById("bill").value);
const percent = Number(document.getElementById("percent").value);
const people = Number(document.getElementById("people").value);
if (!isFinite(bill) || bill < 0 || !isFinite(percent) || percent < 0) {
showError("Enter a valid bill and tip percent.");
return;
}
if (!Number.isInteger(people) || people < 1) {
showError("People must be a whole number of at least 1.");
return;
}
const tip = bill * (percent / 100);
const grand = bill + tip;
const each = grand / people;
document.getElementById("tip").textContent = "Tip: " + money(tip);
document.getElementById("total").textContent = "Grand total: " + money(grand);
document.getElementById("each").textContent = "Each person: " + money(each);
});
</script>
</body>
</html>Rounding and split bills
toFixed(2) returns a string, which is what you want for display. Do the division on the raw numbers first. If you format the tip, then parse it back, you can lose a cent on some totals.
Real restaurants sometimes round the last person up. This page splits evenly and lets the last cent sit in the formatted string. That is enough for the project.
Common mistakes
- Using
parseInton the bill. $48.50 becomes 48 and the tip is short. - Dividing by
peoplewithout checking it is at least 1. Zero people is not a table. - Writing
percent / 100aspercent / 10. Eighteen percent would become 1.8 times the bill. - Showing the tip as a raw float like
8.7300000001. Always format money. - Listening to
submiton a form with nopreventDefault. The page reloads and the result vanishes.
Practice tasks
- Recalculate on every
inputevent so the visitor does not need the button. - Add a 15 / 18 / 20 percent row of buttons that fill the percent field and split immediately.
- Show the bill per person without tip as a fourth line. Preview at /javascript/try.