Skip to main content

Multiple dropdown filters

Demo

Installation

Import the function into the JavaScript file of your project

import { MultipleFilters } from "@overdog/fn"
Note: You need to combine filters with the dropdown element to achieve the look of this demo. Your forms will be placed inside dropdowns.

Create and initialize an instance

new MultipleFilters("#filter-wrapper-multiple-dropdown", {
   divIds: ["listing"]
}).init()

Twig template Code

{# --------------------- SET RELATEDTO ARRAY #}

{% set relatedTo = [] %}

{# --------------------- CATEGORY GROUP 1 - COLORS #}

{% set colorsUrlQueryName = 'colors'|t %} {# variable also used in the form name #}
{% set colorsUrlQueryValue = craft.app.request.getQueryParam(colorsUrlQueryName)|split(',')|filter %}
{% if colorsUrlQueryValue %}
  {% set relatedTo = relatedTo|merge([{
    targetElement: craft.categories({ group: 'groupFoodColors', slug: colorsUrlQueryValue }).ids()
  }]) %}
{% endif %}

{# --------------------- CATEGORY GROUP 2 - TYPES #}

{% set typesUrlQueryName = 'types'|t %} {# variable also used in the form name #}
{% set typesUrlQueryValue = craft.app.request.getQueryParam(typesUrlQueryName)|split(',')|filter %}
{% if typesUrlQueryValue %}
  {% set relatedTo = relatedTo|merge([{
    targetElement: craft.categories({ group: 'groupFoodTypes', slug: typesUrlQueryValue }).ids()
  }]) %}
{% endif %}

{# --------------------- MERGE THE CATEGORIES FOR THE RELATEDTO #}

{% if relatedTo|length > 1 %}
  {% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}

{# --------------------- MAIN QUERY - with eager loading the categories #}

{% set foodQuery = craft.entries()
  .section('structureFood')
  .orderBy('title')
  .relatedTo(relatedTo ? relatedTo : null)
  .with(['structureFoodColors', 'structureFoodTypes'])
  .all()
%}

{# --------------------- FETCH CATEGORIES GROUP TO POPULATE FILTERS #}

{# categories group #}
{% set typesCatGroup = craft.categories().group('groupFoodTypes').all() %}
{% set colorsCatGroup = craft.categories().group('groupFoodColors').all() %}


{# --------------------- FILTERS LAYOUT #}

{# ID is used by Javascript #}
<div id="filter-wrapper-multiple-dropdown" class="grid grid-cols-1 md:grid-cols-3 gap-half mb-half">

  {# TYPES FORM - Dropdown filter multiple choices #}
  {% if typesCatGroup|length %}
    <div class="filter-dropdown relative">
      <button 
         class="flex items-center border-b border-[#a5adb9] h-[50px] justify-between text-left w-full"
         aria-expanded="false"
         aria-controls="form-type"
      >        
      <span>{{ 'Filter by type'|t }}</span>
         {# caret #}
         <span class="
            border-b-0 border-x-transparent border-x-[0.3em] border-t-[0.3em] border-t-slate-600
            transition-all ease-linear duration-200
            parent-is-open:rotate-180
            ">
         </span>
      </button>
      <div 
         id="form-types" {# only for aria-controls - not used by JS #}
         class="absolute hidden left-0 p-4 bg-white rounded-b-md shadow-md overflow-hidden w-full z-10
         parent-is-open:block"
      >
        <form data-fn-filter-group="{{ typesUrlQueryName }}">
          <fieldset>
            <legend class="sr-only">{{ 'Filter by type'|t }}</legend>
            {% for category in typesCatGroup %}
              <div class="flex items-center relative py-1">
                  <input 
                     id="{{ category.slug }}" 
                     class="inset-0 opacity-0 absolute -z-1 peer" 
                     type="checkbox"
                     name="{{ category.slug }}" 
                     value="{{ category.slug }}" {{(category.slug in typesUrlQueryValue) ? 'checked' }}
                  />
                  <label
                     class="cursor-pointer flex items-center w-full
                     peer-checked:before:bg-black
                     before:bg-[#c4cad2] before:cursor-pointer before:inline-block before:h-4 before:w-4 before:mr-4 before:shrink-0" 
                     for="{{ category.slug }}"
                  >
                     {{ category.title }}
                  </label>
              </div>
            {% endfor %}
          </fieldset>
        </form>
      </div>
    </div>
  {% endif %}

  {# COLORS FORMS - Dropdown filter fake select single choice #}
  {% if colorsCatGroup|length %}
    <div class="filter-dropdown relative">
      <button 
         class="flex items-center border-b border-[#a5adb9] h-[50px] justify-between text-left w-full"
         aria-expanded="false"
         aria-controls="form-colors"
      >        
      <span data-fn-dropdown-text>{{ 'Filter by colors'|t }}</span>
      {# caret #}
      <span class="
         border-b-0 border-x-transparent border-x-[0.3em] border-t-[0.3em] border-t-slate-600
         transition-all ease-linear duration-200
         parent-is-open:rotate-180
         ">
      </span>
   </button>
      <div
         id="form-colors" {# only for aria-controls - not used by JS #}
         class="absolute hidden left-0 bg-white rounded-b-md shadow-md overflow-hidden w-full z-10
         parent-is-open:block"
      >
        <form data-fn-filter-group="{{ colorsUrlQueryName }}">
          <fieldset>
            <legend class="sr-only">{{ 'Filter by colors'|t }}</legend>
            <div>
               <input 
                  id="all" 
                  class="absolute inset-0 opacity-0 -z-1"
                  type="radio" 
                  name="cat-radio" 
                  value=""
               />
              <label class="cursor-pointer block px-3.5 py-1.5 w-full hover:bg-[#e7edee]" for="all">{{ 'All colors'|t }}</label>
            </div>
            {% for category in colorsCatGroup %}
              {# name must be the same for radio button #}
              <div>
                  <input 
                     id="{{ category.slug }}"
                     class="absolute inset-0 opacity-0 -z-1" 
                     type="radio" 
                     name="cat-radio" 
                     value="{{ category.slug }}" {{(category.slug in colorsUrlQueryValue) ? 'checked' }}
                  />
                <label class="cursor-pointer block px-3.5 py-1.5 w-full hover:bg-[#e7edee]" for="{{ category.slug }}">{{ category.title }}</label>
              </div>
            {% endfor %}
          </fieldset>
        </form>
      </div>
    </div>
  {% endif %}

</div>


{# --------------------- LAYOUT WIH LOOP FOR CARDS #}

{# ID is used by Javascript #}
<div id="listing" class="grid grid-col-1 md:grid-cols-3 gap-third opacity-1 is-loading:opacity-40 transition-opacity">
   {% if foodQuery|length %}
    {{ include('_lib-fn/_card', { queryName: foodQuery }, ignore_missing = true )}}
  {% else %}
    <div>{{ ('There is no content matching your criteria.')|t }}</div>
  {% endif %}
</div>