ItsRobyn
ItsRobyn

Reputation: 25

Filter on multiple classes with jQuery

I have a page where I want the user to be able to filter on a number of different classes at the same time. Currently, I'm using the following to allow them to filter on just 1 class at a time:

$(document).ready(function(){
            $(".filter-button").click(function(){
                var value = $(this).attr('data-filter');

                if(value == "all")
                {
                    $('.filter').show('1000');
                }
                else
                {
                    $(".filter").not('.'+value).hide('3000');
                    $('.filter').filter('.'+value).show('3000');                    
                }
            });

        });

So let's say there is a list of animals and I want to find all orange cats. Currently I could only find either all cats or all orange animals, not combine the two to find just orange cats.

EDIT: Here is a Fiddle example of what I currently have. What I'm looking for is to be able to click Cats, then Orange, and have it only show "Orange Cat".

EDIT 2 (SOLVED): Me.Name's answer provided the solution I needed. I have refined it slightly further to allow the buttons to toggle on and off (supports more than 2 filters) - Fiddle.

Upvotes: 1

Views: 3882

Answers (3)

Me.Name
Me.Name

Reputation: 12544

edit Based on the updated question, I had the goal reversed in the original answer, (and some quick edits inbetween other chores didn't fare quite well :D), but the same principle can be used: create a selector based on the combined classes. Perhaps checkboxes would be better, but by using the buttons and a an extra 'checked' class:

$(document).ready(function(){
	var targets = $('.filter'), 
     buttons = $('.filter-button').click(function(){
    var value = $(this).data('filter');
    if(value == "all")
    	buttons.removeClass('checked'); 
    else
      $(this).toggleClass('checked'); 
    
    var checkedClasses = buttons.filter('.checked').toArray().map(function(btn){return $(btn).data('filter');}); //create array of filters
    
    if(checkedClasses.length === 0)
    	targets.show('1000');    
    else
    {   
      var selector = '.' + checkedClasses.join('.'), //create selector of the combined classes
    	  show = targets.filter(selector);      
      targets.not(show).hide('3000');
      show.show('3000');
    }
  });
});
.checked{
  font-style: italic;
  background-color:yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <button class="filter-button" type="button" data-filter="all">Show All</button>
  <button class="filter-button" type="button" data-filter="cat">Cats</button>
  <button class="filter-button" type="button" data-filter="orange">Orange</button>
</div>

<div>
  <p class="filter cat">Cat</p>
  <p class="filter orange">Orange</p>
  <p class="filter cat orange">Orange Cat</p>
  <p class="filter cat pink">Pink panther</p>
</div>


Orignal answer (for filtering on more properties):

You could place all values inside the 'data-filter' attributes and parse them to a selector, but to make life easier, you could also place the entire (jQuery) selector inside the attribute. So instead of data-filter = 'orange' , use data-filter = '.orange' and for 2: data-filter= '.orange,.animal' Of course, the extra dot should not be added inside the handler then, but used directly, e.g. $(".filter").not(value)

Example:

$(function(){
	var targets = $('.filter');  
	$('.filter-all').click(function(){ targets.show(1000); }); //put the 'all' in a separate class with seperate handler to prevent the if, but that's just a preference
  $('.filter-button').click(function(){  
  	var selector = $(this).data('filter');
    targets.not(selector).hide('3000');
    targets.filter(selector).show('3000');  
  });
})
.filter{
  border:1px solid blue;
  background-color:lightblue;
  height:50px;
  text-align:center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class='filter-all' data-filter= 'all'>All</button>
<button class='filter-button' data-filter= '.d1'>1</button>
<button class='filter-button' data-filter= '.d2'>2</button>
<button class='filter-button' data-filter= '.d3'>3</button>
<button class='filter-button' data-filter= '.d1,.d3'>1 + 3</button>

<div class='d1 filter'>1</div>
<div class='d2 filter'>2</div>
<div class='d3 filter'>3</div>

Upvotes: 1

user1641831
user1641831

Reputation:

Here we go!

Firstly we create an array of filters, so that - regardless of the number of items that are added to the list - we are always going to have an appropriate amount of buttons.

// Create an array of filters
var filtersArray = [];

// Retrieve all the possible filters from the DOM
$(".filter").each(function(){
    var classes = $(this).attr("class").split(" ");
    classes.forEach(function(e) {filtersArray.push(e.toString());});
});

// Select only unique values, remove empty values and take away the "filter" class, since it's not a real filter
var filtersUnique = filtersArray.filter(function(el,i) {return i == 
filtersArray.indexOf(el);});
var filtersEmpty = filtersUnique.filter(function(v){return v!==''});
var filters = filtersEmpty.filter(function(v){return v!=='filter'});

// Create a button for each filter
$.each(filters,function(k,v){
    $(".filters").append('<button class="filter-button" type="button" data-filter="'+v+'">'+v+'</button>');
});

The click function itself hides all elements that do not have the specific class.

// Click Function
$(".filter-button").click(function(){
  var dataFilter = $(this).attr("data-filter");
  var dataFilterClass = ("."+dataFilter);
  $(".filter").not(dataFilterClass).addClass("hidden");
});

Edit. Forgot Jsfiddle: https://jsfiddle.net/uaf5rLdt/

It would be nice to add some logic to hide the buttons that are not useful anymore (e.g. "Horse" and "Dog", whenever an user clicks on "Cat").

Upvotes: 0

Govind Samrow
Govind Samrow

Reputation: 10179

Here is the code that working for you:

Update you html, add data filter with "." too:

$(document).ready(function() {
  $(".filter-button").click(function() {
    $(this).toggleClass('active')
    var value = $(this).attr('data-filter');
    var active = $(this).hasClass('active');
    $('.filter').hide();
    if (value == "all" && active) {
      $('.filter').show('1000');
      return;
    }
    $(".filter-button.all").removeClass('active')
    var cFilter = "";
    $('.filter-button.active').each(function() {
      cFilter += "." + $(this).attr('data-filter');
    });
    $('p.filter').filter(cFilter).show('3000');

  });
});
.active {
  border-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<body>
  <div>
    <button class="filter-button all" type="button" data-filter="all">Show All</button>
    <button class="filter-button" type="button" data-filter="cat">Cats</button>
    <button class="filter-button" type="button" data-filter="orange">Orange</button>
  </div>

  <div>
    <p class="filter cat">Cat</p>
    <p class="filter orange">Orange</p>
    <p class="filter cat orange">Orange Cat</p>
  </div>

</body>

Upvotes: 0

Related Questions