You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
  });
}

关键修改说明

  1. 事件绑定优化:移除HTML内联事件,改为JavaScript动态绑定,新增表单行时自动为输入框和下拉框绑定事件,确保所有行都能响应操作
  2. 单行计算逻辑:通过closest('.row.clone')定位当前操作的表单行,仅计算该行的金额差值,避免干扰其他行数据
  3. 累加逻辑实现:遍历所有表单行,根据支付类型筛选对应charge值,累加后更新总金额输入框,处理了千分位格式的数值转换
  4. 实时更新触发:在输入值变化、支付类型切换、表单删除时,自动触发总金额重新计算,保证数据实时同步
  5. 数值格式化:使用toLocaleString保留两位小数,统一金额显示格式

内容的提问来源于stack exchange,提问作者Tom-Cat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 11:05:23