CMS Combo Box / Select / Load additional elements / text

Hi Finsweet team!

I was hoping to get some help implementing some additional functionality for the Combo Box.

I’m using a combination of CMS Load, Select and Combo Box to achieve what I need.

CMS Load is used to render all items in a collection of 100+ items and CMS Select is used to populate the select element of the Combo Box dropdown with the CMS items.

Alongside the dropdown label inside each option, I would like another text element below the label and an image on the left, both pulled from the relative CMS item (not static).

Like the below examples:

The additional text I would definitely like, however I won’t need the images for a while - but it would be good to have a solution ready anyway.

I’m comfortable using the Attributes API however I couldn’t work a solution yet.

Would be great to see what you guys think!

Hey @matt5! I can help write some code to add the second text line to the Select, can you please share a link?

Hey @Support-Luis! Thanks, here you go:

Read only

Live site

Thank you! Could you add the second text line to the collection list used?

I think we might get this to work natively :thinking:

All done @Support-Luis. There’s now a second text block in the collection list linked to the “Type” field.

Thank you @matt5! We will need a couple more elements to make this work.

We need another div added to the fs-combobox-element="option-template" with the attribute data-combobox-mirror="target".

We also need to add the attribute data-combobox-mirror="source" to the Type field on your collection list.

I can mirror the text content from the source to the target this way, same if we want to add the images at this point, I can mirror the src to an image on each template. The attributes for this can be data-combobox-image="source" and data-combobox-image="target".

This is how the code should look like, but I have not tested as the current setup is missing the extra elements.

<script>
  // Select all source elements
  const sourceElements = document.querySelectorAll('[data-combobox-mirror="source"]');
  const sourceImages = document.querySelectorAll('[data-combobox-image="source"]');

  // Select all target elements
  const targetImages = document.querySelectorAll('[data-combobox-image="target"]');
  const targetElements = document.querySelectorAll('[data-combobox-mirror="target"]');

  // Ensure the source and target lists have the same length
  if (
    sourceElements.length !== targetElements.length ||
    sourceImages.length !== targetImages.length
  ) {
    console.error('Source and target lists do not have the same number of elements');
  } else {
    // Loop through each source element and image, mirroring both content and image sources
    sourceElements.forEach((source, index) => {
      // Mirror text content
      const target = targetElements[index];
      target.innerHTML = source.innerHTML;

      // Mirror image source
      const sourceImg = sourceImages[index];
      const targetImg = targetImages[index];
      targetImg.src = sourceImg.src;
    });
  }
</script>

Let me know once you’ve added them so I can test and refine the code :wink:

Awesome stuff @Support-Luis, I’ve put the elements in.

I tested the code but it seems we may have to run it after all attributes have loaded, and maybe whenever the user searches using the combobox too.

I know most attributes have the renderitems function but I couldn’t get this to work with combobox. In fact, I couldn’t even get any code to run with the usual fsAttributes.push(‘combobox’) stuff.

Looking forward to any ideas you may have! :muscle:

Hey @matt5!

Could you please test this code? I wrapped it within the callback function to be sure the code is run only after the solution is done loading.

<script>
  window.fsAttributes = window.fsAttributes || [];
  window.fsAttributes.push([
    'combobox',
    (comboboxInstances) => {
      console.log('combobox Successfully loaded!');

      // Select the field to observe
      const comboboxElement = document.getElementById('searchField');

      // Select all the source elements
      const sourceElements = document.querySelectorAll('[data-combobox-mirror="source"]');
      const sourceImages = document.querySelectorAll('[data-combobox-image="source"]');

      // Callback function that will be executed when mutations are observed
      const callback = function (mutationsList, observer) {
        for (let mutation of mutationsList) {
          if (mutation.type === 'attributes' && mutation.attributeName === 'aria-expanded') {
            // Get the new value of the aria-expanded attribute
            const isExpanded = comboboxElement.getAttribute('aria-expanded');

            // Select all target elements
            const targetElements = document.querySelectorAll('[data-combobox-mirror="target"]');
            const targetImages = document.querySelectorAll('[data-combobox-image="target"]');

            // Ensure the source and target lists have the same length
            if (
              sourceElements.length !== targetElements.length ||
              sourceImages.length !== targetImages.length
            ) {
              console.error('Source and target lists do not have the same number of elements');
            } else {
              sourceElements.forEach((source, index) => {
                // Mirror text content
                const target = targetElements[index];
                target.innerHTML = source.innerHTML;

                // Mirror image source
                const sourceImg = sourceImages[index];
                const targetImg = targetImages[index];
                targetImg.src = sourceImg.src;
              });
            }
          }
        }
      };

      const observer = new MutationObserver(callback);
      const config = { attributes: true };

      // Start observing the target element
      observer.observe(comboboxElement, config);
    },
  ]);
</script>

Hi @Support-Luis - I think we’re getting there, however it stops working when you search.

I’ve published the site to the webflow.io domain so feel free to have a fiddle and see what you can come up with!

Thanks for all of your help so far.

Hey @matt5! Do you mean on the page the users are redirected after searching? Or where?

@Support-Luis, it breaks when you search in the input to filter the results of the combobox. It displays fine when you click on the dropdown toggle but as soon as you put a letter in, it filters the results as expected but without the new text or image in the option template.

Here’s a loom

Hey @matt5! Thank you for the video! Yes, this changes the logic as the the lists are not filtered the same. Let me work on a solution for this and I’ll get back to you :wink:

Ok great @Support-Luis, thanks. Just to let you know, I’ve removed the image mirroring as I dont need it now.

thanks @matt5! I have a request that would make this way more simple. Can you add an id to both lists? Since we have a list being filtered and one being static, the logic applied to the previous solution breaks.

I’ll need to create a map or array where the source and target elements have the same id, with that we can check which elements are shown on the combobox list and add their corresponding text without referencing the original list.

The id can be added to each element, a parent wrapper, as a data-combobox-id attribute or whichever you chose as long as we can keep the relationship between lists :slight_smile:

@Support-Luis, I’ve added data-combobox-id=“sublocation” to both the text to be referenced in the collection list item, and also in the target wrapper in the combobox option template. I’ve kept the other attributes there too in case you need them. Let me know if this is what you needed as I’m not quite sure.

Thank you @matt5! but no, we need a unique id I’m afraid.

Another option is to have the hidden source list hidden and filtered by CMS Filter by mirroring the input to a search field. I want to avoid this as it is a bit hacky and may introduce bugs…

@Support-Luis, im not quite sure what you mean. Like a literal element ID? Wouldnt it cause issues with the page if two elements had the same ID? Both the target and source elements have attribute data-combobox-id=“sublocation” - is this not unique?

Yes I agree, I dont want to be mirroring anything of the sort, I’ve already got enough hacks and workarounds on the site. If it cant work simply then I’ll just scrap the idea.

If you let me know exactly what you need putting and on what element, I’ll do that and we’ll see what we can do.

Again, thanks for your help!

Thank you @matt5! What I mean is not an element id but some sort of identifier for the elements in both list in the form of data-combobox-id = {{IDENTIFIER}} like how I am using the slug below.

This way, we can check which elements are being shown on the combobox list to then fetch corresponding item’s text and image form the hidden list that does not change.

I hope this makes sense :sweat_smile:

I do apologize if I am not able to 100% add this functionality but I like the idea and will suggest it to the dev team to see if we can incorporate it somehow into a new feature request :wink: