小费计算器
简单约 20 分钟
你将构建什么
一个小费计算器:输入账单金额和小费百分比,实时显示小费金额和总金额。支持选择常见比例(15%、18%、20%)或自定义输入。
学习目标
- 从输入框实时读取用户输入
- 用函数封装小费计算逻辑
- 用 toFixed 格式化货币输出
- 用 input 事件实现实时计算
前置要求
- 了解 JavaScript 函数和数值运算
- 了解 DOM 操作和事件监听
- 已完成前三个 JS 项目或具备同等基础
步骤
- 1
搭建表单界面
HTML 中放账单金额输入框、小费比例选择按钮组(15%、18%、20%)、结果显示区。
代码<label for="bill">账单金额(元)</label> <input type="number" id="bill" min="0" value="100" /> <div> <button data-tip="15">15%</button> <button data-tip="18">18%</button> <button data-tip="20">20%</button> </div> <p>小费:<span id="tip-amount">0.00</span> 元</p> <p>总额:<span id="total-amount">0.00</span> 元</p>预期结果
页面显示账单输入框、三个比例按钮和结果区域。
提示
用 data-tip 属性存比例值,JS 用 dataset 读取,比 id 更灵活。
- 2
编写计算函数
编写 calculateTip(bill, tipPercent) 函数:接收账单金额和比例,返回小费金额和总额。注意处理除零和负数。
代码function calculateTip(bill, tipPercent) { if (bill < 0 || tipPercent < 0) { return { tip: 0, total: bill }; } const tip = bill * tipPercent / 100; return { tip, total: bill + tip }; }预期结果
调用 calculateTip(100, 15) 返回 { tip: 15, total: 115 }。
提示
返回对象包含两个值,调用方可以分别取用——比返回数组语义更清晰。
- 3
绑定事件实现实时计算
默认选中 15%,给比例按钮绑定 click 事件切换选中状态,给账单输入框绑定 input 事件。任意变化都触发重新计算并更新显示。
代码const billInput = document.querySelector('#bill'); const tipAmountEl = document.querySelector('#tip-amount'); const totalAmountEl = document.querySelector('#total-amount'); const tipButtons = document.querySelectorAll('button[data-tip]'); let currentTip = 15; function update() { const bill = parseFloat(billInput.value) || 0; const { tip, total } = calculateTip(bill, currentTip); tipAmountEl.textContent = tip.toFixed(2); totalAmountEl.textContent = total.toFixed(2); } billInput.addEventListener('input', update); tipButtons.forEach(btn => { btn.addEventListener('click', () => { currentTip = parseInt(btn.dataset.tip); update(); }); }); // 初始计算 update();预期结果
修改账单金额或点击比例按钮,结果实时更新。
提示
input 事件在输入框内容变化时触发,比 change 更实时(change 要失焦才触发)。
- 4
添加选中状态高亮
给当前选中的比例按钮加 active 类高亮显示,切换时移除其他按钮的 active。
代码tipButtons.forEach(btn => { btn.addEventListener('click', () => { tipButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentTip = parseInt(btn.dataset.tip); update(); }); });预期结果
选中的比例按钮有高亮样式,切换时高亮跟随移动。
提示
classList.toggle('active', condition) 可以根据条件添加或移除类。
自己动手试试
起始代码
<label for="bill">账单金额(元)</label>
<input type="number" id="bill" min="0" value="100" />
<div>
<button data-tip="15">15%</button>
<button data-tip="18">18%</button>
<button data-tip="20">20%</button>
</div>
<p>小费:<span id="tip-amount">0.00</span> 元</p>
<p>总额:<span id="total-amount">0.00</span> 元</p>
<script>
// TODO: calculateTip、update、绑定事件
</script>解决方案代码
<label for="bill">账单金额(元)</label>
<input type="number" id="bill" min="0" value="100" />
<div>
<button data-tip="15">15%</button>
<button data-tip="18">18%</button>
<button data-tip="20">20%</button>
</div>
<p>小费:<span id="tip-amount">0.00</span> 元</p>
<p>总额:<span id="total-amount">0.00</span> 元</p>
<script>
function calculateTip(bill, tipPercent) {
if (bill < 0 || tipPercent < 0) {
return { tip: 0, total: bill };
}
const tip = bill * tipPercent / 100;
return { tip, total: bill + tip };
}
const billInput = document.querySelector('#bill');
const tipAmountEl = document.querySelector('#tip-amount');
const totalAmountEl = document.querySelector('#total-amount');
const tipButtons = document.querySelectorAll('button[data-tip]');
let currentTip = 15;
function update() {
const bill = parseFloat(billInput.value) || 0;
const { tip, total } = calculateTip(bill, currentTip);
tipAmountEl.textContent = tip.toFixed(2);
totalAmountEl.textContent = total.toFixed(2);
}
billInput.addEventListener('input', update);
tipButtons.forEach(btn => {
btn.addEventListener('click', () => {
tipButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentTip = parseInt(btn.dataset.tip);
update();
});
});
update();
</script>你学到了什么
你学会了从表单读取输入、用函数封装计算逻辑、用 toFixed 格式化输出——这是工具类应用的基本模式。实时计算用 input 事件,比提交后计算体验更好。
相关资源
相关术语
新手友好的术语表用大白话解释编程术语——变量、函数、DOM、Promise 等等。
相关错误
用大白话解释常见错误——什么意思、为什么发生、如何修复。