Matthew Sweet
Matthew Sweet

Reputation: 123

Javascript: Count number of table rows for each table and return a value to corresponding elements

I have three tables on a web page. Each table has a corresponding element nested in <thead> tags that I need to reflect the number of rows in its respective table, minus the head row and bottom row (-2). When I'm working with a single table, this code works just fine:

HTML Table Snippet:

<table class="table" id="category">
      <thead>
          <tr>
              <th><i class="fa fa-hashtag"></i> headache - <label class="label label-primary" id="tableBadge">0</span></th>
              <th><i class="fa fa-calendar"></i> Date Added</th>
              <th><i class="fa fa-cog"></i> Options</th>
          </tr>
      </thead>
      <tbody>
          <tr>
              <td>Test entry</td>
              <td>1/19/2016</td>
              <td>
              <a href="#" class="btn btn-success btn-xs"><i class="fa fa-pencil"></i></a>
              <a href="#" class="btn btn-primary btn-xs"><i class="fa fa-calendar"></i></a>
              <a href="#" class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
              </td>
          </tr>
          <tr>
              <td><a href="#" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> add entry</a></td>
              </td>
          </tr>
      </tbody>
  </table>

Javascript:

function catCount() {
    var rows = document.getElementById("category").rows.length;
    document.getElementById("tableBadge").innerHTML = rows - 2 + " entries";
}

However, respecting the laws of HTML in that ID's are unique to an element, I'm left in a bind. Using getElementsByClassName() or getElementsByTagName() the values return 0, telling me it's not working. At least using identical syntax.

I've searched Google et al. for a solution, but they seem to be tailored towards the total amount of rows, rather individual counts respective of their tables.

Any help would be appreciated. Thank you in advance.

Upvotes: 1

Views: 5053

Answers (4)

David Thomas
David Thomas

Reputation: 253506

While you've already accepted an answer, I'd suggest that the following might be slightly more useful, and far less-reliant on hard-coding exemptions (and therefore less of a maintenance nightmare for the future).

That said, my solution involves placing the 'footer' inside of a <tfoot> element, in order that the relevant <tr> elements are all contained within the <tbody> element(s) of the <table>.

The JavaScript function I'd recommend:

// wrapping the function in an Immediately-Invoked Function Expression
// ("IIFE") in order that it runs immediately and does not require
// calling later:
(function() {

  // using 'let' (rather than var) to declare local variables, all
  // of which are available only within the block in which they're
  // declared; here we convert the NodeList returned by
  // document.querySelectorAll('span.label') into an Array, using
  // Array.from():
  let outputs = Array.from(document.querySelectorAll('span.label')),

  // declaring another variable for later use:
    tbodies;

  // iterating over each of the found 'span.label' elements
  // in the Array, using Array.prototype.forEach():
  outputs.forEach(function(span) {
    // the first argument (here 'span') is the current
    // array-element of the array over which we're iterating.

    // finding the closest ancestor <table> element from the
    // current span node, and then finding all the <tbody>
    // elements contained within that <table>, and converting
    // that NodeList to an Array, again using Array.from() to
    // do so:
    tbodies = Array.from(span.closest('table').querySelectorAll('tbody'));

    // updating the text-content of the span to:
    // the sum of the child <tr> elements found in each of
    // the <tbody> elements found within the <table>, using
    // Array.prototype.reduce() to reduce the Array to a single
    // (here numeric) value; here we use an Arrow Function
    // to add the number of children of the <tbody> element
    // to the initialValue of the reduce method (0, the
    // final argument following the comma):
    span.textContent = tbodies.reduce((initialValue, tbody) => a + tbody.children.length, 0);
  });

// here the function is invoked:
})();

(function() {
  let outputs = Array.from(document.querySelectorAll('span.label')),
    tbodies;

  outputs.forEach(function(span) {
    tbodies = Array.from(span.closest('table').querySelectorAll('tbody'));

    span.textContent = tbodies.reduce((initialValue, tbody) => initialValue + tbody.children.length, 0);
  });

})();
<table class="table category">
  <thead>
    <tr>
      <th><i class="fa fa-hashtag"></i> headache - <span class="label label-primary"></span>
      </th>
      <th><i class="fa fa-calendar"></i> Date Added</th>
      <th><i class="fa fa-cog"></i> Options</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <td colspan="2"><a href="#" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> add entry</a>
      </td>
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <td>Test entry</td>
      <td>1/19/2016</td>
      <td>
        <a href="#" class="btn btn-success btn-xs"><i class="fa fa-pencil"></i></a>
        <a href="#" class="btn btn-primary btn-xs"><i class="fa fa-calendar"></i></a>
        <a href="#" class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
      </td>
    </tr>
  </tbody>
</table>

JS Fiddle demo.

It's worth noting that the above approach will handle multiple <table> elements each with, potentially, multiple <tbody> elements; and requires no hard-coding of values to discount from the final count, since it selects only those elements that should be counted.

References:

Upvotes: 0

Ron Sims II
Ron Sims II

Reputation: 696

getElementsByTagName and getElementsByClassNAme return node list, you need to iterate over them.

window.addEventListener('load', function(e) {
    //This is a node list you must iterate
    var tables = document.getElementsByTagName('table');

    for (var i = 0; i < tables.length; i++) {
        //This loop will handle each tble selected
        var table = tables[i];
        var totalRows = table.rows.length;
        console.log(totalRows);
        //Add your code here
    }
}, false);

Upvotes: 0

Maxi Schvindt
Maxi Schvindt

Reputation: 1462

change id tableBadge to tableBadge_category and similar to othter tables.

'table_id' is a id of table and you span is tableBadge_table_id

function catCount(table_id) {
    var rows = document.getElementById(table_id).rows.length;
    document.getElementById("tableBadge_"+table_id).innerHTML = rows - 2 + " entries";
}


catCount('category');
catCount('other_table');

Upvotes: 2

Dalorzo
Dalorzo

Reputation: 20024

Try instead using the querySelectorAll like:

document.querySelectorAll('.table>thead>tr')

The length of the array may be the answer you are looking for

Upvotes: 0

Related Questions