
最近折腾了一个小费计算器,本来以为半小时能搞定,结果在除法取整和输入验证上踩了几个坑。这篇把实现过程和踩过的坑都说清楚,给想练手的同学当个参考。
小费计算器的核心逻辑很简单:输入账单金额和小费比例,算出小费金额和总额,再支持按人数分摊。拿餐厅场景举例,120元的账单,18%的小费比例,就是21.6元小费,总共141.6元。
用 JavaScript 做小费计算器,需要三个核心输入:账单金额、小费比例、分摊人数。前两个算出小费,第三个用来算每个人出多少钱。
核心公式就四条:
| 输入项 | 示例 | 用途 |
|---|---|---|
| 账单金额 | ¥120.00 | 小费前的消费金额 |
| 小费比例 | 18% | 用于计算小费的百分比 |
| 人数 | 3 | 分摊账单的人数 |
按照上面这个例子,小费是21.6元,总共141.6元。三个人平摊的话,每人47.2元。
界面上放三个输入框:账单金额、小费比例、人数。然后留几个位置显示计算结果。
先写一个简单的表单:
<label for="bill">Bill amount</label>
<input id="bill" type="number" min="0" step="0.01" placeholder="60.00"> <label for="tip">Tip percentage</label>
<input id="tip" type="number" min="0" step="0.1" placeholder="18"> <label for="people">Number of people</label>
<input id="people" type="number" min="1" step="1" value="1"> <button id="calculate">Calculate Tip</button> <p>Tip: <span id="tipAmount">$0.00</span></p>
<p>Total: <span id="total">$0.00</span></p>
<p>Per person: <span id="perPerson">$0.00</span></p> 用 number 类型的输入框,浏览器会自动调出数字键盘。配合 min 和 step 属性,能限制输入范围,防止用户瞎填。
结果显示的地方各自带上唯一 ID,这样 JS 才能精准找到并更新内容。计算逻辑和显示逻辑分开,后续改起来也方便。
JS 的活就是:读取三个输入框的值,转成数字,按公式算完,再把结果写回页面。
const billInput = document.getElementById("bill");
const tipInput = document.getElementById("tip");
const peopleInput = document.getElementById("people"); const tipAmount = document.getElementById("tipAmount");
const total = document.getElementById("total");
const perPerson = document.getElementById("perPerson"); document.getElementById("calculate").addEventListener("click", calculateTip); 输入框拿到的是文本,需要转成数字才能做运算。Number() 转换一目了然:
const bill = Number(billInput.value);
const tipPercent = Number(tipInput.value);
const people = Number(peopleInput.value); 小费比例换算成小数,再乘以账单金额。
const tip = bill * (tipPercent / 100); 120元的账单,18%小费,JS 算出来就是 120 × 0.18,等于 21.6。
const totalAmount = bill + tip; 总额就是原始账单加上小费。
用总金额除以人数,得到每个人要付的钱。
const amountPerPerson = totalAmount / people; 当然也可以单独算人均小费:
const tipPerPerson = tip / people; 用 textContent 把值塞进对应的元素:
tipAmount.textContent = `$${tip.toFixed(2)}`;
total.textContent = `$${totalAmount.toFixed(2)}`;
perPerson.textContent = `$${amountPerPerson.toFixed(2)}`; toFixed(2) 保留两位小数,符合大多数货币显示习惯。
一个能用的计算器,必须能处理空值、非法数字、人数为零这些情况,否则一除就崩。
function calculateTip() { const bill = Number(billInput.value); const tipPercent = Number(tipInput.value); const people = Number(peopleInput.value); if (!Number.isFinite(bill) || bill < 0) { return; } if (!Number.isFinite(tipPercent) || tipPercent < 0) { return; } if (!Number.isInteger(people) || people < 1) { return; } const tip = bill * (tipPercent / 100); const totalAmount = bill + tip; const amountPerPerson = totalAmount / people; tipAmount.textContent = `$${tip.toFixed(2)}`; total.textContent = `$${totalAmount.toFixed(2)}`; perPerson.textContent = `$${amountPerPerson.toFixed(2)}`;
} 这版代码会拒绝负数账单、负数比例、非数字、以及人数为零或小数的情况。实际项目中最好在对应输入框旁边显示错误提示,让用户知道哪里出了问题。
货币计算一般需要两位小数。JavaScript 的 Math.round() 可以四舍五入取整。
const roundedTotal = Math.round(totalAmount); 不过显示用 toFixed(2) 更合适:
const displayTotal = totalAmount.toFixed(2); 记住计算用的数值和显示用的格式化要分开存。内部保留精确结果,界面只做展示用的格式化。
分摊账单经常会出现分分钱除不尽的情况。比如100.01元除以3人,结果是个无限循环小数。三个人的显示金额四舍五入后加起来,可能跟总额差一分钱。
简单场景下,显示每人四舍五入后的金额就够了。但如果要做支付系统精确对账,就得定个明确的余数处理规则,比如把最后一分钱塞给某个人。
基础功能跑通了,可以再加几个体验优化。
常见的15%、18%、20%做成按钮,一键填入,不用每次都打字:
<button type="button" data-tip="10">10%</button>
<button type="button" data-tip="15">15%</button>
<button type="button" data-tip="18">18%</button>
<button type="button" data-tip="20">20%</button> JS 把选中的值塞进输入框:
document.querySelectorAll("[data-tip]").forEach(button => { button.addEventListener("click", () => { tipInput.value = button.dataset.tip; calculateTip(); });
}); 这样比例值写在 HTML 里,JS 只负责交互逻辑,职责分明。
改成用户边输入边出结果,不用每次都点按钮:
[billInput, tipInput, peopleInput].forEach(input => { input.addEventListener("input", calculateTip);
}); 改金额、调比例、加人减人,结果马上更新,体验顺滑很多。
发布前多测几个例子,不要只测一个正常的120元账单。边界情况最容易暴露问题:
好的测试用例应该有独立算出来的预期结果,这样对答案检查 JS 输出才对得上。
每个输入框都要有可见的标签,用 for 和 id 关联起来。结果标签也要写清楚,比如“小费金额”、“总额”、“人均”,用户扫一眼就知道每个数是什么意思。
JavaScript 怎么计算小费?
账单乘以小费比例再除以100。比如120元的20%小费,就是24元小费。
怎么算总金额?
小费加回原始账单。120元加24元小费,总共144元。
怎么分摊小费?
用小费金额或总金额除以人数。注意处理除不尽时的小数取整问题。
JavaScript 怎么取整?Math.round() 四舍五入到整数,toFixed(2) 格式化成两位小数用于显示。
小费计算器需要后端吗?不需要。所有计算都在浏览器里完成,页面参数输入完直接出结果。
怎么验证非法输入?
计算前先校验账单、比例、人数。拒绝负数,人数为零或非法时不做除法。
能支持不同货币吗?能。计算逻辑跟货币无关,换个符号或格式就行,百分比公式照旧。
小费计算器是个不错的 JavaScript 练手项目,核心算法就那么几行,但界面要考虑验证、格式化、分摊、用户体验这些实际问题。基础版跑通之后,可以加币种选择、税费计算、常用预设、更复杂的分账逻辑,扩展空间不小。