Как сложить <input>с одинаковыми параметрами <select>? - PullRequest
0 голосов
/ 25 марта 2020

Дело в том, что у меня есть эта форма для нескольких входов пожертвований, я почти закончил с ней, мне просто нужно вычислить промежуточные суммы моих пожертвований. В основном каждая запись имеет вход и выбор с 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. Заранее спасибо!

1 Ответ

0 голосов
/ 25 марта 2020

Вы можете добавить слушателей onchange к входам и обновить структуру данных, чтобы отслеживать значения каждого экземпляра, а затем вычислить промежуточные итоги из этой структуры данных. Эта демонстрация требует небольшой настройки - например, она не обрабатывает очистку поля количества - но она показывает основную идею c:

// 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>
...