Finsweet Dependent Filters

Suppose you already have a working Finsweet filter setup with two dropdowns working. Wouldn’t it be nice if the second dropdown only shows those options that have elements with the first dropdowns selected option.

As a demo I will use a small example project. It displays a list of cars, which is filterable by the cars type and their brand.
Try it yourself or Clone it here.


Let’s first look at the structure of our page in Webflow.
We give our two select elements unique classes, so we can identify them later in code.

For our collection list we need to identify the whole collection list and its individual items.
It is also important that we can identify the attributes we want to filter by.

That is all that needs to be done in Webflow, so now let’s look at the code.

First we need to grab our two select elements and listen for the change event on one of them.
We also save our collection list items, because the Finsweet filter will dynamically add and remove list items from the DOM.

	const typeSelect = document.querySelector('.filter-form-select.type');
const brandSelect = document.querySelector('.filter-form-select.brand');
 
const items = Array.from(document.querySelectorAll('.collection-list .collection-list-item')); // Get all collection items
 
typeSelect.addEventListener('change', updateBrands); // Update when type selection changes

Before we look at the implementation of the updateBrands method, we will write ourselves a handy little helper method to display only specific options in our brand select.

	function displayBrands(brands) { // 1.
  brandSelect.querySelectorAll('option').forEach(option => { // 2.
    if (!option.value) return; // 3.
		
    const hidden = !brands.includes(option.value); // 4.
    option.hidden = hidden; // 5.
    option.disabled = hidden; // 6.
  });
}
  1. This method takes an array of the option values that should be displayed as a parameter.
  2. Loop through all option elements in the brand select element.
  3. If the option has no value, the default option, ignore it. It should always be displayed.
  4. If the brands array does not contain this option’s value, it should be hidden.
  5. Set the hidden attribute accordingly.
  6. Some browsers (e.g. Safari) do not support hidden on an option element. As a fallback, set the disabled attribute too. Disabled options cannot be selected and most likely appear greyed out.

After that is out of the way, we can start to implement our updateBrands method. 

	function updateBrands() {
  const selectedType = typeSelect.value;
  if (!selectedType) { // 1.
    const allBrands = Array.from(brandSelect.options).map(option => option.value); // 2.
 
    displayBrands(allBrands); // 3.
  } else {
      . . .
  }
}
  1. First we will cover what happens, when no type is selected. All brands should be displayed.
  2. We get all available options from the brand select element and convert them to an array. Then we map these option elements to their value attributes. We now have an array of all available values.
  3. Lastly we just give this array to our displayBrands method and it will show all option elements.

Now let’s look at what happens when there is a type selected

	} else {
  // 1.
  const brands = items
     .filter(item => { // 2.
        const type = item.querySelector('.type').textContent; // 3.
        return type === selectedType; // 4.
     })
     .map(item => { // 5.
        return item.querySelector('.brand').textContent; // 6.
     });
 
  displayBrands(brands); // 7.
}

  1. We transform the array of list items we saved early to an array of the brands we want to display.
  2. First we filter for items that have the same type as the selected type
  3. We get the corresponding type for each item by querying for the .type class.
  4. If the type is the same as the selected, return true to include this item.
  5. Now we map each of the items to their brands which should be displayed.
  6. So we return the brand value, by querying for the .brand class.
  7. At the end call our displayBrands method again, to show all brands that have at least some items with the selected type.

And that’s it! The second filter dropdown now only displays options that make sense for the selected option of the first filter dropdown. Here’s the full code

	const typeSelect = document.querySelector('.filter-form-select.type');
const brandSelect = document.querySelector('.filter-form-select.brand');
 
typeSelect.addEventListener('change', updateBrands); // Update when type selection changes
 
function updateBrands() {
   const selectedType = typeSelect.value;
   if (!selectedType) { // If no type is selected display all brands
       const allBrands = Array.from(brandSelect.options).map(option => option.value); // Get all available options and convert them to their values
 
       displayBrands(allBrands); // Display all brands that are in the select field
   } else {
       const items = Array.from(document.querySelectorAll('.collection-list .collection-list-item')); // Get all collection items
 
       // Transform to available brands in selected type
       const brands = items.map(item => {
           const type = item.querySelector('.type').textContent; // Get type of the item
 
           if (type === selectedType) { // If this type is selected, return the brand to add it to the list
               return item.querySelector('.brand').textContent;
           } // If this type is not selected, return nothing and ignore this item
       });
 
       displayBrands(brands); // Display all the brands that have the selected type
   }
}
 
function displayBrands(brands) {
   brandSelect.querySelectorAll('option').forEach(option => {
       if (!option.value) return; // Skip default option without value
 
       const hidden = !brands.includes(option.value);
       option.hidden = hidden;
       option.disabled = hidden;
   });
}
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

VW Arteon

Volkswagen

Sedan

BMW 4er Cabrio

BMW

Cabrio

VW Eos Cabrio

Volkswagen

Cabrio

Ford Explorer

Ford

SUV

Toyota Corolla Sedan

Toyota

Sedan

Tesla Model S

Tesla

Sedan

Ford Mondeo Sedan

Ford

Sedan

Toyota C-HR

Toyota

SUV

Tesla Model S Plaid

Tesla

Sedan

Audi A5 Cabriolet

Audi

Cabrio

BMW Z4 Cabrio

BMW

Cabrio

Audi Q7

Audi

SUV

VW Passat

Volkswagen

Sedan

BMW X6

BMW

SUV

Tesla Roadster

Tesla

Cabrio

Tesla Model X

Tesla

SUV

BMW X7

BMW

SUV

BMX X3

BMW

SUV

Audi A3 Sedan

Audi

Sedan

Ford Kuga

Ford

SUV

/**** TODO *****/