Finsweet Attributes V2 Active Filter Tags

Hey,
I just changed over from Finsweet Attibutes Legacy to V2. Does anybody know if there is any way to split the tags, with each active filter being as a seperate tag element, like it was in Legacy. (In V2 only different filter labels are split not each filter, and so also clearing each filter seperately from the Tag is not possible.) Not sure why they changed it in V2.

@1glance_Admin

There isn’t a built-in way to do this in Finsweet Attributes V2 with the default configuration, but you can achieve this behavior with some custom JavaScript.

document.addEventListener('DOMContentLoaded', function() {
  // Add custom CSS to make tag items appear as separate tags
  const style = document.createElement('style');
  style.textContent = `
    [fs-cmsfilter-element="tag"] {
      display: none !important; /* Hide original tags */
    }
    
    .fs-custom-tag {
      display: inline-flex;
      align-items: center;
      margin: 0.25rem;
      padding: 0.25rem 0.5rem;
      background-color: #f0f0f0;
      border-radius: 4px;
      font-size: 0.875rem;
    }
    
    .fs-custom-tag-remove {
      margin-left: 0.5rem;
      cursor: pointer;
      font-weight: bold;
    }
  `;
  document.head.appendChild(style);
  
  // Process tags
  function processFilterTags() {
    const tagsContainer = document.querySelector('[fs-cmsfilter-element="tags-container"]');
    if (!tagsContainer) return;
    
    // Clear any existing custom tags
    const existingCustomTags = document.querySelectorAll('.fs-custom-tag');
    existingCustomTags.forEach(tag => tag.remove());
    
    // Get all tags
    const tags = document.querySelectorAll('[fs-cmsfilter-element="tag"]');
    
    tags.forEach(tag => {
      const field = tag.getAttribute('fs-cmsfilter-field');
      const valuesStr = tag.getAttribute('fs-cmsfilter-value');
      
      if (!valuesStr) return;
      
      const values = valuesStr.split(',').map(v => v.trim());
      
      // Create custom tag for each value
      values.forEach(value => {
        const customTag = document.createElement('div');
        customTag.className = 'fs-custom-tag';
        customTag.innerHTML = `
          <span>${field}: ${value}</span>
          <span class="fs-custom-tag-remove">Ă—</span>
        `;
        
        // Add click handler to remove individual filters
        customTag.querySelector('.fs-custom-tag-remove').addEventListener('click', () => {
          // Find if there are other values for this field
          const remainingValues = values.filter(v => v !== value);
          
          if (remainingValues.length > 0) {
            // Update the original tag with remaining values
            tag.setAttribute('fs-cmsfilter-value', remainingValues.join(','));
            // Simulate click on clear filters button to apply changes
            const clearAllBtn = document.querySelector('[fs-cmsfilter-element="clear"]');
            if (clearAllBtn) {
              const event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
              });
              clearAllBtn.dispatchEvent(event);
              
              // Re-apply remaining filters
              setTimeout(() => {
                remainingValues.forEach(remainingValue => {
                  const filterBtn = document.querySelector(`[fs-cmsfilter-field="${field}"][fs-cmsfilter-value*="${remainingValue}"]`);
                  if (filterBtn) filterBtn.click();
                });
              }, 100);
            }
          } else {
            // If it's the last value, just click the original tag's remove button
            const removeBtn = tag.querySelector('[fs-cmsfilter-element="tag-remove"]');
            if (removeBtn) removeBtn.click();
          }
        });
        
        tagsContainer.appendChild(customTag);
      });
    });
  }
  
  // Watch for changes in the tags container
  setTimeout(() => {
    const tagsContainer = document.querySelector('[fs-cmsfilter-element="tags-container"]');
    if (tagsContainer) {
      const observer = new MutationObserver(processFilterTags);
      observer.observe(tagsContainer, { childList: true, subtree: true });
      
      // Initial processing
      processFilterTags();
    }
  }, 1000);
});

Thanks. Where exactly would i place this code.

Inside a Custom Code block in your Webflow project:

  • Go to your Webflow project dashboard
  • Navigate to “Project Settings” or “Site Settings”
  • Find the “Custom Code” section
  • Add the code within a <script> tag in either the “Head Code” or “Footer Code” section (Footer is usually preferred for performance reasons)

I added the code but nothing happened. Could it be that in the code where it says fs-“cmsfilter-element=tag” it should say “fs-list-element=tag” as the new attributes have changed the attribute name of the filter?

V2 documentation shows that tags now use fs-list-element="tag", tag fields use fs-list-element="tag-field", tag values use fs-list-element :antCitation[]{citations="747e12e9-344b-49a3-818d-19d35f390b12"}="tag-value", and tag remove buttons use fs-list-element="tag-remove".

document.addEventListener('DOMContentLoaded', function() {
  // Add custom CSS to make tag items appear as separate tags
  const style = document.createElement('style');
  style.textContent = `
    [fs-list-element="tag"] {
      display: none !important; /* Hide original tags */
    }
    
    .fs-custom-tag {
      display: inline-flex;
      align-items: center;
      margin: 0.25rem;
      padding: 0.25rem 0.5rem;
      background-color: #f0f0f0;
      border-radius: 4px;
      font-size: 0.875rem;
    }
    
    .fs-custom-tag-remove {
      margin-left: 0.5rem;
      cursor: pointer;
      font-weight: bold;
    }
  `;
  document.head.appendChild(style);
  
  // Process tags
  function processFilterTags() {
    const tagsContainer = document.querySelector('[fs-list-element="tags"]');
    if (!tagsContainer) return;
    
    // Clear any existing custom tags
    const existingCustomTags = document.querySelectorAll('.fs-custom-tag');
    existingCustomTags.forEach(tag => tag.remove());
    
    // Get all tags
    const tags = document.querySelectorAll('[fs-list-element="tag"]');
    
    tags.forEach(tag => {
      const field = tag.getAttribute('fs-list-field');
      const valuesStr = tag.getAttribute('fs-list-value');
      
      if (!valuesStr) return;
      
      const values = valuesStr.split(',').map(v => v.trim());
      
      // Create custom tag for each value
      values.forEach(value => {
        const customTag = document.createElement('div');
        customTag.className = 'fs-custom-tag';
        customTag.innerHTML = `
          <span>${field}: ${value}</span>
          <span class="fs-custom-tag-remove">Ă—</span>
        `;
        
        // Add click handler to remove individual filters
        customTag.querySelector('.fs-custom-tag-remove').addEventListener('click', () => {
          // Find if there are other values for this field
          const remainingValues = values.filter(v => v !== value);
          
          if (remainingValues.length > 0) {
            // Update the original tag with remaining values
            tag.setAttribute('fs-list-value', remainingValues.join(','));
            // Simulate click on clear filters button to apply changes
            const clearAllBtn = document.querySelector('[fs-list-element="clear"]');
            if (clearAllBtn) {
              const event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
              });
              clearAllBtn.dispatchEvent(event);
              
              // Re-apply remaining filters
              setTimeout(() => {
                remainingValues.forEach(remainingValue => {
                  const filterBtn = document.querySelector(`[fs-list-field="${field}"][fs-list-value*="${remainingValue}"]`);
                  if (filterBtn) filterBtn.click();
                });
              }, 100);
            }
          } else {
            // If it's the last value, just click the original tag's remove button
            const removeBtn = tag.querySelector('[fs-list-element="tag-remove"]');
            if (removeBtn) removeBtn.click();
          }
        });
        
        tagsContainer.appendChild(customTag);
      });
    });
  }
  
  // Watch for changes in the tags container
  setTimeout(() => {
    const tagsContainer = document.querySelector('[fs-list-element="tags"]');
    if (tagsContainer) {
      const observer = new MutationObserver(processFilterTags);
      observer.observe(tagsContainer, { childList: true, subtree: true });
      
      // Initial processing
      processFilterTags();
    }
  }, 1000);
});
1 Like

Thanks for the updated code. Problem is that i added it to the footer and now no tags are showing at all? Any idea whats going wrong?

  • Remove the old code from your footer

  • Add the new code above to your site’s footer (before </body> tag)

  • Make sure you have the Finsweet V2 script properly loaded in your <head>

  • Check tag structure: Ensure your tag template has the correct elements:

  • Main tag: fs-list-element="tag"

  • Field name: fs-list-element="tag-field"

  • Value: fs-list-element="tag-value"

  • Remove button: fs-list-element="tag-remove"

document.addEventListener('DOMContentLoaded', function() {
  // Wait for Finsweet to initialize
  function waitForFinsweet() {
    return new Promise((resolve) => {
      const checkInterval = setInterval(() => {
        // Check if tags container exists (means Finsweet has loaded)
        const tagsContainer = document.querySelector('[fs-list-element="tags"]');
        if (tagsContainer) {
          clearInterval(checkInterval);
          resolve();
        }
      }, 100);
    });
  }

  // Add custom CSS
  const style = document.createElement('style');
  style.textContent = `
    [fs-list-element="tag"] {
      display: none !important; /* Hide original tags */
    }
    
    .fs-custom-tag {
      display: inline-flex;
      align-items: center;
      margin: 0.25rem;
      padding: 0.25rem 0.75rem;
      background-color: #e5e7eb;
      border: 1px solid #d1d5db;
      border-radius: 6px;
      font-size: 0.875rem;
      font-weight: 500;
      color: #374151;
      transition: all 0.2s ease;
    }
    
    .fs-custom-tag:hover {
      background-color: #f3f4f6;
    }
    
    .fs-custom-tag-remove {
      margin-left: 0.5rem;
      padding: 0.125rem 0.25rem;
      cursor: pointer;
      font-weight: bold;
      color: #6b7280;
      border-radius: 3px;
      transition: all 0.2s ease;
    }
    
    .fs-custom-tag-remove:hover {
      background-color: #ef4444;
      color: white;
    }
    
    .fs-custom-tags-container {
      display: flex;
      flex-wrap: wrap;
      gap: 0.25rem;
      margin-top: 0.5rem;
    }
  `;
  document.head.appendChild(style);

  // Main processing function
  async function initializeCustomTags() {
    await waitForFinsweet();
    
    const tagsContainer = document.querySelector('[fs-list-element="tags"]');
    if (!tagsContainer) {
      console.log('Tags container not found');
      return;
    }

    // Create container for custom tags
    let customTagsContainer = document.querySelector('.fs-custom-tags-container');
    if (!customTagsContainer) {
      customTagsContainer = document.createElement('div');
      customTagsContainer.className = 'fs-custom-tags-container';
      tagsContainer.parentNode.insertBefore(customTagsContainer, tagsContainer.nextSibling);
    }

    function processFilterTags() {
      // Clear existing custom tags
      customTagsContainer.innerHTML = '';
      
      // Get all active filter tags
      const tags = document.querySelectorAll('[fs-list-element="tag"]');
      
      tags.forEach(tag => {
        // Get field name and values
        const fieldElement = tag.querySelector('[fs-list-element="tag-field"]');
        const valueElement = tag.querySelector('[fs-list-element="tag-value"]');
        
        if (!fieldElement || !valueElement) return;
        
        const fieldName = fieldElement.textContent.trim();
        const valuesText = valueElement.textContent.trim();
        
        // Split multiple values (in case there are comma-separated values)
        const values = valuesText.split(',').map(v => v.trim()).filter(v => v);
        
        // Create individual tags for each value
        values.forEach(value => {
          const customTag = document.createElement('div');
          customTag.className = 'fs-custom-tag';
          customTag.innerHTML = `
            <span>${fieldName}: ${value}</span>
            <span class="fs-custom-tag-remove" title="Remove filter">Ă—</span>
          `;
          
          // Add click handler for remove button
          const removeBtn = customTag.querySelector('.fs-custom-tag-remove');
          removeBtn.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            
            // Find the corresponding remove button in the original tag
            const originalRemoveBtn = tag.querySelector('[fs-list-element="tag-remove"]');
            if (originalRemoveBtn) {
              originalRemoveBtn.click();
            }
          });
          
          customTagsContainer.appendChild(customTag);
        });
      });
    }

    // Initial processing
    processFilterTags();

    // Set up observer to watch for changes
    const observer = new MutationObserver((mutations) => {
      let shouldUpdate = false;
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          // Check if tags were added or removed
          const addedNodes = Array.from(mutation.addedNodes);
          const removedNodes = Array.from(mutation.removedNodes);
          
          if (addedNodes.some(node => node.nodeType === 1 && node.hasAttribute && node.hasAttribute('fs-list-element')) ||
              removedNodes.some(node => node.nodeType === 1 && node.hasAttribute && node.hasAttribute('fs-list-element'))) {
            shouldUpdate = true;
          }
        } else if (mutation.type === 'attributes' && mutation.attributeName && mutation.attributeName.startsWith('fs-list-')) {
          shouldUpdate = true;
        }
      });
      
      if (shouldUpdate) {
        // Small delay to ensure DOM is fully updated
        setTimeout(processFilterTags, 50);
      }
    });

    // Observe the tags container and its parent for changes
    observer.observe(tagsContainer, { 
      childList: true, 
      subtree: true, 
      attributes: true,
      attributeFilter: ['fs-list-element', 'fs-list-field', 'fs-list-value']
    });

    // Also observe the parent container in case tags are added/removed at a higher level
    if (tagsContainer.parentNode) {
      observer.observe(tagsContainer.parentNode, { 
        childList: true, 
        subtree: true 
      });
    }

    console.log('Custom filter tags initialized successfully');
  }

  // Initialize when DOM is ready
  initializeCustomTags().catch(console.error);
});