I am facing a problem with the Grand Total calculation in a dynamic HTML table where rows can be added, removed, and quantities can be adjusted. The issue arises when a new row is added or a row is removed; the Grand Total does not update correctly unless the quantity is manually increased.
Here’s a brief overview of the functionality:
- Products can be added to the table from a datalist.
- Quantity input for each product allows adjustment.
- Rows can be removed with a "Remove" button.
The Grand Total is expected to update dynamically with each modification to the table, but there seems to be an issue in the JavaScript code that handles the calculation.
// Initial check when the page loads
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("productTable").style.display = "none";
});
function addProductRow() {
document.getElementById("productTable").style.display = "table";
var productInput = document.getElementById("product");
var productName = productInput.value;
if (productName.trim() !== "") {
var productDataList = document.getElementById("products");
var selectedOption = [...productDataList.options].find(option => option.value === productName);
updateGrandTotal();
if (selectedOption) {
var productPrice = selectedOption.getAttribute("data-price");
var tableBody = document.getElementById("productTable").getElementsByTagName('tbody')[0];
var row = tableBody.insertRow();
row.innerHTML = `
<td>${productName}</td>
<td>$${productPrice}</td>
<td>
<input type="number" name="quantity" min="1" value="1" oninput="updateSubtotal(this); updateGrandTotal();">
</td>
<td class="subtotal">$${productPrice}</td>
<td>
<button class="remove-button" onclick="removeProductRow(this)">Remove</button>
</td>
`;
productInput.value = ""; // Clear the product input after adding a row
updateGrandTotal();
}
}
}
function removeProductRow(button) {
var row = button.parentElement.parentElement;
row.parentElement.removeChild(row);
updateGrandTotal();
//Check if there are no products
var tableBody = document.getElementById("productTable").getElementsByTagName('tbody')[0];
if (tableBody.rows.length < 2) {
document.getElementById("productTable").style.display = "none";
}
updateGrandTotal();
}
function updateSubtotal(input) {
var quantity = parseInt(input.value);
var unitPriceCell = input.parentElement.previousElementSibling;
var unitPrice = parseFloat(unitPriceCell.textContent.replace('$', ''));
var row = input.parentElement.parentElement;
var subtotalCell = row.querySelector('.subtotal');
var subtotal = quantity * unitPrice;
subtotalCell.textContent = `$${subtotal}`;
updateGrandTotal();
}
function updateGrandTotal() {
var tableBody = document.getElementById("productTable").getElementsByTagName('tbody')[0];
var rows = tableBody.getElementsByTagName('tr');
var grandTotal = 0;
for (var i = 0; i < rows.length - 1; i++) {
//console.log("rows length :", rows.length)
//console.log("i value:", i); // Log the value
var subtotalCell = rows[i].querySelector('.subtotal');
//console.log("Sub Total Cell :", rows.length)
grandTotal += parseInt(subtotalCell.textContent.replace('$', '')) || 0;
//console.log("grand total :", grandTotal)
//console.log("")
}
var grandTotalRow = document.getElementById("grandTotalRow");
if (grandTotalRow) {
grandTotalRow.parentElement.removeChild(grandTotalRow);
}
grandTotalRow = tableBody.insertRow();
grandTotalRow.id = "grandTotalRow";
grandTotalRow.className = "grand-total-row";
grandTotalRow.innerHTML = `
<td colspan="2"></td>
<td align="right"><strong>Grand Total:</strong></td>
<td class="subtotal" id="grandTotalValue">$${grandTotal}</td>
<td></td>
`;
}
<form>
<table id="productTable">
<thead>
<tr>
<th>Product Name</th>
<th>Unit Price</th>
<th>Quantity</th>
<th>Sub-Total</th>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<label for="product">Product name:</label>
<input list="products" name="product" id="product" oninput="addProductRow()">
<datalist id="products">
<option value="Product 1" data-price="25">
<option value="Product 2" data-price="30">
<option value="Product 3" data-price="15">
<option value="Product 4" data-price="22">
</datalist>
<input type="submit">
</form>
3
Answers
The answer by @mplungjan worked. The Grand total was updated properly. https://stackoverflow.com/a/77598801/14238946
But it introduced a bug for sub-totals where it's not responsive to value increase or decrease. Modifying the
updateSubtotal
part solved it. Instead of getting the unit price from the.subtotal
cell, it should be obtained from the corresponding product row.-I strongly recommend delegation
Here is a rewrite. Note I moved the grandTotal to a static tfoot
The way you’re changing the total is the problem with your grand total computation. Every time the updateGrandTotal() function is run, a new row for the Grand Total is created. Updates should be made to the current Grand Total row instead.