如何对具有相同 <select> 选项的 <input> 求和?
Posted
技术标签:
【中文标题】如何对具有相同 <select> 选项的 <input> 求和?【英文标题】:How to sum <input>s that have the same <select> options? 【发布时间】:2020-03-25 03:55:13 【问题描述】:问题是我有这个用于多个捐赠入口的表格,我几乎完成了,我只需要计算我的捐赠的小计。基本上每个条目都有一个输入和一个带有 5 个选项的选择 - 每个选项都将作为我的捐赠输入的小计。我想将属于相同小计类别(相同选择选项)的捐款相加并列出。
我在想出解决方案时遇到了很多麻烦。现在我正在寻找香草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 事件制作的,我会更喜欢。提前致谢!
【问题讨论】:
为了确保我理解:上面的 HTML 结构重复了?每个实例都有一个类别和一个值?你想按类别小计? 不需要重复,因为我已经处理好了,但我不介意你的。几乎,只要我需要一个实例,我就会创建一个实例,每个实例都有一个类别和一个值的输入文本,是的。确切地说,按类别小计。我忘了包括:我希望它是通过 onClick 事件,鉴于我的经验有限,跟踪和管理事件会更容易。 【参考方案1】:您可以将 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>
【讨论】:
我非常喜欢你的代码,非常感谢。你能让它符合我最后的要求吗?我在这里尝试您的代码,但在 onChange 事件上遇到了一点问题。 你最后的要求是什么? 使用 onClick 事件代替 onChange 事件。我尝试过,但运气不佳。有可能吗? 你的代码非常好。我有问题。这可能是我的错,但是当我用我的代码尝试它时,我无法使小计工作,也许是因为我正在动态地创建每个实例。我在没有动态创建任何实例的情况下尝试了您的代码,并且效果很好。我绝对接受你的回答。你能帮我解决这个问题吗? My demo 演示中小计的问题是,当您创建新捐赠时,您会为输入的数字提供一个唯一的名称(例如amount_1
)。 subtotal 函数将'amount'
字段相加,因此它忽略了新的输入。如果去掉加名字后缀的那一行(第28行)it works.以上是关于如何对具有相同 <select> 选项的 <input> 求和?的主要内容,如果未能解决你的问题,请参考以下文章
更改 <select> 的选项并使用 JavaScript 触发事件
两个浏览器选项卡中的相同 Vue.js 组件使 <select> 镜像其选定值