Filter CMS Form Select Options

Hi Everyone,

Is there a way to filter the options in a form select, which are populated with CMS items?

For example:

Select 1: Contains a list of countries.
Select 2: Contains a list of cities.

If ‘New Zealand’ is chosen as a country in the first select field, could we then filter the options in the ‘Cities’ select field to include only cities within New Zealand?

Both of these selections are populated by CMS items.

Thanks,
Marko

Hello @Marko, this is not possible with the CMS attribute. Anyway, it can be achieved using a custom script.
Could you define to me how you determine which Cities are under a certain country, are they nested within the CMS item and I’ll see how we can come up with a custom script

1 Like

Thanks @josephmukunga! The cities are catagorised using a reference field in the cms collection. The reference field is linked to the country collection. Let me know if you have any other questions. Thanks so much for taking a look.

Could you please share the published site and read-only links

@josephmukunga My apologies for the late reply. I became unwell and was away from my computer for quite some time. I’m a lot better now and glad to get back into it. Here are the links:

https://cms-filter-select.webflow.io/

https://preview.webflow.com/preview/cms-filter-select?utm_medium=preview_link&utm_source=designer&utm_content=cms-filter-select&preview=b7c41aee670fe51820ce37c3fcd90193&workflow=preview

Any questions, please let me know.

1st you’ll have to remove all the CMS Select implementation.

Have this setup:

  1. For the department collection list wrapper, give it the attribute department="cms"
  2. For the department text that you need to populate the dropdown, give it the attribute department="text"
  3. For the roles collection list wrapper, give it the attribute role="cms"
  4. For the roles text that you need to populate the dropdown, give it the attribute role="text"
  5. For the department that the role exist to give it the attribute role="department" and ensure this is within each role cms collection item
  6. Give the department select field the attribute department="select"
  7. Give the role select field the attribute role="select"

Then add the scripts below:

<script>
const departments = document.querySelectorAll('[department="cms"] [department="text"');
const rolesCMSItems = document.querySelectorAll('[role="cms"');
const roles = rolesCMSItems.map(cmsItem => {
  const role = {
    role: document.querySelector('[role="text"'),
    department: document.querySelector('[role="department"')
  }
})

        let departmentDropdown = document.querySelector('[department="select"]');
        for (var i = 0; i < departments.length; i++) {
            let option = document.createElement("option");
            option.value = departments[i];
            option.textContent = departments[i];
            departmentDropdown.appendChild(option);
        }
        
        // Function to filter roles based on department selection
        function filterRoles() {
            let departmentDropdown = document.querySelector('[department="select"]');
            let roleDropdown = document.querySelector('[role="select"]');;
            let selectedDepartment = departmentDropdown.value;
            
            // Clear previous options
            roleDropdown.innerHTML = "";
            
            // Filter roles based on selected department
            let filteredRoles = roles.filter(function(role) {
                return selectedDepartment === "all" || role.department === selectedDepartment;
            });
            
            // Populate role dropdown with filtered roles
            for (var i = 0; i < filteredRoles.length; i++) {
                var option = document.createElement("option");
                option.value = filteredRoles[i].role;
                option.textContent = filteredRoles[i].role;
                roleDropdown.appendChild(option);
            }
        }

      departments.addEventListener('change', () => {
        filterRoles()
     })

       
</script>

Thanks @josephmukunga! I really appreciate it! I’ve implemented the changes but have made a mistake somewhere as it’s not quite working. Can you see where I went wrong? Updated links are below:

https://preview.webflow.com/preview/cms-filter-select?utm_medium=preview_link&utm_source=designer&utm_content=cms-filter-select&preview=b7c41aee670fe51820ce37c3fcd90193&workflow=preview

https://cms-filter-select.webflow.io/

2 things

  1. The roles cms wrapper does not have the attribute ``
  2. Replace the previous script with this
<script>
const departments = document.querySelectorAll(
  '[department="cms"] [department="text"]'
);
const rolesCMSItems = document.querySelectorAll('[role="cms"]');
const roles = rolesCMSItems.map((cmsItem) => {
  const role = {
    role: cmsItem.querySelector('[role="text"]'),
    department: cmsItem.querySelector('[role="department"]')
  };
  return role;
});

let departmentDropdown = document.querySelector('[department="select"]');
for (var i = 0; i < departments.length; i++) {
  let option = document.createElement("option");
  option.value = departments[i];
  option.textContent = departments[i];
  departmentDropdown.appendChild(option);
}

// Function to filter roles based on department selection
function filterRoles() {
  let departmentDropdown = document.querySelector('[department="select"]');
  let roleDropdown = document.querySelector('[role="select"]');
  let selectedDepartment = departmentDropdown.value;

  // Clear previous options
  roleDropdown.innerHTML = "";

  // Filter roles based on selected department
  let filteredRoles = roles.filter(function (role) {
    return (
      selectedDepartment === "all" || role.department === selectedDepartment
    );
  });

  // Populate role dropdown with filtered roles
  for (var i = 0; i < filteredRoles.length; i++) {
    var option = document.createElement("option");
    option.value = filteredRoles[i].role;
    option.textContent = filteredRoles[i].role;
    roleDropdown.appendChild(option);
  }
}

departments.addEventListener("change", () => {
  filterRoles();
});

</script>

Thanks, Joseph! I updated the site but I’m still missing something, please see here:

https://cms-filter-select.webflow.io/

https://preview.webflow.com/preview/cms-filter-select?utm_medium=preview_link&utm_source=designer&utm_content=cms-filter-select&preview=b7c41aee670fe51820ce37c3fcd90193&workflow=preview

    <script>
      const departments = document.querySelectorAll(
        '[department="cms"] [department="text"]'
      );
      const rolesCMSItems = document.querySelectorAll(
        '[role="cms"] [role="listitem"]'
      );
      const roles = Array.from(rolesCMSItems).map((cmsItem) => {
        const role = {
          role: cmsItem.querySelector('[role="text"]').innerText,
          department: cmsItem.querySelector('[role="department"]').innerText,
        };
        return role;
      });

      let departmentDropdown = document.querySelector('[department="select"]');
      for (var i = 0; i < departments.length; i++) {
        const department = departments[i];
        const departmentText = department.innerText;
        let option = document.createElement("option");
        option.value = departmentText;
        option.textContent = departmentText;
        departmentDropdown.appendChild(option);
      }

      // Function to filter roles based on department selection
      function filterRoles() {
        let roleDropdown = document.querySelector('[role="select"]');
        let selectedDepartment = departmentDropdown.value;
        // Clear previous options
        roleDropdown.innerHTML = "";
        // Filter roles based on selected department
        let filteredRoles = roles.filter((role) => {
          return (
            selectedDepartment === "all" ||
            role.department === selectedDepartment
          );
        });

        // Populate role dropdown with filtered roles
        for (var i = 0; i < filteredRoles.length; i++) {
          var option = document.createElement("option");
          option.value = filteredRoles[i].role;
          option.textContent = filteredRoles[i].role;
          roleDropdown.appendChild(option);
        }
      }

      departmentDropdown.addEventListener("change", () => {
        filterRoles();
      });
    </script>

This should now be ok

1 Like

@josephmukunga Hi, how are you? I apologise for the delayed response; I was preoccupied with another job. Thank you for sending the updated code! Very appreciated!

It seems to work for some items but not for all. Could you help me identify where I might be going wrong? For instance, it works for ‘Camera’.

https://cms-filter-select.webflow.io/

EDIT: I’ve noticed that only the first set of pagination roles are loading in the ‘Role’ dropdown menu. How can I modify it to load all the roles? So far, I have added FinSweet’s CMS Load attribute with a render all added.

Hi everyone,

Does anyone have any ideas on how I can implement the above? Any help would be much appreciated!

Cheers!

Hey @Marko! I hope you are doing great!

If you have used CMS Load to load your CMS Collection where you are generating the options you can wait until all items have finished loading and execute the script above inside the Attributes callback. The code would look something like this

<script>
  window.fsAttributes = window.fsAttributes || [];
  window.fsAttributes.push([
    'cmsload',
    (listInstances) => {
      console.log('cmsload has finished laoding!');
      const departments = document.querySelectorAll('[department="cms"] [department="text"]');
      const rolesCMSItems = document.querySelectorAll('[role="cms"] [role="listitem"]');
      const roles = Array.from(rolesCMSItems).map((cmsItem) => {
        const role = {
          role: cmsItem.querySelector('[role="text"]').innerText,
          department: cmsItem.querySelector('[role="department"]').innerText,
        };
        return role;
      });

      let departmentDropdown = document.querySelector('[department="select"]');
      for (var i = 0; i < departments.length; i++) {
        const department = departments[i];
        const departmentText = department.innerText;
        let option = document.createElement('option');
        option.value = departmentText;
        option.textContent = departmentText;
        departmentDropdown.appendChild(option);
      }

      // Function to filter roles based on department selection
      function filterRoles() {
        let roleDropdown = document.querySelector('[role="select"]');
        let selectedDepartment = departmentDropdown.value;
        // Clear previous options
        roleDropdown.innerHTML = '';
        // Filter roles based on selected department
        let filteredRoles = roles.filter((role) => {
          return selectedDepartment === 'all' || role.department === selectedDepartment;
        });

        // Populate role dropdown with filtered roles
        for (var i = 0; i < filteredRoles.length; i++) {
          var option = document.createElement('option');
          option.value = filteredRoles[i].role;
          option.textContent = filteredRoles[i].role;
          roleDropdown.appendChild(option);
        }
      }

      departmentDropdown.addEventListener('change', () => {
        filterRoles();
      });
    },
  ]);
</script>

Let me know if this fixes your issues! :slight_smile:

1 Like

Thanks @Support-Luis, this worked! Thanks a bunch.

1 Like

You are welcome! :muscle: