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.
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
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.
@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:
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:
@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’.
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.
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>
Hello @Support-Luis , I badly need this functionality but I can’t seem to make it work. May have to do something with the new V2 attributes added.
I processed your code solution through Claude like this and something’s happening but I’m not quite there. Could you please have a look at the staging link here and the read-only here? Here’s a Loom as well.
// Wait for Finsweet to populate the selects
setTimeout(() => {
// Read all bike shop CMS items and store shop + city pairs
const bikeShopItems = document.querySelectorAll(‘[fs-cmsselect-element=“text-value-2”]’);
const bikeShops = ;
bikeShopItems.forEach((item) => {
const shopName = item.textContent.trim();
// The city reference is a sibling/nearby element in the same CMS item
const parent = item.closest('[fs-list-element="list"] > *') || item.parentElement;
const cityRef = parent.querySelector('[fs-list-field="location"]')?.textContent?.trim();
if (shopName && cityRef) {
bikeShops.push({ shop: shopName, city: cityRef });
}
});
// Save original options as backup
const originalOptions = Array.from(bikeShopSelect.options);
function rebuildBikeShops() {
const selectedCity = citySelect.value;
const placeholder = bikeShopSelect.querySelector('option[value=""]');
bikeShopSelect.innerHTML = '';
if (placeholder) bikeShopSelect.appendChild(placeholder);
const seen = new Set();
bikeShops.forEach(({ shop, city }) => {
if (selectedCity && selectedCity !== '' && city !== selectedCity) return;
if (seen.has(shop)) return;
seen.add(shop);
const option = document.createElement('option');
option.value = shop;
option.textContent = shop;
bikeShopSelect.appendChild(option);
});
bikeShopSelect.value = '';
bikeShopSelect.dispatchEvent(new Event('change', { bubbles: true }));
}
citySelect.addEventListener('change', rebuildBikeShops);
Hello @jesse.muiruri , many many thanks for your help. I think we’re close but the filtered bike shop items still won’t show up properly in the 2nd select field. Here’s a short loom, here’s the staging link and here’s a read-only again.
Could you please have a look and let me know what I’m getting wrong? Thank you so much.
You can use this code, it gets the correct bikeShop divs
PS: You could give the cityRef a more descriptive class or custom attribute e.g. data="city-ref"
Then replace this line of code