To all jQuery heros out there, I need help! (again)
I found similar questions to mine on the internet but there were no answers so far :-/
I want to filter a bunch of items with jquery isotope. I adapted the combination filter example from the plugin homepage, which works fine and looks like so:
$(function () {
var $container = $('#container'),
filters = {};
$container.isotope({
itemSelector: '.item',
filter: '',
});
// filter links
$('.filter a').click(function () {
var $this = $(this);
// don't proceed if already selected
if ($this.hasClass('selected')) {
return;
}
var $optionSet = $this.parents('.option-set');
// change selected class
$optionSet.find('.selected').removeClass('selected');
$this.addClass('selected');
// store filter value in object
// i.e. filters.color = 'red'
var group = $optionSet.attr('data-filter-group');
filters[group] = $this.attr('data-filter-value');
// convert object into array
var isoFilters = [];
for (var prop in filters) {
isoFilters.push(filters[prop])
}
var selector = isoFilters.join('');
$container.isotope({
filter: selector
});
return false;
}); });
and some html of the filter menu:
<div id="nav">
<ul class="filter option-set" data-filter-group="who">
<li><a href="#filter-who-any" data-filter-value="" class="selected">Any</a></li>
<li><a href="#filter-who-boys" data-filter-value=".boys">Boys</a></li>
<li><a href="#filter-who-girls" data-filter-value=".girls">Girls</a></li>
</ul>
</div>
I set up a fiddle demo with 3 filter categories which can be combined: jsFiddle
Now I'd like to make it possible to select multiple tags from each category:
Category 1: a OR b OR c
AND
Categoy 2: a OR b OR c
AND
Categoy 3: a OR b OR c
There's a check-box-example by desandro, but this is not quite the thing I'm searching for, because I want to keep the AND condition between the categories. Basically I want 3 filter categories with some check-boxes for each of them (or links but I guess check-boxes are more functional). I think the methods used in both examples are completely different and I don't know how to combine their functionality.
I hope you get my point and that anyone could help me find a solution.
Thanks in advance!
Using multiple filter types with metafizzy isotope filtering requires a multi-dimensional array to hold an array of each filter type.
I have a live example here
And js for the example is here
For a full code explanation see my answer to this question
have a look here
http://codepen.io/desandro/pen/btFfG
it's the same answer by DrewT but from the Isotope team.
all you have to do in order to filter is put every group under same object
var filters = {};//sould be outside the scope of the filtering function
$a.click(function(){//filtering function
var group = $optionSet.attr('data-filter-group');
var filterGroup = filters[group];
if (!filterGroup) {
filterGroup = filters[group] = [];
}
filters[ group ].push($this.attr('data-filter-value'));
})
now all you have to do is call getComboFilter function(you don't have to understand the code inside getComboFilter, think of it as a part of isotope)
var comboFilter = getComboFilter( filters );
$container.isotope({ filter: comboFilter});
function getComboFilter( filters ) {
var i = 0;
var comboFilters = [];
var message = [];
for ( var prop in filters ) {
message.push( filters[ prop ].join(' ') );
var filterGroup = filters[ prop ];
// skip to next filter group if it doesn't have any values
if ( !filterGroup.length ) {
continue;
}
if ( i === 0 ) {
// copy to new array
comboFilters = filterGroup.slice(0);
} else {
var filterSelectors = [];
// copy to fresh array
var groupCombo = comboFilters.slice(0); // [ A, B ]
// merge filter Groups
for (var k=0, len3 = filterGroup.length; k < len3; k++) {
for (var j=0, len2 = groupCombo.length; j < len2; j++) {
filterSelectors.push( groupCombo[j] + filterGroup[k] ); // [ 1, 2 ]
}
}
// apply filter selectors to combo filters for next group
comboFilters = filterSelectors;
}
i++;
}
var comboFilter = comboFilters.join(', ');
return comboFilter;
}
Here is the JS Fiddle code you can follow for Isotope Combination Filter with multiple select dropdown
$(document).ready(function(){
// init Isotope
var $grid = $('.grid').isotope({
itemSelector: '.item'
});
// store filter for each group
var filters = {};
$('.filters').on('change', '.filters-select', function(){
var $this = $(this);
var filterGroup = $this.attr('data-filter-group');
filters[ filterGroup ] = $this.val();
var filterValue = concatValues( filters );
$grid.isotope({ filter: filterValue });
});
// flatten object by concatting values
function concatValues( obj ) {
var value = '';
for ( var prop in obj ) {
value += obj[ prop ];
}
return value;
}
});
I could make it work but it was not as straightforward as I thought in a firts instance. I think you need to bear on mind a couple of things:
If you want to use a restrictive AND condition, then I suggest don´t use the "data-filter-value" option. Instead, put your filter variables as classes. Then, you will need to fire the isotope function by yourself in the onclick event. This is because otherwise you will fire the "default" isotope function when the user clicks the button and you won´t be able to achieve the restrictive AND mentioned.
In the same direction, you will need to use new variable names (so new classes) for each combination of nested filter options you are going to work with. Otherwise, I don´t see other way (at the moment) to get a restrictive AND condition.
This concept about restrictive and nonrestrictive AND clauses is like the difference between an outer and an inner join. It's the concept you should use if you want to handle two filters, one subfilter of the other one.
One example would be something like a list of ebooks which could be filtered by brand, an other subfilter based on the different range prices they belong to.
I show you below a code example where I implement this idea, it can give you a hint of what i mean:
HTML:
<!--Filters section -->
<div id="filters">
<ul id="optionSet1" class="option-set" data-option-key="filter">
<li><a id="filter10" href="#filter" class="selected">Everything</a></li>
<c:forEach var="brand" items="${brands}" varStatus="status" >
<li><a id="filter1${status.count}" href="#filter" class='${brand}'>${brand}</a></li>
</c:forEach>
</ul>
<ul id="optionSet2" class="option-set" data-option-key="filter">
<li><a id="filter20" href="#filter" class="selected">Any Price</a> </li>
<c:forEach var="brandPrice" items="${brandPrices}" varStatus="status" >
<li><a id="filter2${status.count}" href="#filter" class='${brandPrice}'>${brandPrice}</a></li>
</c:forEach>
</ul>
</div>
<!--Elements to filter -->
<div id="portfolio-wrapper" class="row">
<c:forEach var="publication" items="${publications}" varStatus="status" >
<div class='span4 portfolio-item ${publication.filterOption1} ${publication.filterOption2} ${publication.filterOption3}...'>
<!-- filterOption3 would be the combination of filterOption1 and filterOption2, you can make this building a string as the addition of filterOption1 and filterOption2 strings-->
<!--Content to display-->
</c:forEach>
</div>
javascript:
$(function(){
$("a[id^='filter1']").on('click', function() {
// alert($(this).attr('class'));
// alert($('#optionSet2 .selected').attr('class'));
var myclass = $('#optionSet2 .selected').attr('class');
myclass = myclass.replace(' selected','');
myclass = myclass.replace('selected','');
var myclass2 = $(this).attr('class');
myclass2 = myclass2.replace(' selected','');
myclass2 = myclass2.replace('selected','');
if($(myclass=='' && myclass2==''){
$('#portfolio-wrapper').isotope({ filter: '*'});
}else if(myclass==''){
$('#portfolio-wrapper').isotope({ filter: '.'+ myclass2+'' });
}else if(myclass2==''){
$('#portfolio-wrapper').isotope({ filter: '.'+myclass+'' });
}else{
$('#portfolio-wrapper').isotope({ filter: '.'+myclass2+myclass+''});
}
});
$("a[id^='filter2']").on('click', function() {
// alert($(this).attr('class'));
// alert($('#optionSet1 .selected').attr('class'));
var myclass = $('#optionSet1 .selected').attr('class');
myclass = myclass.replace(' selected','');
myclass = myclass.replace('selected','');
var myclass2 = $(this).attr('class');
myclass2 = myclass2.replace(' selected','');
myclass2 = myclass2.replace('selected','');
if(myclass=='' && myclass2==''){
$('#portfolio-wrapper').isotope({ filter: '*'});
}else if(myclass==''){
$('#portfolio-wrapper').isotope({ filter: '.'+ myclass2+'' });
}else if(myclass2==''){
$('#portfolio-wrapper').isotope({ filter: '.'+myclass+'' });
}else{
$('#portfolio-wrapper').isotope({ filter: '.'+myclass+myclass2+''});
}
});
});
Sorry to dredge this question up, but I recently ran into the same problem and (imho) wasted a lot of time solving this with combined selectors. (Combine every class from array A with every class from array B, etc.)
Use a filtering function. It allows arbitrary complexity and is probably the right choice for all non-trivial cases.
Most importantly, there is no performance benefit to using selectors (which you might expect if the selector were fed directly into a querySelector or jQuery call). Isotope iterates through each item and applies the selector as a function regardless:
return function( item ) {
return matchesSelector( item.element, filter );
};
Therefore, you might as well just put your filtering logic into a function instead of trying to generate a selector string.
来源:https://stackoverflow.com/questions/17553076/isotope-combination-filtering-multiple-selection