filter table rows with multiple filters

19.4k views Asked by At

I have a table with several rows and columns and a set of search fields. I would like to be able to show / hide the rows that match / don't match the search fields. Each field is related to a column of the table. I have been partly successful in this task, because the filtering is done correctly (as you can see here). However, I would like to fix two things.

  • First is the fact that the jquery script also hides the table head.
  • Secondly, I would like to be able to filter the rows as I type. Ex. if I type only 'J' at name box, everything disappears, because there is no row with the name 'J'. However, we have got 'James' and 'Jamie', which are potential match. And I would like to keep them until the name is fully typed. I tried to do it with s1.localeCompare(s2) (link here), but it does not work.

By the way, no need to worry about uppercase / lowercase typing. I actually take care of it in the original code, but tried to keep it simple here.

The code here:

<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
        <script>
        $(document).ready(function(){
            table = $("#MI6"); //set table name
            search_field = new Object();
            ///we create it as an object, initially empty
            $('.search-key').on('change keyup paste', function () {
                search_field['name']      = $( "#name" ).val();
                search_field['lastname']  = $("#lastname").val();
                search_field['number']    = $("#number").val();

                table.find('tr').each(function () {
                    current_row = $(this); //keep track of the row being checked, iterate through it's cells
                    var display = 0;
                    current_row.show();
                    $(this).find('td').each(function() {
                    //when we stumble upon the data used as a search criteria
                        cell_value = $(this).html(); //gets the value of the cell being checked
                        if (cell_value == search_field[this.id] || search_field[this.id] == '') {
                            display++;    
                        }
                    });
                    if (display < 3) {
                        current_row.hide(); //if this cell is a match, or we no longer want to use it as a search criteria, the row is displayed
                    }
                });

            });   
        });
        </script>
    </head>
    <body>
        <input type="text" id="name" class="search-key" placeholder="name">
        <input type="text" id="lastname" class="search-key" placeholder="lastname">
        <input type="number" id="number" class="search-key" placeholder="number">
        <p></p>
        <table id="MI6">
            <tr>
                <th>Firstname</th>
                <th>Lastname</th> 
                <th>Number</th>
            </tr>
            <tr>
                <td id="name">James</td>
                <td id="lastname">Bond</td> 
                <td id="number">7</td>
            </tr>
            <tr>
                <td id="name">Vesper</td>
                <td id="lastname">Lynd</td> 
                <td id="number">6</td>
            </tr>
            <tr>
                <td id="name">Rene</td>
                <td id="lastname">Mathis</td> 
                <td id="number">5</td>
            </tr>
    </table>
    </body>
</html>
2

There are 2 answers

5
billyonecan On BEST ANSWER

To answer your first question, simply omit the first row of the table from the collection using .not(':first'):

table.find('tr').not(':first')

In order to do partial string matching, you can use indexOf().

The indexOf() method returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex. Returns -1 if the value is not found.

I noticed that you can duplicate ids in your markup, they must be unique.

Your script could be rewritten to be more dynamic with a couple of small changes to the markup:

<td data-input="name">Rene</td>
<td data-input="lastname">Mathis</td> 
<td data-input="number">5</td>

You can then use the data-input to target the corresponding input. You can combine this with jQuery's filter() method to return matching rows:

/* $rows = table.find('tr').not(':first') */
$rows.hide().filter(function() {

  return $(this).find('td').filter(function() {

    var tdText = $(this).text().toLowerCase(),
        inputValue = $('#' + $(this).data('input')).val().toLowerCase();

    return tdText.indexOf(inputValue) != -1;

  }).length == $(this).find('td').length;

}).show();

The above first hides each row, and then filters. Inside of there, each contained td is filtered, comparing its text against the value of the corresponding input. If a match is found, the td is returned. It then checks the number of matching td elements against the number of td elements in that row, if they are the same, all fields contain a partial match, and the entire row is returned. Finally, any matching rows are then shown.

This way will allow you to add more inputs, and tds without having to modify the code. You'd just have to set the id on the input, and add the corresponding data-input to the td elements.

Here's a complete example

1
sumitmangela On

This might help

$('.filter').change(function(){

  filter_function();
  
  //calling filter function each select box value change
  
});

$('table tbody tr').show(); //intially all rows will be shown

function filter_function(){
  $('table tbody tr').hide(); //hide all rows
  
  var companyFlag = 0;
  var companyValue = $('#filter-company').val();
  var contactFlag = 0;
  var contactValue = $('#filter-contact').val();
   var rangeFlag = 0;
  var rangeValue = $('#filter-range').val();
   var rangeminValue = $('#filter-range').find(':selected').attr('data-min');
   var rangemaxValue = $('#filter-range').find(':selected').attr('data-max');
  
  //setting intial values and flags needed
  
 //traversing each row one by one
  $('table tr').each(function() {  
  
    if(companyValue == 0){   //if no value then display row
    companyFlag = 1;
    }
    else if(companyValue == $(this).find('td.company').data('company')){ 
      companyFlag = 1;       //if value is same display row
    }
    else{
      companyFlag = 0;
    }
    
    
     if(contactValue == 0){
    contactFlag = 1;
    }
    else if(contactValue == $(this).find('td.contact').data('contact')){
      contactFlag = 1;
    }
    else{
      contactFlag = 0;
    }
    
    
    
     if(rangeValue == 0){
    rangeFlag = 1;
    }
  //condition to display rows for a range
    else if((rangeminValue <= $(this).find('td.range').data('min') && rangemaxValue >  $(this).find('td.range').data('min')) ||  (
      rangeminValue < $(this).find('td.range').data('max') &&
      rangemaxValue >= $(this).find('td.range').data('max'))){
      rangeFlag = 1;
    }
    else{
      rangeFlag = 0;
    }
     
      console.log(rangeminValue +' '+rangemaxValue);
      console.log($(this).find('td.range').data('min') +' '+$(this).find('td.range').data('max'));
    
    
   if(companyFlag && contactFlag && rangeFlag){
     $(this).show();  //displaying row which satisfies all conditions
   }

});


  
  
}
<html>
<head>

</head>
<body>
  
  <select id="filter-company" class="filter">
     <option value="0">No value</option>
  <option value="Alfreds">Alfreds</option>
  <option value="Centro">Centro</option>
  <option value="Ernst">Ernst</option>
  <option value="Island">Island</option>
      <option value="Laughing">Laughing</option>
      <option value="Magazzini">Magazzini</option>
</select> 
  
    <select id="filter-contact" class="filter">
     <option value="0">No value</option>
  <option value="Maria Anders">Maria Anders</option>
  <option value="Francisco Chang">Francisco Chang</option>
  <option value="Roland Mendel">Roland Mendel</option>
</select>  
  
      <select id="filter-range" class="filter">
          <option value="0" data-min="1" data-max="1" >No value</option>
  <option value="£100,000 - £200,000" data-min="100000" data-max="200000" >£100,000 - £200,000</option>
<option value="£200,000 - £300,000" data-min="200000" data-max="300000" >£200,000 - £300,000</option>
<option value="£300,000 - £400,000" data-min="300000" data-max="400000" >£300,000 - £400,000</option>
<option value="£400,000 - £500,000" data-min="400000" data-max="500000" >£400,000 - £500,000</option>
</select>
  
<!-- ^^ range select box contains data-max and data-min which will be used to compare ranges -->
  
  

<h2>HTML Table</h2>

<table>
  <thead>
  <tr>
    <th>Company</th>
    <th>Contact</th>
    <th>Range</th>
  </tr>
  </thead>
  <tr>
    <tbody>
    <td class="company" data-company="Alfreds">Alfreds</td>
      
     <!-- Data attributes helps in fetching values but you can fetch value of text in td also --> 
      
    <td class="contact" data-contact="Maria Anders">Maria Anders</td>
     <td class="range" data-min="200000" data-max="300000">£200,000 - £300,000</td>
 
       <!-- here Data attributes are necessary as we need separate min max values --> 
 
  </tr>
  <tr>
    <td class="company"  data-company="Centro">Centro</td>
    <td class="contact" data-contact="Francisco Chang">Francisco Chang</td>
      <td class="range" data-min="100000" data-max="200000">£100,000 - £200,000</td>
  </tr>
  <tr>
    <td class="company" data-company="Alfreds" >Alfreds</td>
    <td class="contact" data-contact="Roland Mendel">Roland Mendel</td>
     <td class="range" data-min="200000" data-max="300000">£200,000 - £300,000</td>
  </tr>
  <tr>
    <td class="company"  data-company="Centro" >Centro</td>
    <td class="contact" data-contact="Helen Bennett">Helen Bennett</td>
      <td class="range" data-min="100000" data-max="200000">£100,000 - £200,000</td>
  </tr>
  <tr>
    <td class="company"  data-company="Laughing">Laughing</td>
    <td class="contact" data-contact="Yoshi Tannamuri">Yoshi Tannamuri</td>
   <td class="range" data-min="200000" data-max="300000">£200,000 - £300,000</td>
  </tr>
  <tr>
    <td class="company"  data-company="Laughing">Laughing</td>
    <td data-contact="Giovanni Rovelli">Giovanni Rovelli</td>
    <td class="range" data-min="150000" data-max="250000">£150,000 - £250,000</td>
  </tr>
  </tbody>
</table>

  

  
</body>
</html>

https://codepen.io/sumitmangela/pen/NzZpzQ