Home > Back-end >  Update total based on checkbox selection
Update total based on checkbox selection

Time:12-31

I have a simple finance tracker app that I am working on. Within the app, there is a table of all outgoing bills and then a total figure that sits outside of the table.

I'm trying to find a way to use JS or AJAX so that when I select a checkbox within 1 of the table rows I can include/exclude the bill from the total and have the total automatically update to reflect the exclusion/inclusion.

The table rows are dynamically created so each time a new bill is added the rows increase which means the checkbox ID would need to increase.

<div >
  <table  style="width:100%" id="example">
    <thead >
      <tr >
        <th scope="col">#</th>
        <th scope="col">CREDITOR</th>
        <th scope="col">DESCRIPTION</th>
        <th scope="col">CATEGORY</th>
        <th scope="col">AMOUNT</th>
        <th scope="col" style="text-align: center">INCLUDE / EXCLUDE</th>
      </tr>
    </thead>
    <tbody>
      {% for item in debts %}
      <tr >
        <td>{{ forloop.counter }}</td>
        <td>{{ item.creditor }}</td>
        <td>{{ item.description }}</td>
        <td>{{ item.category }}</td>
        <td>£{{ item.amount }}</td>
        <td><input type="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"  id="myCheckbox" /></td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
</div>

I've got this bit of JS

const checkbox = document.getElementById('myCheckbox')

checkbox.addEventListener('change', (event) => {
  if (event.currentTarget.checked) {
    alert('checked');
  } else {
    alert('not checked');
  }
})

That does fire the alert when the checkbox is checked, but this only fires on the first checkbox as this is the checkbox with the same ID.

So this is probably a tricky task to solve, as I would need the checkbox JS to know what the amount is for the row the checkbox is checked for and then subtract that figure from the total or vice/versa. Maybe this is the wrong approach?

The total is displayed as:

<h3 ><i ></i><span >£{{total_monthly_outgoing|floatformat:2}}<span > per month</span></span></h3>

Thanks

CodePudding user response:

You just need to delegate

I had to unwrap your span in the h3 to not make very messy JavaScript

Give the total an ID to make it easier than document.querySelector("h3.text-danger span").

You do not need IDs for this script

INCLUDE when checked

const totalContainer = document.querySelector("h3.text-danger span");

document.querySelector(".card-body").addEventListener("click", (e) => {
  if (!e.target.matches(".table-checkbox")) return; // not a checkbox
  const total = [...document.querySelectorAll(".table-checkbox:checked")]
    .map(chk =>  chk.closest("td").previousElementSibling.textContent.slice(1)) // remove the pound
    .reduce((a,b)=> a b); // sum
  totalContainer.textContent = `£${total.toFixed(2)}`;
})
.table-checkbox {
  border: 2px dotted #00f;
  display: block;
  background: #ff0000;
}
<div >
  <table  style="width:100%" id="example">
    <thead >
      <tr >
        <th scope="col">#</th>
        <th scope="col">CREDITOR</th>
        <th scope="col">DESCRIPTION</th>
        <th scope="col">CATEGORY</th>
        <th scope="col">AMOUNT</th>
        <th scope="col" style="text-align: center">INCLUDE / EXCLUDE</th>
      </tr>
    </thead>
    <tbody>
      <tr >
        <td>1</td>
        <td>creditor 1</td>
        <td>description 1</td>
        <td>category 1</td>
        <td>£123</td>
        <td><input type="checkbox"  /></td>
      </tr>
      <tr >
        <td>2</td>
        <td>creditor 2</td>
        <td>description 2</td>
        <td>category 2</td>
        <td>£223</td>
        <td><input type="checkbox"  /></td>
      </tr>
      <tr >
        <td>3</td>
        <td>creditor 3</td>
        <td>description 3</td>
        <td>category 3</td>
        <td>£323</td>
        <td><input type="checkbox"  /></td>
      </tr>

    </tbody>
  </table>
  <h3 ><i ></i><span >£0</span><span > per month</span>
  </h3>

</div>

EXCLUDE when checked

const totalContainer = document.querySelector("h3.text-danger span");
let total = [...document.querySelectorAll(".table-checkbox")]    
  .map(chk =>  chk.closest("td").previousElementSibling.textContent.slice(1)) // remove the pound
  .reduce((a,b)=> a b); // sum

document.querySelector(".card-body").addEventListener("click", (e) => {
  const tgt = e.target;
  if (!tgt.matches(".table-checkbox")) return; // not a checkbox
  const val =  tgt.closest("td").previousElementSibling.textContent.slice(1);
  total  =  val*(tgt.checked ? -1 :  1)
  totalContainer.textContent = `£${total.toFixed(2)}`;
})
.table-checkbox {
  border: 2px dotted #00f;
  display: block;
  background: #ff0000;
}
<div >
  <table  style="width:100%" id="example">
    <thead >
      <tr >
        <th scope="col">#</th>
        <th scope="col">CREDITOR</th>
        <th scope="col">DESCRIPTION</th>
        <th scope="col">CATEGORY</th>
        <th scope="col">AMOUNT</th>
        <th scope="col" style="text-align: center">INCLUDE / EXCLUDE</th>
      </tr>
    </thead>
    <tbody>
      <tr >
        <td>1</td>
        <td>creditor 1</td>
        <td>description 1</td>
        <td>category 1</td>
        <td>£123</td>
        <td><input type="checkbox"  /></td>
      </tr>
      <tr >
        <td>2</td>
        <td>creditor 2</td>
        <td>description 2</td>
        <td>category 2</td>
        <td>£223</td>
        <td><input type="checkbox"  /></td>
      </tr>
      <tr >
        <td>3</td>
        <td>creditor 3</td>
        <td>description 3</td>
        <td>category 3</td>
        <td>£323</td>
        <td><input type="checkbox"  /></td>
      </tr>

    </tbody>
  </table>
  <h3 ><i ></i><span >£669.00</span><span > per month</span>
  </h3>

</div>

  • Related