如何对具有相同 <select> 选项的 <input> 求和?

How to sum <input>s that have the same <select> options?

问题是我有这个用于多个捐赠入口的表格,我快完成了,我只需要计算我的捐赠小计。基本上每个条目都有一个输入和一个带有 5 个选项的 select - 每个选项都将作为我的捐赠输入的小计。我想将相同小计类别(相同 select 选项)中的捐款相加并列出。

我在想出解决方案时遇到了很多麻烦。现在我正在寻找 vanilla js 或 jquery 的解决方案。有人知道怎么做吗?

这里是 HTML 简化版:

 <label>donation value</label>
 <input type="number" class="donation">

 <select class="categories">
   <option id="cat1">category 1</option>
   <option id="cat2">category 2</option>
   <option id="cat3">category 3</option>
 </select>

编辑:如果它是用 onClick 事件制作的,我会更喜欢。提前致谢!

您可以将 onchange 侦听器添加到输入并更新数据结构以跟踪每个实例的值,然后根据该数据结构计算小计。这个演示需要一点调整——例如,它不处理清除金额字段——但它展示了基本思想:

// an array to keep track of each donation instance
const donations = [];

// change handler for both the number input and the category select
// The first argument is the change event, from which we
// extract the target's name and value, where 'name' will be
// either 'category' for the select or 'amount' for the number input
function onDonationChange({target: {name, value}}, index) {
  // update the entry at [index] to reflect the change
  donations[index] = {
    // keep the existing fields for this entry if it exists,
    // otherwise create it using default values
    ...(donations[index] ?? { category: 'category 1', amount: 0}),
    // update with the event target's field name and value
    [name]: value
  };
  
  // update the subtotal display to reflect the change
  updateSubtotal();
}

function updateSubtotal () {
  // find the subtotal dom element we want to update
  const elem = document.querySelector('.subtotal');
  
  // compute the subtotals for each category, e.g.
  // { 'category 1': 100, 'category 2': 200 } etc.
  const subtotals = donations.reduce((acc, {category, amount}) => ({ ...acc, [category]: ((acc[category] ?? 0) + parseFloat(amount)) }), {});
  
  // update the display.
  // for each entry in subtotals, create an html snippet,
  // then join them into a single string.
  elem.innerHTML = Object.entries(subtotals)
    .map(([category, subtotal]) => (
      `<div><span class="cat">${category}</span>: ${subtotal}</div>`
    )).join('');
}

// find the items so we can add the event listeners
const items = document.querySelectorAll('li');

// iterate over each item and...
items.forEach((item, index) => {
  // find the input and select for this instance
  const inputs = item.querySelectorAll('input, select');
  
  // add a change listener that passes both the change
  // event and the item's index
  inputs.forEach(i => i.addEventListener('change', e => onDonationChange(e, index)));
});
body {
  font-family: sans-serif;
}

ul {
  list-style: none;
  padding: 0;
}

.cat {
  opacity: 0.5;
}
<ul>
  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>
  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

</ul>
<hr />
<div class="subtotal"></div>


或者如果你真的需要事后立即触发它:

// Get a donation object for the given LI
// returns an object representing the LI's inputs, e.g.
// { category: 'category 1', amount: 122 }
function getDonation (listItem) {
  // find all the inputs and selects in this LI
  const inputs = listItem.querySelectorAll('input, select');
  
  // construct and return the object based on the inputs' names and values
  return [...inputs]
    .reduce((acc, {name, type, value}) => ({...acc, [name]: type === 'number' ? parseFloat(value) : value}), {})
}


function updateSubtotal() {
  // Get all of the LI's
  const listItems = document.querySelectorAll('li');
  
  // Get a donation object for each LI
  // Filter out items where amount is not a number
  const donations = [...listItems].map(getDonation).filter(({amount}) => !isNaN(amount));
  
  // Compute the subtotals for each category
  const subtotals = donations.reduce((acc, {category, amount}) => ({ ...acc, [category]: ((acc[category] ?? 0) + parseFloat(amount)) }), {});
  
  // Find the subtotal display element
  const displayElem = document.querySelector('.subtotal');
  
  // Generate and insert HTML from the computed subtotals
  displayElem.innerHTML = Object.entries(subtotals)
    .map(([category, amount]) => (
      `<div><span class="category">${category}</span>: <span class="amount">${amount}</span></div>`
    )).join('');
}
body {
  font-family: sans-serif;
}

ul {
  list-style: none;
  padding: 0;
}

.category {
  opacity: 0.5;
}

.amount {
  color: tomato;
}
<ul>
  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>
  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>

  <li>
    <label>donation value</label>
    <input name="amount" type="number" class="donation">

    <select name="category" class="categories">
      <option>category 1</option>
      <option>category 2</option>
      <option>category 3</option>
    </select>
  </li>
</ul>
<button onclick="updateSubtotal()">Update Subtotal</button>
<hr />
<div class="subtotal"></div>