This pen is part of a tutorial on "Advanced Filtering" at https://mixitup.kunkalabs.com/learn/tutorial/advanced-filtering/
The values of the active buttons in each group are concatenated with "AND" logic.
A Pen by patrickkunka on CodePen.
<form class="controls" id="Filters"> | |
<!-- We can add an unlimited number of "filter groups" using the following format: --> | |
<fieldset> | |
<h4>Shapes</h4> | |
<button class="filter" data-filter=".triangle">Triangle</button> | |
<button class="filter" data-filter=".square">Square</button> | |
<button class="filter" data-filter=".circle">Circle</button> | |
</fieldset> | |
<fieldset> | |
<h4>Colours</h4> | |
<button class="filter" data-filter=".green">Green</button> | |
<button class="filter" data-filter=".blue">Blue</button> | |
<button class="filter" data-filter=".white">White</button> | |
</fieldset> | |
<button id="Reset">Clear Filters</button> | |
</form> | |
<div id="Container" class="container"> | |
<div class="mix triangle white"></div> | |
<div class="mix square white"></div> | |
<div class="mix circle green"></div> | |
<div class="mix triangle blue"></div> | |
<div class="mix square white"></div> | |
<div class="mix circle blue"></div> | |
<div class="mix triangle green"></div> | |
<div class="mix square blue"></div> | |
<div class="mix circle white"></div> | |
<div class="gap"></div> | |
<div class="gap"></div> | |
<div class="gap"></div> | |
<div class="gap"></div> | |
</div> | |
// To keep our code clean and modular, all custom functionality will be contained inside a single object literal called "buttonFilter". | |
var buttonFilter = { | |
// Declare any variables we will need as properties of the object | |
$filters: null, | |
$reset: null, | |
groups: [], | |
outputArray: [], | |
outputString: '', | |
// The "init" method will run on document ready and cache any jQuery objects we will need. | |
init: function(){ | |
var self = this; // As a best practice, in each method we will asign "this" to the variable "self" so that it remains scope-agnostic. We will use it to refer to the parent "buttonFilter" object so that we can share methods and properties between all parts of the object. | |
self.$filters = $('#Filters'); | |
self.$reset = $('#Reset'); | |
self.$container = $('#Container'); | |
self.$filters.find('fieldset').each(function(){ | |
self.groups.push({ | |
$buttons: $(this).find('.filter'), | |
active: '' | |
}); | |
}); | |
self.bindHandlers(); | |
}, | |
// The "bindHandlers" method will listen for whenever a button is clicked. | |
bindHandlers: function(){ | |
var self = this; | |
// Handle filter clicks | |
self.$filters.on('click', '.filter', function(e){ | |
e.preventDefault(); | |
var $button = $(this); | |
// If the button is active, remove the active class, else make active and deactivate others. | |
$button.hasClass('active') ? | |
$button.removeClass('active') : | |
$button.addClass('active').siblings('.filter').removeClass('active'); | |
self.parseFilters(); | |
}); | |
// Handle reset click | |
self.$reset.on('click', function(e){ | |
e.preventDefault(); | |
self.$filters.find('.filter').removeClass('active'); | |
self.parseFilters(); | |
}); | |
}, | |
// The parseFilters method checks which filters are active in each group: | |
parseFilters: function(){ | |
var self = this; | |
// loop through each filter group and grap the active filter from each one. | |
for(var i = 0, group; group = self.groups[i]; i++){ | |
group.active = group.$buttons.filter('.active').attr('data-filter') || ''; | |
} | |
self.concatenate(); | |
}, | |
// The "concatenate" method will crawl through each group, concatenating filters as desired: | |
concatenate: function(){ | |
var self = this; | |
self.outputString = ''; // Reset output string | |
for(var i = 0, group; group = self.groups[i]; i++){ | |
self.outputString += group.active; | |
} | |
// If the output string is empty, show all rather than none: | |
!self.outputString.length && (self.outputString = 'all'); | |
console.log(self.outputString); | |
// ^ we can check the console here to take a look at the filter string that is produced | |
// Send the output string to MixItUp via the 'filter' method: | |
if(self.$container.mixItUp('isLoaded')){ | |
self.$container.mixItUp('filter', self.outputString); | |
} | |
} | |
}; | |
// On document ready, initialise our code. | |
$(function(){ | |
// Initialize buttonFilter code | |
buttonFilter.init(); | |
// Instantiate MixItUp | |
$('#Container').mixItUp({ | |
controls: { | |
enable: false // we won't be needing these | |
}, | |
callbacks: { | |
onMixFail: function(){ | |
alert('No items were found matching the selected filters.'); | |
} | |
} | |
}); | |
}); |
body, button{ | |
font-family: 'Helvetica Neue', arial, sans-serif; | |
color: white; | |
} | |
/** | |
* Form & Button Styles | |
*/ | |
h4{ | |
font-weight: 700; | |
margin-bottom: .5em; | |
} | |
label{ | |
font-weight: 300; | |
} | |
button{ | |
display: inline-block; | |
padding: .4em .8em; | |
background: #666; | |
border: 0; | |
margin: 0 .2em; | |
color: #ddd; | |
font-size: 16px; | |
font-weight: 300; | |
border-radius: .25em; | |
cursor: pointer; | |
} | |
button.active{ | |
background: #68b8c4; | |
} | |
button:focus{ | |
outline: 0 none; | |
} | |
button:first-of-type{ | |
margin-left: 0; | |
} | |
button:last-of-type{ | |
margin-right: 0; | |
} | |
button:focus{ | |
outline: 0 none; | |
} | |
.controls{ | |
background: #333; | |
padding: 2%; | |
} | |
fieldset{ | |
display: inline-block; | |
vertical-align: top; | |
margin: 0 1em 0 0; | |
background: #444; | |
padding: .5em; | |
border-radius: 3px; | |
} | |
.checkbox{ | |
display: block; | |
position: relative; | |
cursor: pointer; | |
margin-bottom: 8px; | |
} | |
.checkbox input[type="checkbox"]{ | |
position: absolute; | |
display: block; | |
top: 0; | |
left: 0; | |
height: 100%; | |
width: 100%; | |
cursor: pointer; | |
margin: 0; | |
opacity: 0; | |
z-index: 1; | |
} | |
.checkbox label{ | |
display: inline-block; | |
vertical-align: top; | |
text-align: left; | |
padding-left: 1.5em; | |
} | |
.checkbox label:before, | |
.checkbox label:after{ | |
content: ''; | |
display: block; | |
position: absolute; | |
} | |
.checkbox label:before{ | |
left: 0; | |
top: 0; | |
width: 18px; | |
height: 18px; | |
margin-right: 10px; | |
background: #ddd; | |
border-radius: 3px; | |
} | |
.checkbox label:after{ | |
content: ''; | |
position: absolute; | |
top: 4px; | |
left: 4px; | |
width: 10px; | |
height: 10px; | |
border-radius: 2px; | |
background: #68b8c4; | |
opacity: 0; | |
pointer-events: none; | |
} | |
.checkbox input:checked ~ label:after{ | |
opacity: 1; | |
} | |
.checkbox input:focus ~ label:before{ | |
background: #eee; | |
} | |
/** | |
* Container/Target Styles | |
*/ | |
.container{ | |
padding: 2%; | |
background: #68b8c4; | |
text-align: justify; | |
} | |
.container .mix, | |
.container .gap{ | |
width: 100px; | |
display: inline-block; | |
margin: 0 5%; | |
} | |
.container .mix{ | |
width: 100px; | |
height: 100px; | |
margin: 5%; | |
background: white; | |
display: none; | |
} | |
.container .mix.green{ | |
background: #a6e6a7; | |
} | |
.container .mix.blue{ | |
background: #6bd2e8; | |
} | |
.container .mix.circle{ | |
border-radius: 999px; | |
} | |
.container .mix.triangle{ | |
width: 0; | |
height: 0; | |
border: 50px solid transparent; | |
border-top-color: #68b8c4; | |
border-left-color: #68b8c4; | |
} |