For a current project I am creating a simple product catalogue which should be able to be filtered by Jquery with several sets of checkboxes.
In one single set when two or more checkboxes are checked the logic should be OR while when using two or more sets of checkboxes it should be AND.
A good (and working) example I am currently using can be found here:
https://stackoverflow.com/a/22941156/5567735
JSFIDDLE
if (cselector === '' && nselector === '') {
$('.flowers > div').filter(selector).show();
} else if (cselector === '') {
$('.flowers > div').filter(selector).filter(nselector).show();
} else if (nselector === '') {
$('.flowers > div').filter(selector).filter(cselector).show();
} else {
$('.flowers > div').filter(selector).filter(cselector).filter(nselector).show();
}
My question regards this part of the code where the filters are being used in case they were selected. Now this works pretty good for the 3 filters that are being used in the example. What I am wondering is there some better way to be doing this? In my case I want to add some more filters and then this part of the code will get messy pretty soon when I have to add a lot more if statements.
So basically my question is: Using this setup, how would you add more filters so you have 6 or 7 in the end instead of the current 3?
Thanks a lot,
Peter
P.S. I also appreciate tips for completely different approaches but as I already put quite some time into this I would really like to get it work this way.
Your current approach isn't very dynamic, selectors and arrays are being hardcoded, so each time you add new filter options you'll have to add code to handle it.
Instead, just bind a change handler to all filter checkboxes, you can collect up their values, and group them by their respective names, eg:
var $filterCheckboxes = $( '.filter-checkboxes' );
$filterCheckboxes.on( 'change', function() {
var selectedFilters = {};
$filterCheckboxes.filter( ':checked' ).each( function() {
if ( ! selectedFilters.hasOwnProperty( this.name ) ) {
selectedFilters[ this.name ] = [];
}
selectedFilters[ this.name ].push( this.value );
} );
} );
This will create an object containing input-name -> value array
pairs, eg:
selectedFilters = {
'fl-colour': [ 'red', 'green' ],
'fl-size': [ 'tiny' ]
};
You can then loop over each selectedFilters
, and filter your .flower
elements. If a .flower
element matches a value in each named set, we return true so that the element is included in the $filteredResults
collection:
// create a collection containing all of the filterable elements
var $filteredResults = $( '.flower' );
// loop over the selected filter name -> (array) values pairs
$.each( selectedFilters, function( name, filterValues ) {
// filter each .flower element
$filteredResults = $filteredResults.filter( function() {
var matched = false,
currentFilterValues = $( this ).data( 'category' ).split( ' ' );
// loop over each category value in the current .flower's data-category
$.each( currentFilterValues, function( _, currentFilterValue ) {
// if the current category exists in the selected filters array
// set matched to true, and stop looping. as we're ORing in each
// set of filters, we only need to match once
if ( $.inArray( currentFilterValue, filterValues) != -1 ) {
matched = true;
return false;
}
} );
// if matched is true the current .flower element is returned
return matched;
} );
} );
Then simply hide all the .flower
elements, and show the $filteredResults
, eg:
$( '.flower' ).hide().filter( $filteredResults ).show();
Here's an example fiddle
Here is a fork of the jsfiddle from billyonecan. Differences are...
. jsFiddle
var $filterCheckboxes = $('input[type="checkbox"]');
// Read All Available Filter Groups
var allFilters = [];
$filterCheckboxes.each(function() {
if ($.inArray(this.name,allFilters) == -1){
allFilters.push(this.name);
}
});
// console.log(allFilters);
$filterCheckboxes.on('change', function() {
// create a collection containing all of the filterable elements
var $filteredResults = $('.item');
var $filterCategoryApplied = 0;
$.each(allFilters, function(arIndex, filterName) {
// console.log(filterName);
var $filterCheckboxCategory = $('input[name='+filterName+']').filter(':checked');
console.log(filterName + ' length = ' + $filterCheckboxCategory.length);
if ( $filterCheckboxCategory.length === 0 ) {
// alert('none checked for ' + filterName);
$filteredResults = [];
}
});
console.log('start checking');
// Read Selectetd Filters
var selectedFilters = {};
$filterCheckboxes.filter(':checked').each(function() {
if (!selectedFilters.hasOwnProperty(this.name)) {
selectedFilters[this.name] = [];
}
selectedFilters[this.name].push(this.value);
});
// loop over the selected filter name -> (array) values pairs
$.each(selectedFilters, function(name, filterValues) {
//console.log(selectedFilters['fl-colour'].length);
//console.log(name);
//console.log(filterValues);
//console.log(filterValues.length);
// filter each .flower element
$filteredResults = $filteredResults.filter(function() {
var matched = false,
currentFilterValues = $(this).data('category').split(' ');
// loop over each category value in the current .flower's data-category
$.each(currentFilterValues, function(_, currentFilterValue) {
// if the current category exists in the selected filters array
// set matched to true, and stop looping. as we're ORing in each
// set of filters, we only need to match once
if ($.inArray(currentFilterValue, filterValues) != -1) {
matched = true;
return false;
}
});
// if matched is true the current .flower element is returned
return matched;
});
});
$('.item').hide().filter($filteredResults).show();
});
body {
font-family: 'Arial';
color: #646464;
}
.items-wrap {
float: left;
width: 20%;
margin: 0 3% 0 0;
padding: 0;
position: relative;
}
.items {
float: left;
width: 60%;
}
.items div.item {
float: left;
width: 45%;
height: 28px;
line-height: 38px;
padding: 0 1%;
background: #eee;
margin: 0 0 1px;
position: relative;
font-size: 0.7em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<pre id=result> </pre>
<div class="items-wrap">
<h3 style="font-size:13px; font-weight:normal;">Filter Items</h3>
<p style="font-size:12px;"><strong>By colour:</strong></p>
<form id='filters'>
<label style="font-size:12px;">
<input type="checkbox" name="fl-colour" value="red" id="red" checked /> Red</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-colour" value="yellow" id="yellow" checked /> Yellow</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-colour" value="green" id="green" checked /> Green</label>
<p style="font-size:12px;"><strong>By size:</strong></p>
<label style="font-size:12px;">
<input type="checkbox" name="fl-size" value="small" id="small" checked /> Small</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-size" value="medium" id="medium" checked /> Medium</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-size" value="large" id="large" checked /> Large</label>
<p style="font-size:12px;"><strong>By Shape:</strong></p>
<label style="font-size:12px;">
<input type="checkbox" name="fl-shape" value="square" id="square" checked /> Square</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-shape" value="circle" id="circle" checked /> Circle</label>
<br>
<label style="font-size:12px;">
<input type="checkbox" name="fl-shape" value="triangle" id="triangle" checked /> Triangle</label>
</form>
</div>
<div class="items">
<div class="item" data-id="rss" data-category="red small square">red small square</div>
<div class="item" data-id="yss" data-category="yellow small square">yellow small square</div>
<div class="item" data-id="gss" data-category="green small square">green small square</div>
<div class="item" data-id="rms" data-category="red medium square">red medium square</div>
<div class="item" data-id="yms" data-category="yellow medium square">yellow medium square</div>
<div class="item" data-id="gms" data-category="green medium square">green medium square</div>
<div class="item" data-id="rls" data-category="red large square">red large square</div>
<div class="item" data-id="yls" data-category="yellow large square">yellow large square</div>
<div class="item" data-id="gls" data-category="green large square">green large square</div>
<div class="item" data-id="rsc" data-category="red small circle">red small circle</div>
<div class="item" data-id="ysc" data-category="yellow small circle">yellow small circle</div>
<div class="item" data-id="gsc" data-category="green small circle">green small circle</div>
<div class="item" data-id="rmc" data-category="red medium circle">red medium circle</div>
<div class="item" data-id="ymc" data-category="yellow medium circle">yellow medium circle</div>
<div class="item" data-id="gmc" data-category="green medium circle">green medium circle</div>
<div class="item" data-id="rlc" data-category="red large circle">red large circle</div>
<div class="item" data-id="ylc" data-category="yellow large circle">yellow large circle</div>
<div class="item" data-id="glc" data-category="green large circle">green large circle</div>
<div class="item" data-id="rst" data-category="red small triangle">red small triangle</div>
<div class="item" data-id="yst" data-category="yellow small triangle">yellow small triangle</div>
<div class="item" data-id="gst" data-category="green small triangle">green small triangle</div>
<div class="item" data-id="rmt" data-category="red medium triangle">red medium triangle</div>
<div class="item" data-id="ymt" data-category="yellow medium triangle">yellow medium triangle</div>
<div class="item" data-id="gmt" data-category="green medium triangle">green medium triangle</div>
<div class="item" data-id="rlt" data-category="red large triangle">red large triangle</div>
<div class="item" data-id="ylt" data-category="yellow large triangle">yellow large triangle</div>
<div class="item" data-id="glt" data-category="green large triangle">green large triangle</div>
</div>