JavaScript实现多输入框onchange事件取值及金额计算
问题解决:多表单支付类型金额自动计算与累加
需求回顾
- 当同一表单行的
price和pay输入框均有值时,自动计算pay - price并填充到该行的charge(即input-total)输入框 - 选择支付类型后,对应支付类型的总金额字段自动累加所有同类型表单行的
charge值 - 支持动态新增/删除表单行,操作后总金额实时更新
修改后的完整代码
HTML部分
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> <button id="add-button" class="btn btn-primary mb-3">Add Form</button> <div class="wrappers"> <div class="row clone mb-2"> <div class="col px-0"> <div class="input-group mb-2"> <input type="number" class="form-control rounded-0 input-price" placeholder="Price" /> </div> </div> <div class="col px-0"> <div class="input-group mb-2"> <input type="number" class="form-control rounded-0 input-pay" placeholder="Pay" /> </div> </div> <div class="col-auto px-0"> <div class="input-group mb-2"> <select class="form-control rounded-0 type-payment" role="button" > <option value="CASH">CASH</option> <option value="DEBIT">DEBIT</option> <option value="CASHLESS">CASHLESS</option> </select> </div> </div> <div class="col px-0"> <div class="input-group mb-2"> <input value="0.00" type="text" class="form-control rounded-0 input-total" disabled /> </div> </div> </div> </div> <div class="pt-4"> <div class="row"> <div class="col-md-8 pr-0"> <input class="form-control total rounded-0" disabled placeholder="TOTAL CASH" /> </div> <div class="col-md-4 pl-0"> <input id="CASH" class="form-control rounded-0 sum" disabled placeholder="0.00" /> </div> </div> <div class="row"> <div class="col-md-8 pr-0"> <input class="form-control total rounded-0" disabled placeholder="TOTAL DEBIT" /> </div> <div class="col-md-4 pl-0"> <input id="DEBIT" class="form-control rounded-0 sum" disabled placeholder="0.00" /> </div> </div> <div class="row"> <div class="col-md-8 pr-0"> <input class="form-control total rounded-0" disabled placeholder="TOTAL CASHLESS" /> </div> <div class="col-md-4 pl-0"> <input id="CASHLESS" class="form-control rounded-0 sum" disabled placeholder="0.00" /> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
JavaScript部分
// 动态新增/删除表单 const btn = document.getElementById("add-button"); const formClone = document.querySelector(".clone"); const parentForm = document.querySelector(".wrappers"); btn.addEventListener("click", (e) => { e.preventDefault(); const clone = formClone.cloneNode(true); clone.querySelectorAll("input").forEach((input) => { input.value = ""; // 给新增的价格/支付输入框绑定事件 if (input.classList.contains('input-price') || input.classList.contains('input-pay')) { input.addEventListener('input', handleInputChange); } }); // 给新增的支付类型下拉框绑定事件 clone.querySelector('.type-payment').addEventListener('change', handlePaymentChange); const deleteForm = document.createElement("button"); deleteForm.innerText = "X"; deleteForm.classList.add("btn", "btn-danger", "btn-sm"); clone.appendChild(deleteForm); deleteForm.addEventListener("click", (e) => { e.preventDefault(); e.target.parentElement.remove(); // 删除后重新计算所有总金额 updateAllTotals(); }); parentForm.appendChild(clone); }); // 初始化绑定所有表单行的事件 document.querySelectorAll('.input-price, .input-pay').forEach(input => { input.addEventListener('input', handleInputChange); }); document.querySelectorAll('.type-payment').forEach(select => { select.addEventListener('change', handlePaymentChange); }); // 计算单行的charge值 function handleInputChange(e) { const row = e.target.closest('.row.clone'); const priceInput = row.querySelector('.input-price'); const payInput = row.querySelector('.input-pay'); const totalInput = row.querySelector('.input-total'); const price = parseFloat(priceInput.value) || 0; const pay = parseFloat(payInput.value) || 0; if (price && pay) { const total = pay - price; totalInput.value = total.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); } else { totalInput.value = "0.00"; } // 计算完单行后更新对应支付类型的总金额 updateTotalByType(row.querySelector('.type-payment').value); } // 切换支付类型时更新总金额 function handlePaymentChange(e) { const row = e.target.closest('.row.clone'); // 先确保当前行的charge值已计算 const priceInput = row.querySelector('.input-price'); const payInput = row.querySelector('.input-pay'); if (priceInput.value && payInput.value) { handleInputChange({target: priceInput}); } // 更新所有支付类型的总金额 updateAllTotals(); } // 根据支付类型计算累加总金额 function updateTotalByType(paymentType) { const sumInput = document.getElementById(paymentType); let total = 0; document.querySelectorAll('.row.clone').forEach(row => { const select = row.querySelector('.type-payment'); const totalInput = row.querySelector('.input-total'); if (select.value === paymentType) { total += parseFloat(totalInput.value.replace(/,/g, '')) || 0; } }); sumInput.value = total.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); } // 更新所有支付类型的总金额 function updateAllTotals() { ['CASH', 'DEBIT', 'CASHLESS'].forEach(type => { updateTotalByType(type); }); }
关键修改说明
- 事件绑定优化:移除HTML内联事件,改为JavaScript动态绑定,新增表单行时自动为输入框和下拉框绑定事件,确保所有行都能响应操作
- 单行计算逻辑:通过
closest('.row.clone')定位当前操作的表单行,仅计算该行的金额差值,避免干扰其他行数据 - 累加逻辑实现:遍历所有表单行,根据支付类型筛选对应
charge值,累加后更新总金额输入框,处理了千分位格式的数值转换 - 实时更新触发:在输入值变化、支付类型切换、表单删除时,自动触发总金额重新计算,保证数据实时同步
- 数值格式化:使用
toLocaleString保留两位小数,统一金额显示格式
内容的提问来源于stack exchange,提问作者Tom-Cat
相关产品推荐
相关产品推荐

