Go to homepage

Projects /

Filter

A plugin to filter/sort items in a grid.

View Demo

How to

The Filter component is used to filter and sort a gallery of items.


🔍 On this page:

  1. Gallery
  2. Controls
  3. Filtering
  4. Sorting
  5. Fallback Message
  6. Additional Options
  7. Initial Status
  8. Custom Sorting/Filtering functions
  9. More Filtering/Sorting Input types
  10. Custom Events

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>

Controls #

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>

Filtering #

You can use the following filtering controls:

  • a list of radio input (one selected option at a time);
  • a list of checkbox input (multiple selected options at a time);
  • a list of buttons (customizable to have one selected option or multiple options at a time);
  • a <select> element (one selected option at a time).

You can use multiple filtering controls of different type for the same gallery.

Filtering - Radio/Checkbox Buttons #

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>

Filtering - Group of Buttons #

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>

Filtering - <select> element #

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).

Sorting #

You can use the following sorting controls:

  • a list of radio buttons;
  • a list of buttons;
  • a <select> element.

You can have one active sorting option at a time.

Sorting - Radio Buttons #

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>

Sorting - Group of Buttons #

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>

Sorting - <select> element #

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>

Fallback Message #

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>

Additional Options #

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').

Initial Status #

Your gallery can be filtered on load. To do that you need to:

  • if you are using a list of radio/checkbox buttons, add the checked attribute to the selected filter/sort <input>;
  • if you are using a <select> element, add the selected attribute to the selected filter/sort <option>;
  • if you are using a list of buttons, add the .js-filter-selected class to the selected filter/sort <button>.

Custom Sorting/Filtering functions #

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
  }
});

Custom Filtering Function #

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.

Custom Sorting Function #

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.

More Filtering/Sorting Input types #

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:

  1. an aria-controls equal to the ID value of the .js-filter element;
  2. a class .js-filter__custom-control;
  3. a 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;
  }
});

Custom Events #

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 

Categories

Bug report & feedback

Component Github page ↗

Project duplicated

Project created

Globals imported

There was an error while trying to export your project. Please try again or contact us.