Alex Knopp
Alex Knopp

Reputation: 935

How do I show only elements that contain an array of classes with jQuery

I have a few elements with various options that the user can select to hide and show elements in a table.

This select' box is what I refer to as the Selector. There are a number of these in the document.

<div class="elegant_filters">
  <select multiple class="location">
   <option value="red">Red</option>
   <option value="green">Green</option>
   <option value="blue">Blue</option>
   <option value="orange">Orange</option>
   <option value="black">Black</option>
  </select>
</div>

Here is the table that contains the data.

<table>
  <tr class="red blue"><td>Some Data</td></tr>
  <tr class="red green"><td>Some Data</td></tr>
  <tr class="green blue"><td>Some Data</td></tr>
  <tr class="black blue"><td>Some Data</td></tr>
  <tr class="red orange"><td>Some Data</td></tr>
</table>

I am using this JS to create an array of class names on click in order to hide and show the rows with those classes.

//For the primary filters
jQuery('.elegant_filters option').on('click', function(e){

    jQuery(this).toggleClass('selected');

    var filters = [];
    jQuery('.elegant_filters option.selected').each(function(){
        var val = jQuery(this).val();
        filters.push('.'+val);          
    });

    if (jQuery(filters).length < 1) {
        jQuery('.elegant_list_properties tr.propertyrow').show();
    } else {
        jQuery('.elegant_list_properties tr.propertyrow').hide();
        jQuery(filters.join(', ')).show();
    }       

})

The resulting array when clicking through the options is [.red , .green]

THE CURRENT RESULT:

If i click on 'red' and 'green' options, all rows that contain these classes either together or on their own will show and those rows that don't will be removed.

THE REQUIRED RESULT:

If i click on 'red' and 'green' options, then only rows with BOTH classes will show and those rows that don't will be removed.

Upvotes: 0

Views: 429

Answers (2)

Steve0
Steve0

Reputation: 2253

I think the only substantive change I made to your code is to join the filters with no separator, this ensures that the row has all classes to be shown.

I did re-organize the code a little when I made a snippet.

jQuery('.elegant_filters option').on('click', function(e) {
  
  jQuery(this).toggleClass('selected');

  var filters = [];
  jQuery('.elegant_filters option.selected').each(function() {
    var val = jQuery(this).val();
    filters.push('.' + val);
  });
  //- not all but this -//
  //  if (jQuery(filters).length < 1) {
  //    jQuery('.elegant_list_properties tr.propertyrow').show();
  //  } else {
  //    jQuery('.elegant_list_properties tr.propertyrow').hide();
  //    jQuery(filters.join(', ')).show();
  //  }
  hideAllBut(filters);
});

function hideAllBut(selectors) {
  $("table tr").hide(); //hide all
  $("table tr" + selectors.join('')).show(); //show the matching ones
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="elegant_filters">
  <select multiple class="location">
   <option value="red">Red</option>
   <option value="green">Green</option>
   <option value="blue">Blue</option>
   <option value="orange">Orange</option>
   <option value="black">Black</option>
  </select> Here is the table that contains the data.
</div>
<table>
  <tr class="red blue">
    <td>red blue</td>
  </tr>
  <tr class="red green">
    <td>red green</td>
  </tr>
  <tr class="green blue">
    <td>green blue</td>
  </tr>
  <tr class="black blue">
    <td>black blue</td>
  </tr>
  <tr class="red orange">
    <td>red orange</td>
  </tr>
</table>

Consideration:

You may want to think about handling the change function of the select instead of the click of an option and then iterating over .elegant_filters option:selected this would remove your need for the selected class and allow for that portion to be handled by the select element instead.

jQuery('.elegant_filters select').on('change', function() {
  hideAllBut($.map($(this).find("option:selected"), function(option) {
    return "." + $(option).val(); //build the array of selected options as classes
  }));
});

function hideAllBut(selectors) {
  $("table tr").hide(); //hide all
  $("table tr" + selectors.join('')).show(); //show the matching ones
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="elegant_filters">
  <select multiple class="location">
   <option value="red">Red</option>
   <option value="green">Green</option>
   <option value="blue">Blue</option>
   <option value="orange">Orange</option>
   <option value="black">Black</option>
  </select> Here is the table that contains the data.
</div>
<table>
  <tr class="red blue">
    <td>red blue</td>
  </tr>
  <tr class="red green">
    <td>red green</td>
  </tr>
  <tr class="green blue">
    <td>green blue</td>
  </tr>
  <tr class="black blue">
    <td>black blue</td>
  </tr>
  <tr class="red orange">
    <td>red orange</td>
  </tr>
</table>

Upvotes: 1

chiliNUT
chiliNUT

Reputation: 19573

replace

jQuery(filters.join(', ')).show();

with

jQuery(filters.join('')).show();

If the class names are together without commas, it makes sure the items that match the selector match all of the classes in the selector, not just one of them

Upvotes: 0

Related Questions