Home > Back-end >  Dynamically calculating total for each column based on selected rows for HTML table using Javascript
Dynamically calculating total for each column based on selected rows for HTML table using Javascript

Time:04-24

html table

From the picture, I am trying to calculate the total of each column for the selected rows a and c. The code works if I unwrap it from the function calculateCols. Is it possible to make it work in the wrapped function. I want to be able to choose a variation of rows to sum for each column without copy and pasting the code and editing it multiple times for each one.

function calculateCols(ID, calculate) {
  var final = 0
  var tbody = document.querySelector('tbody');
  var howManyCols = tbody.rows[0].cells.length;
  var totalRow = document.getElementById(ID);

  for (var j = 1; j < howManyCols; j  ) {
    final = calculate;
    const check = document.createElement('td');
    check.innerText = final;
    totalRow.appendChild(check);
  }

  function getRow(rowID) {

    var result = 0;

    try {
      var check = document.getElementById(rowID)
      var thisNumber = parseInt(check.cells[j].childNodes.item(0).data);

      if (!isNaN(thisNumber))
        result  = thisNumber;

    } finally {
      return result;
    }
  }

}

calculateCols('total', getRow('a')   getRow('c'));
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <table>
    <tbody>
      <tr>
        <td></td>
        <th scope="col">2013</th>
        <th scope="col">2014</th>
        <th scope="col">2015</th>
        <th scope="col">2016</th>
      </tr>
      <tr id="a">
        <th scope="row">a</th>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
      </tr>
      <tr id="b">
        <th scope="row">b</th>
        <td>5</td>
        <td>6</td>
        <td>4</td>
        <td>5</td>
      </tr>
      <tr id="c">
        <th scope="row">c</th>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr id="d">
        <th scope="row">d</th>
        <td>5</td>
        <td>6</td>
        <td>8</td>
        <td>5</td>
      </tr>

      <tr id="total">
        <th scope="row" id="Total">Total a   c</th>

      </tr>
    </tbody>
  </table>
  <script src="checkit.js"></script>

</body>

</html>

CodePudding user response:

This should do it:

function sumUp(...args){
 const [res,...cols]=args.map(n=>[...document.getElementById(n).children].slice(1));
 res.forEach((r,i)=>r.textContent= cols.reduce((a,c)=> c[i].textContent a, 0));
}
sumUp("total","a","b","c");
<table id="tbl">
    <tbody>
      <tr>
        <td></td>
        <th scope="col">2013</th>
        <th scope="col">2014</th>
        <th scope="col">2015</th>
        <th scope="col">2016</th>
      </tr>
      <tr id="a">
        <th scope="row">a</th>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
      </tr>
      <tr id="b">
        <th scope="row">b</th>
        <td>5</td>
        <td>6</td>
        <td>4</td>
        <td>5</td>
      </tr>
      <tr id="c">
        <th scope="row">c</th>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr id="d">
        <th scope="row">d</th>
        <td>5</td>
        <td>6</td>
        <td>8</td>
        <td>5</td>
      </tr>
      <tr id="total">
        <th scope="row" id="Total">Total a   c</th>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
    </tbody>
  </table>

I changed your table in so far that I added some <td>s for the results in the tr#total row. The rest is fairly easy: the rows you are interested in all have unique ids, so they can be addressed directly and individually. I created constants with the same name as the ids and looped over their children.

The first columns of the chosen rows were exempt, as I used .slice(1) on each trs children.

As long as you keep the ids unique you can have as many columns or rows as you like.

CodePudding user response:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <table>
    <tbody>
      <tr>
        <td></td>
        <th scope="col">2013</th>
        <th scope="col">2014</th>
        <th scope="col">2015</th>
        <th scope="col">2016</th>
      </tr>
      <tr id="a">
        <th scope="row">a</th>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
      </tr>
      <tr id="b">
        <th scope="row">b</th>
        <td>5</td>
        <td>6</td>
        <td>4</td>
        <td>5</td>
      </tr>
      <tr id="c">
        <th scope="row">c</th>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr id="d">
        <th scope="row">d</th>
        <td>5</td>
        <td>6</td>
        <td>8</td>
        <td>5</td>
      </tr>

      <tr id="total">
        <th scope="row" id="Total"></th>
      </tr>
    </tbody>
  </table>
  <script>
    function toIntOrZero(value) {
      let toInt = parseInt(value);
      return !isNaN(toInt) ? toInt : 0;
    }

    function getRowValues(rowID) {
      let children = document.getElementById(rowID).children;
      let values = [];
      for (let i = 1; i < children.length; i  ) {
        values.push(toIntOrZero(children[i].innerText));
      }
      return values;
    }

    function getRows(rowIds) {
      return rowIds.map(id => getRowValues(id));
    }

    function calculateColSums(array) {
      return array.reduce(function (r, a) {
        a.forEach(function (b, i) {
          r[i] = (r[i] || 0)   b;
        });
        return r;
      }, []);
    }

    function appendResults(results, rowIds) {
      document.getElementById('Total').innerHTML = 'Total of '   rowIds.join();
      var tableTotalRow = document.querySelector('tbody > #total');
      var totalColumns = document.querySelector('tbody').rows[0].cells.length;
      for (var j = 1; j < totalColumns; j  ) {
        const colSum = document.createElement('td');
        colSum.innerText = results[j - 1];
        tableTotalRow.appendChild(colSum);
      }
    }

    function calculateSumOfSelectedRows(rowIds) {
      appendResults(calculateColSums(getRows(rowIds)), rowIds);
    }

    calculateSumOfSelectedRows(['b', 'd']);
  </script>
</body>
</html>

CodePudding user response:

You shuffled the scopes (help: https://davidwalsh.name/for-and-against-let).
E.g.check.cells[j] always return howManyCols-1.
The first mistake (the debugger also warns), the getRow function is within the calculateCols function, but the call (calculateCols('total',getRow('a'),getRow('c'))) is not.

I have written a simpler code.

let results = {a: 0, b: 0, c: 0, d: 0};
rows = ['a', 'b', 'c', 'd'];
for (let i = 0; i < rows.length; i  ) {
  document.querySelectorAll('#' rows[i] ' td').forEach(
    function(elem) {
      results[rows[i]]  = Number(elem.textContent)
    }
  )
}
for (let i=0;i<rows.length;i  ) {
  let result = document.createElement('td');
  result.textContent = results[rows[i]];
  document.querySelector('#total').appendChild(result);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <table>
    <tbody>
      <tr>
        <td></td>
        <th scope="col">2013</th>
        <th scope="col">2014</th>
        <th scope="col">2015</th>
        <th scope="col">2016</th>
      </tr>
      <tr id="a">
        <th scope="row">a</th>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
      </tr>
      <tr id="b">
        <th scope="row">b</th>
        <td>5</td>
        <td>6</td>
        <td>4</td>
        <td>5</td>
      </tr>
      <tr id="c">
        <th scope="row">c</th>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr id="d">
        <th scope="row">d</th>
        <td>5</td>
        <td>6</td>
        <td>8</td>
        <td>5</td>
      </tr>

      <tr id="total">
        <th scope="row" id="Total">Total</th>

      </tr>
    </tbody>
  </table>
  <script src="checkit.js"></script>

</body>

</html>

If you want to only get the a and c rows' amount, set the list rows ['a','c']

  • Related