Forms: How to add search for dropdowns with long option lists

Want to make searching the long lists of the dropdown fields in your Form widget simpler? We’ve got a solution!

Add this code to the Custom JS field on the Settings tab of your widget’s settings and the search will appear in dropdowns with more than 12 options:

const listenerBlock = (selector, callback) => {
  const observedNodes = new Set();

  const checkAndCallback = (node) => {
    if (!observedNodes.has(node)) {
      observedNodes.add(node);
      callback(node);
    }
  };

  document.querySelectorAll(selector).forEach(checkAndCallback);

  const mutationObserver = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.matches(selector)) {
            checkAndCallback(node);
          } else {
            node.querySelectorAll(selector).forEach(checkAndCallback);
          }
        }
      }
    }
  });

  mutationObserver.observe(document.body, {
    childList: true,
    subtree: true
  });
};

const createInput = (placeholder) => {
  const input = document.createElement('input');
  input.type = 'text';
  input.placeholder = placeholder;

  input.style.cssText = `
    width: calc(100% - 24px) !important;
    box-sizing: border-box !important;
    padding: 10px 29px 10px 15px !important;
    margin: 0px 12px !important;
    border: 1px solid rgba(17, 17, 17, 0.6) !important;
    border-radius: 4px !important;
    position: fixed  !important;
    top: 10px !important;
    left: 0 !important;
    background: inherit !important;
    z-index: 2 !important;
	`;

  return input;
};

listenerBlock('[class*="FormLayout__Container-sc"]', (formContainer) => {
  const attachFilter = (dropdown) => {
    if (dropdown.querySelector('input[data-filter="true"]')) {
      return;
    }

    const mutationObserver = new MutationObserver((mutations) => {
      for (const {
          addedNodes
        }
        of mutations) {
        for (const container of addedNodes) {
          if (container.nodeType !== Node.ELEMENT_NODE) {
            continue;
          }

          const dropdownContainer = container.querySelector(
            '[class*="Dropdown__DropdownContainer-sc"]'
          );
          if (!dropdownContainer) {
            continue;
          }

          const withFilters = dropdownContainer.childNodes.length > 12;
          if (!withFilters) {
            continue;
          }

          const filter = createInput('Search...');
          filter.dataset.filter = 'true';
          dropdownContainer.prepend(filter);

          dropdownContainer.style.position = 'relative';
          dropdownContainer.style.paddingTop = '60px';

          filter.addEventListener('input', (e) => {
            const value = e.target.value.trim().toLowerCase();
            dropdownContainer.childNodes.forEach((node) => {
              if (node.nodeName === 'INPUT') {
                return;
              }

              node.style.display = !value || node.textContent.toLowerCase().includes(value) ?
                'flex' :
                'none';
            });
          });
        }
      }
    });

    mutationObserver.observe(dropdown, {
      childList: true
    });
  };

  formContainer
    .querySelectorAll('[class*="dropdown__Container-sc"]')
    .forEach(attachFilter);

  const observer = new MutationObserver(() => {
    formContainer
      .querySelectorAll('[class*="dropdown__Container-sc"]')
      .forEach(attachFilter);
  });

  observer.observe(formContainer, {
    childList: true,
    subtree: true
  });
});

Note: Custom JS doesn’t function in the preview mode, so you can check the result right on your website.


Guys, was this solution helpful? Let us know in the comments :slightly_smiling_face: