I want to add grand Total to table

Viewed 67

I am facing an issue with appending a row into a table's body. I wrote the whole logic part, but I cannot get the desired solution to work. I have a tablewith some rows and columns; I also have one button for calculating the total price of the items.

On button click I want to create a row showing the total price of the items.

Here is my code:

function calcTotal() {
  let sum = 0;
  let price = document.querySelectorAll(".price");
  for (let i = 0; i < price.length; i++) {
    sum = sum + parseInt(price[i].innerHTML);
  }
  console.log(sum);
  const tbody = document.getElementsByName("tbody");
  const row = document.createElement("tr");
  row.innerHTML = '<td>' + Grand - Total + '</td><td data-ns-test="grandTotal">' + sum + '</td>';
  tbody[0].appendChild(row);

}
<!DOCTYPE html>
<html>

<head>

</head>

<body>
  <h1>Grocery List</h1>
  <table>
    <thead>
      <tr>
        <th>Sr. No.</th>
        <th>Title</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>Item-1</td>
        <td data-ns-test="price" class="price">100</td>
      </tr>
      <tr>
        <td>2</td>
        <td>Item-2</td>
        <td data-ns-test="price" class="price">200</td>
      </tr>
      <tr>
        <td>3</td>
        <td>Item-3</td>
        <td data-ns-test="price" class="price">2</td>
      </tr>
      <tr>
        <td>4</td>
        <td>Item-4</td>
        <td data-ns-test="price" class="price">1</td>
      </tr>
    </tbody>
  </table>
  <button onClick='calcTotal()'>Total</button>
  <script src="app.js"></script>
</body>

</html>

4 Answers

There are bunch of mistakes in the code:

  • You are adding "Grand Total" to the string without quotes so it is taking these as a variable
  • you need to use document.getElementsByTagName('tbody')

working solution:

function calcTotal() {
    let sum = 0;
    let price = document.querySelectorAll(".price");
    for(let i=0; i<price.length; i++){
        sum = sum + parseInt(price[i].innerHTML);
    }

    const tbody = document.getElementsByTagName("tbody");
    const row = document.createElement("tr");
    row.innerHTML = '<td>Grand-Total </td><td colspan="2" data-ns-test="grandTotal">'+ sum +'</td>';
    tbody[0].appendChild(row);
}
<!DOCTYPE html>
<html>

<head>

</head>

<body>
    <h1>Grocery List</h1>
    <table>
        <thead>
            <tr>
                <th>Sr. No.</th>
                <th>Title</th>
                <th>Price</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>Item-1</td>
                <td data-ns-test="price" class="price">100</td>
            </tr>
            <tr>
                <td>2</td>
                <td>Item-2</td>
                <td data-ns-test="price" class="price">200</td>
            </tr>
            <tr>
                <td>3</td>
                <td>Item-3</td>
                <td data-ns-test="price" class="price">2</td>
            </tr>
            <tr>
                <td>4</td>
                <td>Item-4</td>
                <td data-ns-test="price" class="price">1</td>
            </tr>
        </tbody>
    </table>
    <button onClick='calcTotal()'>Total</button>
    <script src="app.js"></script>
</body>

</html>

I have added an Id to TBody and adjusted the javascript for this and i placed th Grand - added between the brackets.

HTML:

<!DOCTYPE html>
<html>

<head>

</head>

<body>
    <h1>Grocery List</h1>
    <table>
        <thead>
            <tr>
                <th>Sr. No.</th>
                <th>Title</th>
                <th>Price</th>
            </tr>
        </thead>
        <tbody id="body">
            <tr>
                <td>1</td>
                <td>Item-1</td>
                <td data-ns-test="price" class="price">100</td>
            </tr>
            <tr>
                <td>2</td>
                <td>Item-2</td>
                <td data-ns-test="price" class="price">200</td>
            </tr>
            <tr>
                <td>3</td>
                <td>Item-3</td>
                <td data-ns-test="price" class="price">2</td>
            </tr>
            <tr>
                <td>4</td>
                <td>Item-4</td>
                <td data-ns-test="price" class="price">1</td>
            </tr>
        </tbody>
    </table>
    <button onClick='calcTotal()'>Total</button>
</body>

</html>

javascript

function calcTotal() {
  let sum = 0;
  let price = document.querySelectorAll(".price");
  for (let i = 0; i < price.length; i++) {
    sum = sum + parseInt(price[i].innerHTML);
  }
  console.log(sum);
  const tbody = document.getElementById("body");
  console.log(tbody)
  const row = document.createElement("tr");
  row.innerHTML = '<td> Grand - Total </td><td data-ns-test="grandTotal">' + sum + '</td>';
  tbody.appendChild(row);

}

There's two problems:

  1. tbody is null, the following is wrong:
    document.getElementsByName("tbody");
    
    The above would work for:
    <tbody name="tbody">...</tbody>
    

    document.getElementsByTagName("tbody"); is the proper syntax.

  2. The new <tr> added only has 2 <td> but the rest of the table has 3 columns.

In the example below, a <tfoot>, a <tr>, a <td colspan='2'>, and another <td> are added. Also, clicks beyond the first click only updates the total and no duplicate <tr>.

Details are commented in example

// Bind click event to <button>
document.querySelector('.total').onclick = calcTotal;

function calcTotal(e) {
  /*
  Collect all .price into a NodeList then convert it into an array
  Iterate through array with .map() and on each iteration
  return current .price text coerced to a number
  */
  const prices = [...document.querySelectorAll('.price')]
    .map(p => +p.textContent);
  // Get the sum of the array with .reduce()
  const totalSum = prices.reduce((sum, add) => sum + add);
  // Create and insert a <tfoot> into <table>
  const tFoot = document.querySelector('table').createTFoot();
  // If there was a <tfoot> there previously, empty it ✥
  tFoot.replaceChildren();
  // Create and insert a <tr> into <tfoot>
  const row = tFoot.insertRow();
  // Create and insert 2 <td> into the <tr> of <tfoot>
  const colA = row.insertCell(0);
  const colB = row.insertCell(1);
  // Assign colspan and text to the first <td>
  colA.setAttribute('colspan', '2');
  colA.textContent = 'Grand Total';
  // Add the sum previously calculated to last <td> of <tfoot>
  colB.textContent = totalSum;
}
// ✥ Necessary to prevent any duplication of <tr>
td:last-of-type {
  text-align: right
}
<!DOCTYPE html>
<html>
<head></head>
<body>
  <table>
    <caption>Grocery List</caption>
    <thead>
      <tr>
        <th>No.</th>
        <th>Title</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>Item-1</td>
        <td class="price">100</td>
      </tr>
      <tr>
        <td>2</td>
        <td>Item-2</td>
        <td class="price">200</td>
      </tr>
      <tr>
        <td>3</td>
        <td>Item-3</td>
        <td class="price">2</td>
      </tr>
      <tr>
        <td>4</td>
        <td>Item-4</td>
        <td class="price">1</td>
      </tr>
    </tbody>
  </table>
  <button class='total'>Total</button>
  <script src="app.js"></script>
</body>

</html>

This worked for me. I used querySelector instead of getElementsByName so I had no array to deal with. If you have more than one tbody on the page you might consider giving them a unique id, or class that refers to what they are doing.

   function calcTotal() {
    let sum = 0;
    let price = document.querySelectorAll(".price");
    for (let i = 0; i < price.length; i++) {
      sum = sum + parseInt(price[i].innerHTML);
    }
    // const tbody = document.getElementsByName("tbody");
    const tbody = document.querySelector("tbody");
    const row = document.createElement("tr");
    window.r = row
    window.tbody = tbody
    row.innerHTML =
      "<td>" +
      "Grand-Total" +
      '</td><td data-ns-test="grandTotal">' +
      sum +
      "</td>";
    // tbody[0].appendChild(row);
    tbody.appendChild(row);
  }

I left the original commented out so you can see what I changed

Related