Filter
A plugin to filter/sort items in a grid.
Projects /
A plugin to filter/sort items in a grid.
The Filter component is used to filter and sort a gallery of items.
Create a gallery using CodyFrame's grid utility classes: use the .grid
and .gap-{size}
classes on the gallery container, and the .col-{number}
and .col-{number}@{breakpoint}
classes on the grid items.
Read more about these classes on the Grid & Layout documentation page.
Make sure to add the .js-filter
class to the gallery container and the .js-filter__item
to each item of the gallery.
Add an ID attribute to the .js-filter
element (more info about this in the Controls section).
<ul class="grid gap-md js-filter" id="filter-gallery">
<li class="col-6 col-4@md js-filter__item">
<!-- // content here -->
</li>
<li class="col-6 col-4@md js-filter__item">
<!-- // content here -->
</li>
<!-- additional items -->
</ul>
Each list of controls (e.g., 'Filter by category', 'Sort by Index') needs to have an aria-controls
attribute equal to the ID value of the .js-filter
element.
<fieldset>
<legend>Filter by Category</legend>
<ul aria-controls="filter-gallery">
<!-- list of categories -->
</ul>
</fieldset>
<fieldset>
<legend>Sort by Index</legend>
<ul aria-controls="filter-gallery">
<!-- list of Index Sorting options -->
</ul>
</fieldset>
You can use the following filtering controls:
<select>
element (one selected option at a time).You can use multiple filtering controls of different type for the same gallery.
Add a data-filter
equal to the filter value (e.g., category-1) to each input element. If you include an All/Reset option, set its data-filter
equal to *:
<!-- List of radio input -->
<fieldset>
<legend>Filter by Category</legend>
<ul aria-controls="filter-gallery">
<li>
<input type="radio" name="radioCategory" id="radioCategoryAll" data-filter="*">
<label for="radioCategoryAll">All</label>
</li>
<li>
<input type="radio" name="radioCategory" id="radioCategory1" data-filter="category-1">
<label for="radioCategory1">Category 1</label>
</li>
<li>
<input type="radio" name="radioCategory" id="radioCategory2" data-filter="category-2">
<label for="radioCategory2">Category 2</label>
</li>
<!-- all other category options -->
</ul>
</fieldset>
<!-- List of checkbox input -->
<fieldset>
<legend>Filter by Color</legend>
<ul aria-controls="filter-gallery">
<li>
<input type="checkbox" id="checkboxBgPrimary" data-filter="bg-primary">
<label for="checkboxBgPrimary">Primary</label>
</li>
<li>
<input type="checkbox" id="checkboxBgAccent" data-filter="bg-accent">
<label for="checkboxBgAccent">Accent</label>
</li>
<!-- all other color options -->
</ul>
</fieldset>
To each item in your gallery, add a data-filter
equal to the list of filters that apply to that element:
<ul class="grid gap-sm js-filter" id="filter-gallery">
<li class="col-4 js-filter__item" data-filter="category-1 bg-accent">
<!-- content here -->
</li>
<li class="col-4 js-filter__item" data-filter="category-2 category-3 bg-primary">
<!-- content here -->
</li>
<!-- other gallery items -->
</ul>
Add a data-filter
to each button element (same as with radio/checkbox buttons).
To each item in your gallery, add a data-filter
equal to the list of filters that apply to that element.
You can customize the behaviour of a button control adding the following attributes to the control list element:
data-selected-class
: pass the list of classes you want to add to the button element when it is selected (e.g., btn--selected
);data-filter-checkbox
: set this to "true" if you want more buttons to be selectable at the same time.<!-- the .color-primary class will be added to <button> when selected -->
<ul aria-controls="filter-gallery" data-selected-class="color-primary">
<li><button data-filter="*">All</button></li>
<li><button data-filter="category-1">Category 1</button></li>
<li><button data-filter="category-2">Category 2</button></li>
<li><button data-filter="category-3">Category 3</button></li>
</ul>
<!-- you can select multiple colors -->
<ul aria-controls="filter-gallery" data-filter-checkbox="true">
<li><button data-filter="bg-primary">Primary</button></li>
<li><button data-filter="bg-accent">Accent</button></li>
<li><button data-filter="bg-contrast-lower">Contrast Lower</button></li>
</ul>
Add a data-filter="true"
to the <select>
element and set the value
of the <option>
equal to the filter value:
<select aria-controls="filter-gallery" data-filter="true">
<option value="*">All</option>
<option value="category-1">Category 1</option>
<option value="category-2">Category 2</option>
<option value="category-3">Category 3</option>
</select>
To each item in your gallery, add a data-filter
equal to the list of filters that apply to that element (same as with radio/checkbox buttons).
You can use the following sorting controls:
<select>
element.You can have one active sorting option at a time.
Add a data-sort
equal to the sort option (e.g., name) to each input element. If you are including an All/Reset option, set its data-sort
equal to *.
By default, the order used is ascendent. You can add a data-sort-order="desc"
to use a descendent order.
If you are using a number as sort option (e.g, index), add a data-sort-number="true"
.
<fieldset>
<legend>Sort by</legend>
<ul aria-controls="filter-gallery">
<li>
<input type="radio" name="radioSort" id="radioSortNone" data-sort="*">
<label for="radioSortNone">No sorting</label>
</li>
<li>
<input type="radio" name="radioSort" id="radioSortIndex" data-sort="index" data-sort-number="true">
<label for="radioSortIndex">Index</label>
</li>
<li>
<input type="radio" name="radioSort" id="radioSortIndexDesc" data-sort="index" data-sort-number="true" data-sort-order="desc">
<label for="radioSortIndexDesc">Index Desc</label>
</li>
<li>
<input type="radio" name="radioSort" id="radioSortName" data-sort="name">
<label for="radioSortName">Name</label>
</li>
</ul>
</fieldset>
To each item in your gallery, add a data-sort-{value}
equal to the value of that sorting option.
For example, if sorting by index, add a data-sort-index="indexValue"
. If sorting by name, add a data-sort-name="nameValue"
:
<ul class="grid gap-sm js-filter" id="filter-gallery">
<li class="col-6 col-4@md js-filter__item" data-sort-index="2" data-sort-name="name1">
<!-- content here -->
</li>
<li class="col-6 col-4@md js-filter__item" data-sort-index="4" data-sort-name="name2">
<!-- content here -->
</li>
<!-- other items -->
</ul>
Add a data-sort
to each button element (same as with radio buttons).
To each item in your gallery, add a data-sort-{value}
equal to the value of that sorting option.
For each <button>
, you can use a data-sort-order
and data-sort-number
(same as with radio buttons).
You can add to the controls list element a data-selected-class
attribute equal to the class list you want to add to the <button>
element when it's selected (e.g., btn--selected
):
<ul aria-controls="filter-gallery" data-selected-class="color-primary">
<li><button data-sort="index" data-sort-number="true">Index</button></li>
<li><button data-sort="index" data-sort-number="true" data-sort-order="desc">Index Desc</button></li>
</ul>
Add a data-sort="true"
to the <select>
element and set the value of the <option>
equal to the sorting option.
For each <option>
, you can use a data-sort-order
and data-sort-number
(same as with radio buttons).
<select aria-controls="filter-gallery" data-sort="true">
<option value="*">Reset</option>
<option value="index" data-sort-number="true">Index</option>
<option value="index" data-sort-order="desc" data-sort-number="true">Index desc</option>
</select>
If you want to show a fallback message when the filtering returns no results, create an element with a data-fallback-gallery-id
equal to the ID attribute of the gallery.
<div data-fallback-gallery-id="filter-gallery">
<p>No results</p>
</div>
Add the following attributes to the .js-filter
element to customize the plugin:
data-filter-duration
: the duration of the filtering animation (in ms, e.g., 500). Default is 400;data-filter-animation
: you can set it to 'off' if you do not want to animate items (default is 'on').Your gallery can be filtered on load. To do that you need to:
checked
attribute to the selected filter/sort <input>
;<select>
element, add the selected
attribute to the selected filter/sort <option>
;.js-filter-selected
class to the selected filter/sort <button>
.You can customize the filter functionality using the Filter
object and passing custom functions to this object.
To do that, do not add the .js-filter
class to the gallery container; this way, the filter will not be initialized with the default parameters (your custom configuration will be used).
In your JS, inizialize the gallery passing your custom filtering/sorting function:
new Filter({
element: document.getElementById('filter-gallery'), // this is your gallery element
customFilterFn: function(items){
// function code here
},
customSortFn: function(items){
// function code here
}
});
If you want to use a custom filter function for one of your controls, set the data-filter
attribute of the control equal to the function and then pass the function as configuration parameter.
Let's say that you want to only show items that have a even data-order
value. We'll define the orderEven
function:
<fieldset>
<legend>Filter by</legend>
<ul aria-controls="filter-gallery">
<li>
<input type="checkbox" id="checkboxOrderEven" data-filter="orderEven">
<label for="checkboxOrderEven">Show items with even order value</label>
</li>
<!-- other filters -->
</ul>
</fieldset>
<ul class="grid gap-sm" id="filter-gallery">
<li class="col-4 js-filter__item" data-order="4">
<!-- content here-->
</li>
<!-- other items here -->
</ul>
<script>
new Filter({
element: document.getElementById('filter-gallery'), // this is your gallery element
orderEven: function(items){ // custom filter function
var filteredArray = [];
for(var i = 0; i < items.length; i++) {
filteredArray[i] = parseInt(items[i].getAttribute('data-order')) % 2 == 0;
}
return filteredArray;
}
});
</script>
The custom function has to return an array of booleans: true if the item is visible, false otherwise.
If you want to use a custom sort function for one of your controls, set the data-sort
attribute of the control equal to the function and then pass the function as configuration parameter.
Let's say that you want to sort items by the sum of two values (data-order
and data-score
). We'll define the scoreOrder
function:
<fieldset>
<legend>Sort by</legend>
<ul aria-controls="filter-gallery">
<li>
<input type="radio" id="radioScoreOrder" data-sort="scoreOrder">
<label for="radioScoreOrder">Sort by score+order sum</label>
</li>
<!-- other sort options -->
</ul>
</fieldset>
<ul class="grid gap-sm" id="filter-gallery">
<li class="col-4 js-filter__item" data-order="4" data-score="5">
<!-- content here-->
</li>
<!-- other items here -->
</ul>
<script>
new Filter({
element: document.getElementById('filter-gallery'), // this is your gallery element
scoreOrder: function(items){ // custom score function
items.sort(function(left, right) {
var leftVal = parseInt(left[0].getAttribute('data-order')) + parseInt(left[0].getAttribute('data-score')),
rightVal = parseInt(right[0].getAttribute('data-order')) + parseInt(right[0].getAttribute('data-score'));
return leftVal > rightVal ? -1 : 1;
});
return items;
});
</script>
The custom function accepts as argument an array: each item of the array has, as first element, one item of the gallery and as second element the initial index of the item. The function needs to sort the array and return it.
There may be cases where you need to use, as your filter/sorting controllers, input types different from the ones already covered (checkbox/radio/select/buttons).
For example, you want to use an <input type="range">
to filter your products by price (see the Advanced Filter component).
In this case, you will need to use a custom filter/sorting function (as explained in the sections above).
To make sure the custom filter works properly, you'll need to add, to the wrapper of your input, the following:
aria-controls
equal to the ID value of the .js-filter
element;.js-filter__custom-control
;data-filter
(or data-sort
) equal to your custom function.<div class="js-filter__custom-control" aria-controls="filter-gallery" data-filter="priceRange">
<div>
<label for="priceMinValue">Price min value</label>
<input type="range" id="priceMinValue" name="priceMinValue" min="0" max="100" value="0">
</div>
<div>
<label for="priceMaxValue">Price max value</label>
<input type="range" id="priceMaxValue" name="priceMaxValue" min="0" max="100" value="100">
</div>
</div>
You can define the priceRange
custom function when initializing the filter:
new Filter({
element: document.getElementById('filter-gallery'), // this is your gallery element
priceRange: function(items){ // custom price function
var filteredArray = [],
minVal = document.getElementById('priceMinValue').value,
maxVal = document.getElementById('priceMaxValue').value;
for(var i = 0; i < items.length; i++) {
var price = parseInt(items[i].getAttribute('data-price'));
filteredArray[i] = (price >= minVal) && (price <= maxVal);
}
return filteredArray;
}
});
At the end of each gallery filtering, the custom filter-selection-updated
event is emitted. You can listen to this event if you need to perform actions after a filtering animation (like updating number of results):
var filterGallery = document.getElementsByClassName('js-filter')[0];
filterGallery.addEventListener('filter-selection-updated', function(event){
// do your actions here
});
If you need to trigger a gallery filtering using an external controller (e.g., when resetting a form), you can use the update-filter-results
event:
var filterGallery = document.getElementsByClassName('js-filter')[0],
customEvent = new CustomEvent('update-filter-results');
filterGallery.dispatchEvent(customEvent); // gallery will be filtered