1

I have a simple table and I want to sum two comlumns. On top of than only when a relevant checkbox is checked.

The table is like that

<table id="Zinzino" style="border-collapse: collapse; width: 100%;" border="1">
<tbody>
<tr>
<th><strong>Název</strong></th>
<th class="sum"><strong>První balíček</strong></th>
<th class="sum"><strong>Měsíčně</strong></th>
<th> </th>
</tr>
<tr>
<td>BalanceOil <input type="checkbox" id="BalanceOil" name="BalanceOil" class="beru"></td>
<td>149 EUR</td>
<td>30 EUR</td>
<td> </td>
</tr>
<tr>
<td>Extend (2x)<input type="checkbox" </td>
<td>44 EUR</td>
<td>22 EUR</td>
<td> </td>
</tr>
<tr>
<td>Zinobiotic (3x)<input type="checkbox" </td>
<td>64 EUR</td>
<td>23 EUR</td>
<td> </td>
</tr>
<tr>
<td><strong>Celkem</strong></td>
<td class="celkem"> </td>
<td class="celkem"> </td>
<td> </td>
</tr>
</tbody>
</table>

I can modify the hmtl if needed. I have a working fiddle solution Because I have not found anything working out of the box I coded this one. I am sure that someone could provide us with something more elegant. Or even correct my code.

2 Answers 2

2

This should do it. Feel free to ask if some detail is not understood.

$("input[type='checkbox']").on('change', function() {
  updateTotals();
});

function updateTotals() {
  
  // loop cells with class 'celkem'
  $('.total').each(function(){
    
    // for each total, get the column
    const column = $(this).index();
    let total = 0;
    
    // loop trough all table rows, except the header and the row of totals
    $(this).closest('table').find('tr:not(:first, :last)').each(function(){
      if($(this).find('input').is(':checked')) {
      
        // if the input is checked, add the numeric part to the total
        const str = $(this).find(`td:eq(${column})`).text().replace(/\D/g, "");
        if(str) {
          total += Number(str);
        }
      }
    });
    
    if(!total) {
      // if the total is zero, clear the cell
      $(this).text("");
    } else {
      // otherwise, print the total for this column in the cell
      $(this).text(total + " EUR");
    }
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1" width="100%">
  <tr>
    <th>Col 1</th>
    <th><strong>Col 2</strong></th>
    <th><strong>Col 3</strong></th>
  </tr>
  <tr>
    <td><input type="checkbox"></td>
    <td>149 EUR</td>
    <td>30 EUR</td>
  </tr>
  <tr>
    <td><input type="checkbox"></td>
    <td>44 EUR</td>
    <td>22 EUR</td>
  </tr>
  <tr>
    <td><input type="checkbox"></td>
    <td>64 EUR</td>
    <td>23 EUR</td>
  </tr>
  <tr>
    <td><strong>Totals</strong></td>
    <td class="total"> </td>
    <td class="total"> </td>
  </tr>
</table>

Sign up to request clarification or add additional context in comments.

4 Comments

thank you again. The more I read you answer the more I like it. I wonder if you could help me with css. The check box is hidden on my website. From the answers I got to my question stackoverflow.com/questions/63310473/… I can get it visible but I want to have it centered verticaly. Any idea?
I took the liberty to make this code more generic, in case someone else finds it useful. To center vertically inside a td you can just add td { vertical-align: middle; }
yep, I noticed. You even used only English ;-) vertical-align doesnt work. I already tried that on the checkbox itself. But it wont help on TD either. zz.zustatzdravy.cz/kalkulator
alotropico, how would you update another row of totals? Lets say I want to insert new row as 2nd row with totals.
2

OK, I completed my solution which can be seen as an "almost One-Liner" in Vanilla JavaScript. Because of its brevity the readybility is slightly impaired.

In the second line the array trs is filled with all table rows (trs) with checked boxes. Only if something was checked (i.e. trs.length is "truthy") the calculation of sums is started in the following lines, otherwise sums is set to false. The "calculation" consists of a two-stage .map()-process (resulting in a 2D Array with all the individual prices) and a subsequent .reduce() call to do the summation for each column. A .forEach() is used internally here to do the summation for each of the relevant columns (not the first and the last ones).

In the last two lines the reults are written back to the table (last table record). Here the trenary operator ( ? : ) carefully checks whether sums is "truthy" before attemping the concatenation of sums[j]+" EUR".

document.getElementById("Zinzino").addEventListener('change',function(ev){
  const trs=[...document.querySelectorAll("tbody tr")].filter(tr=>tr.querySelector(':checked'));
  const sums=trs.length                        // calculate only when boxes were checked ...
   ? trs.map(tr=>[...tr.children].slice(1,-1)  // NOT first and last columns
         .map(td=>parseInt(td.textContent)))   // 2D array of prices
     .reduce((a,c)=>(a.forEach((dum,j)=>a[j]+=c[j]),a) ) // summation for each column
   : false;  // -> no calculation 
  // put results into last table row: 
  [...document.querySelectorAll("tbody tr:last-child td")].slice(1,-1)
     .forEach((td,j)=>td.textContent=sums ? sums[j]+" EUR" :'' );
})
<table id="Zinzino" style="border-collapse: collapse; width: 100%;" border="1">
<thead><tr>
<th><strong>Název</strong></th>
<th class="sum"><strong>První balíček</strong></th>
<th class="sum"><strong>Měsíčně</strong></th>
<th> </th>
</tr></thead>
<tbody>
<tr>
<td>BalanceOil <input type="checkbox" id="BalanceOil" name="BalanceOil" class="beru"></td>
<td>149 EUR</td>
<td>30 EUR</td>
<td> </td>
</tr>
<tr>
<td>Extend (2x)<input type="checkbox"></td>
<td>44 EUR</td>
<td>22 EUR</td>
<td> </td>
</tr>
<tr>
<td>Zinobiotic (3x)<input type="checkbox"></td>
<td>64 EUR</td>
<td>23 EUR</td>
<td> </td>
</tr>
<tr>
<td><strong>Celkem</strong></td>
<td class="celkem"> </td>
<td class="celkem"> </td>
<td> </td>
</tr>
</tbody>
</table>

5 Comments

Wow. that is nice. Too ... lets say complex for me. I like it but I don't fully understand the code. You dont have to explail, it would take a looooooooooooong time. Thank you for your time.
Thanks for giving me the feedback.I really like the accepted answer too, as it is easy to understand! And, as you are already using jQuery,.it makes sense to use it! My.solution was merely an exercise in doing it in Vanilla JS.
I might change the accepted solution if I like something better. I am pleased that probably 100% understand @alotropico solution. Thank you for yours. It almost looks like oneliner in ruby....
:D Yes, it is not far from a one-liner. But sometimes it is more important to code in a "readable" way. So, stick with your choice of accepted answer!
I like mine :-) in fiddle but I am going to use yours, guys... when I used to work in IT I showed my boss SO. We got something we could not solve... Posted on SO and got working solution in 5 minutes

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.