dh762
dh762

Reputation: 2429

jQuery: hide table rows via search input, addressing td rowspan and colspan

I have a table with multiple rowspans and colspans and a search input. The input should only display those rows that match to the first column and hide all "child" rows.

Currently it hides only the first row but not all related sub rows. How can I hide all rows (with variable number of sub rows)? For example if I type "foo" there should only be the row with "Foo" appearing.

JSFiddle: https://jsfiddle.net/qsbw6Le4/

HTML:

<table id='myTable'>
<thead>
    <tr>
        <td colspan="1" rowspan="1">
            <input type="text" placeholder="searchme" class='table-search' data-table-search="myTable"></input>
        </td>
    </tr>
    <tr>
        <td colspan="2" rowspan="2">tablename</td>
        <td colspan="1" rowspan="1">header1</td>
        <td colspan="1" rowspan="1">header2</td>
    </tr>
</thead>
<tbody>
    <tr>
        <td colspan="1" rowspan="2">Foo </td>
        <td>disagg1</td>
        <td>value</td>
        <td>value</td>
    </tr>
    <tr>
        <td>disagg2</td>
        <td>value</td>
        <td>value</td>
    </tr>
    <tr>
        <td colspan="1" rowspan="2">Bar</td>
        <td>disagg1</td>
        <td>value</td>
        <td>value</td>      
    </tr>
    <tr>
        <td>disagg2</td>
        <td>value</td>
        <td>value</td>      
    </tr>
</tbody>
</table>

Javascript:

$(".table-search").on("keyup", function() {
    var value = $(this).val().toLowerCase(),
        tableattr = $(this).attr("data-table-search"),
        tablesearch = $('#'+tableattr).find('tbody tr')

    tablesearch.each(function() {
        $row = $(this);
        var id = $row.find("td:first").text().toLowerCase();
        if (id.indexOf(value) === -1) {
            $row.hide();
        }
        else {
            $row.show();
        }     
    });
});

I tried various methods described e.g. here to no avail: how to hide row with rowspan

Upvotes: 1

Views: 1188

Answers (1)

Tyler Roper
Tyler Roper

Reputation: 21672

I've included a solution below, and I'll do my best to explain each and every facet of it.

  1. Modify your keyup event to hide every row. We can reduce the logic in the loop to simply be "Should I show this row?".

  2. Most of this logic will revolve around the rowspan attribute, so we'll fetch that by doing $firstCell.attr('rowspan');.

  3. The tricky bit. If a row has a rowspan of 2, that means that we should completely ignore the next row. If it has a rowspan of 3, that means ignore the next 2 rows. If it has a rowspan of n, ignore the next n-1 rows.

    If we come across a rowspan, we can store that number outside of our loop. Doing this allows us to determine whether or not sequential iterations are the "children" of a rowspan or not.

    If is is the child of a rowspan, we'll ignore that row entirely by doing return true; in our loop.

    If is is not the child of a rowspan, we'll check that row for the searched text.

  4. Using your logic to check whether the text is present or not, we can show that row if it contains the text. However, taking it one step further, we'll also need to show any of its "children" rows. Remember, a row's children are the next rowspan-1 rows.

    jQuery has a handy selector for cases like this (the :lt(n) selector), which selects "the next n elements".

    If our row has a rowspan, we'll want to select the next rowspan-1 elements, which can be done using :lt(rowspan-1), add them to our current row, and show them.

// search in table
$(".table-search").on("keyup", function() {
  var value = $(this).val().toLowerCase(),
    tableattr = $(this).attr("data-table-search"),
    tablesearch = $('#' + tableattr).find('tbody tr');

  tablesearch.hide();                           //start with all rows hidden
  
  var previousRowspan = 1;                      //initiate our stored rowspan with the default of 1

  tablesearch.each(function() {
    var $row = $(this);
    var $firstCell = $row.find("td:first");
    var id = $firstCell.text().toLowerCase();
    var rowspan = $firstCell.attr('rowspan');   //get the row's rowspan

    
    if (previousRowspan > 1) {                  //if this row is a child
        previousRowspan--;                      //decrease the rowspan
        return true;                            //don't check this row
    }

    previousRowspan = +rowspan;                 //this row is not a child. update our rowspan.

    if (id.indexOf(value) > -1) {               //if the text is found
      var additionalRows = previousRowspan-1;   //use our n-1 formula
      var $additionalRows = $row.nextAll(':lt(' + additionalRows + ')');  //select the next (n-1) rows
      $row.add($additionalRows).show();         //show this row, and the next (n-1) rows as well
    }

  });

});
td {
  border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id='myTable'>
  <thead>
    <tr>
      <td colspan="1" rowspan="1">
        <input type="text" placeholder="searchme" class='table-search' data-table-search="myTable"></input>
      </td>
    </tr>
    <tr>
      <td colspan="2" rowspan="2">
        tablename
      </td>
      <td colspan="1" rowspan="1">
        header1
      </td>
      <td colspan="1" rowspan="1">
        header2
      </td>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td colspan="1" rowspan="2">Foo </td>
      <td>disagg1</td>
      <td>value</td>
      <td>value</td>
    </tr>
    <tr>
      <td>disagg2</td>
      <td>value</td>
      <td>value</td>
    </tr>
    <tr>
      <td colspan="1" rowspan="2">Bar</td>
      <td>disagg1</td>
      <td>value</td>
      <td>value</td>
    </tr>
    <tr>
      <td>disagg2</td>
      <td>value</td>
      <td>value</td>
    </tr>
  </tbody>
</table>

Upvotes: 3

Related Questions