Komponenten
Schablonen
Attribute
Integrationen
Standort-Tester
Benutzerdefinierter Code

Mitglieder-Skripte

Eine attributbasierte Lösung zum Hinzufügen von Funktionen zu Ihrer Webflow-Site.
Kopieren Sie einfach etwas Code, fügen Sie einige Attribute hinzu, und schon sind Sie fertig.

Vielen Dank! Ihr Beitrag ist eingegangen!
Huch! Beim Absenden des Formulars ist etwas schief gelaufen.
Benötigen Sie Hilfe mit MemberScripts?

Alle Memberstack-Kunden können im 2.0 Slack um Unterstützung bitten. Bitte beachten Sie, dass dies keine offiziellen Funktionen sind und der Support nicht garantiert werden kann.

UX

#Nr. 111 - Automatisch rotierende Registerkarten

Der einfachste Weg, um Ihre Tabs mit einem Timer automatisch rotieren zu lassen.


<!-- 💙 MEMBERSCRIPT #111 v0.1 💙 - AUTO-ROTATING TABS -->
<script>
  // Function to rotate tabs
  function initializeTabRotator() {
    // Find all tab containers with the ms-code-rotate-tabs attribute
    const tabContainers = document.querySelectorAll('[ms-code-rotate-tabs]');

    tabContainers.forEach(container => {
      const interval = parseInt(container.getAttribute('ms-code-rotate-tabs'), 10);
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const tabContent = container.closest('.w-tabs').querySelector('.w-tab-content');
      const tabPanes = tabContent.querySelectorAll('.w-tab-pane');
      let currentIndex = Array.from(tabLinks).findIndex(link => link.classList.contains('w--current'));
      let rotationTimer;

      // ANIMATION CONFIGURATION
      // Modify these values to adjust the animation behavior
      const FADE_OUT_DURATION = 300; // Duration for fading out the current tab (in milliseconds)
      const FADE_IN_DURATION = 100;  // Duration for fading in the new tab (in milliseconds)
      const EASING_FUNCTION = 'ease'; // Choose from: 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'
      // or use a cubic-bezier function like 'cubic-bezier(0.1, 0.7, 1.0, 0.1)'
      // Additional easing options (uncomment to use):
      // const EASING_FUNCTION = 'ease-in-quad';
      // const EASING_FUNCTION = 'ease-out-quad';
      // const EASING_FUNCTION = 'ease-in-out-quad';
      // const EASING_FUNCTION = 'ease-in-cubic';
      // const EASING_FUNCTION = 'ease-out-cubic';
      // const EASING_FUNCTION = 'ease-in-out-cubic';
      // const EASING_FUNCTION = 'ease-in-quart';
      // const EASING_FUNCTION = 'ease-out-quart';
      // const EASING_FUNCTION = 'ease-in-out-quart';
      // const EASING_FUNCTION = 'ease-in-quint';
      // const EASING_FUNCTION = 'ease-out-quint';
      // const EASING_FUNCTION = 'ease-in-out-quint';
      // const EASING_FUNCTION = 'ease-in-sine';
      // const EASING_FUNCTION = 'ease-out-sine';
      // const EASING_FUNCTION = 'ease-in-out-sine';
      // const EASING_FUNCTION = 'ease-in-expo';
      // const EASING_FUNCTION = 'ease-out-expo';
      // const EASING_FUNCTION = 'ease-in-out-expo';
      // const EASING_FUNCTION = 'ease-in-circ';
      // const EASING_FUNCTION = 'ease-out-circ';
      // const EASING_FUNCTION = 'ease-in-out-circ';
      // const EASING_FUNCTION = 'ease-in-back';
      // const EASING_FUNCTION = 'ease-out-back';
      // const EASING_FUNCTION = 'ease-in-out-back';
      // END OF ANIMATION CONFIGURATION

      function switchToTab(index) {
        // Fade out current tab
        tabPanes[currentIndex].style.transition = `opacity ${FADE_OUT_DURATION}ms ${EASING_FUNCTION}`;
        tabPanes[currentIndex].style.opacity = '0';

        setTimeout(() => {
          // Remove active classes and update ARIA attributes for current tab and pane
          tabLinks[currentIndex].classList.remove('w--current');
          tabLinks[currentIndex].setAttribute('aria-selected', 'false');
          tabLinks[currentIndex].setAttribute('tabindex', '-1');
          tabPanes[currentIndex].classList.remove('w--tab-active');

          // Update current index
          currentIndex = index;

          // Add active classes and update ARIA attributes for new current tab and pane
          tabLinks[currentIndex].classList.add('w--current');
          tabLinks[currentIndex].setAttribute('aria-selected', 'true');
          tabLinks[currentIndex].setAttribute('tabindex', '0');
          tabPanes[currentIndex].classList.add('w--tab-active');

          // Fade in new tab
          tabPanes[currentIndex].style.transition = `opacity ${FADE_IN_DURATION}ms ${EASING_FUNCTION}`;
          tabPanes[currentIndex].style.opacity = '1';

          // Update the data-current attribute on the parent w-tabs element
          const wTabsElement = container.closest('.w-tabs');
          if (wTabsElement) {
            wTabsElement.setAttribute('data-current', tabLinks[currentIndex].getAttribute('data-w-tab'));
          }
        }, FADE_OUT_DURATION);
      }

      function rotateToNextTab() {
        const nextIndex = (currentIndex + 1) % tabLinks.length;
        switchToTab(nextIndex);
      }

      function startRotation() {
        clearInterval(rotationTimer);
        rotationTimer = setInterval(rotateToNextTab, interval);
      }

      // Add click event listeners to tab links
      tabLinks.forEach((link, index) => {
        link.addEventListener('click', (e) => {
          e.preventDefault();
          switchToTab(index);
          startRotation(); // Restart rotation from this tab
        });
      });

      // Start the initial rotation
      startRotation();
    });
  }

  // Run the function when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', initializeTabRotator);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #111 v0.1 💙 - AUTO-ROTATING TABS -->
<script>
  // Function to rotate tabs
  function initializeTabRotator() {
    // Find all tab containers with the ms-code-rotate-tabs attribute
    const tabContainers = document.querySelectorAll('[ms-code-rotate-tabs]');

    tabContainers.forEach(container => {
      const interval = parseInt(container.getAttribute('ms-code-rotate-tabs'), 10);
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const tabContent = container.closest('.w-tabs').querySelector('.w-tab-content');
      const tabPanes = tabContent.querySelectorAll('.w-tab-pane');
      let currentIndex = Array.from(tabLinks).findIndex(link => link.classList.contains('w--current'));
      let rotationTimer;

      // ANIMATION CONFIGURATION
      // Modify these values to adjust the animation behavior
      const FADE_OUT_DURATION = 300; // Duration for fading out the current tab (in milliseconds)
      const FADE_IN_DURATION = 100;  // Duration for fading in the new tab (in milliseconds)
      const EASING_FUNCTION = 'ease'; // Choose from: 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'
      // or use a cubic-bezier function like 'cubic-bezier(0.1, 0.7, 1.0, 0.1)'
      // Additional easing options (uncomment to use):
      // const EASING_FUNCTION = 'ease-in-quad';
      // const EASING_FUNCTION = 'ease-out-quad';
      // const EASING_FUNCTION = 'ease-in-out-quad';
      // const EASING_FUNCTION = 'ease-in-cubic';
      // const EASING_FUNCTION = 'ease-out-cubic';
      // const EASING_FUNCTION = 'ease-in-out-cubic';
      // const EASING_FUNCTION = 'ease-in-quart';
      // const EASING_FUNCTION = 'ease-out-quart';
      // const EASING_FUNCTION = 'ease-in-out-quart';
      // const EASING_FUNCTION = 'ease-in-quint';
      // const EASING_FUNCTION = 'ease-out-quint';
      // const EASING_FUNCTION = 'ease-in-out-quint';
      // const EASING_FUNCTION = 'ease-in-sine';
      // const EASING_FUNCTION = 'ease-out-sine';
      // const EASING_FUNCTION = 'ease-in-out-sine';
      // const EASING_FUNCTION = 'ease-in-expo';
      // const EASING_FUNCTION = 'ease-out-expo';
      // const EASING_FUNCTION = 'ease-in-out-expo';
      // const EASING_FUNCTION = 'ease-in-circ';
      // const EASING_FUNCTION = 'ease-out-circ';
      // const EASING_FUNCTION = 'ease-in-out-circ';
      // const EASING_FUNCTION = 'ease-in-back';
      // const EASING_FUNCTION = 'ease-out-back';
      // const EASING_FUNCTION = 'ease-in-out-back';
      // END OF ANIMATION CONFIGURATION

      function switchToTab(index) {
        // Fade out current tab
        tabPanes[currentIndex].style.transition = `opacity ${FADE_OUT_DURATION}ms ${EASING_FUNCTION}`;
        tabPanes[currentIndex].style.opacity = '0';

        setTimeout(() => {
          // Remove active classes and update ARIA attributes for current tab and pane
          tabLinks[currentIndex].classList.remove('w--current');
          tabLinks[currentIndex].setAttribute('aria-selected', 'false');
          tabLinks[currentIndex].setAttribute('tabindex', '-1');
          tabPanes[currentIndex].classList.remove('w--tab-active');

          // Update current index
          currentIndex = index;

          // Add active classes and update ARIA attributes for new current tab and pane
          tabLinks[currentIndex].classList.add('w--current');
          tabLinks[currentIndex].setAttribute('aria-selected', 'true');
          tabLinks[currentIndex].setAttribute('tabindex', '0');
          tabPanes[currentIndex].classList.add('w--tab-active');

          // Fade in new tab
          tabPanes[currentIndex].style.transition = `opacity ${FADE_IN_DURATION}ms ${EASING_FUNCTION}`;
          tabPanes[currentIndex].style.opacity = '1';

          // Update the data-current attribute on the parent w-tabs element
          const wTabsElement = container.closest('.w-tabs');
          if (wTabsElement) {
            wTabsElement.setAttribute('data-current', tabLinks[currentIndex].getAttribute('data-w-tab'));
          }
        }, FADE_OUT_DURATION);
      }

      function rotateToNextTab() {
        const nextIndex = (currentIndex + 1) % tabLinks.length;
        switchToTab(nextIndex);
      }

      function startRotation() {
        clearInterval(rotationTimer);
        rotationTimer = setInterval(rotateToNextTab, interval);
      }

      // Add click event listeners to tab links
      tabLinks.forEach((link, index) => {
        link.addEventListener('click', (e) => {
          e.preventDefault();
          switchToTab(index);
          startRotation(); // Restart rotation from this tab
        });
      });

      // Start the initial rotation
      startRotation();
    });
  }

  // Run the function when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', initializeTabRotator);
</script>
Ansicht Memberscript
UX

#Nr. 110 - QuickInfos für Webflow

Fügen Sie Ihrer Webflow-Website mit Attributen ganz einfach Tippy.js-Tooltips hinzu.


<!-- 💙 MEMBERSCRIPT #110 v0.1 💙 - TOOLTIPS FOR WEBFLOW -->
<script>
  // Function to load Tippy.js, its CSS, and additional theme/animation CSS
  function loadTippy(callback) {
    // Load Tippy.js script
    const script = document.createElement('script');
    script.src = 'https://unpkg.com/@popperjs/core@2';
    script.onload = function() {
      const tippyScript = document.createElement('script');
      tippyScript.src = 'https://unpkg.com/tippy.js@6';
      tippyScript.onload = function() {
        // Load Tippy.js CSS
        const cssFiles = [
          'https://unpkg.com/tippy.js@6/dist/tippy.css',
          'https://unpkg.com/tippy.js@6/themes/light.css',
          'https://unpkg.com/tippy.js@6/themes/light-border.css',
          'https://unpkg.com/tippy.js@6/animations/shift-away.css',
          'https://unpkg.com/tippy.js@6/animations/shift-toward.css',
          'https://unpkg.com/tippy.js@6/animations/scale.css',
          'https://unpkg.com/tippy.js@6/animations/perspective.css'
        ];
        
        let loadedCount = 0;
        cssFiles.forEach(file => {
          const link = document.createElement('link');
          link.href = file;
          link.rel = 'stylesheet';
          link.onload = function() {
            loadedCount++;
            if (loadedCount === cssFiles.length) {
              // Call the callback function when everything is loaded
              callback();
            }
          };
          document.head.appendChild(link);
        });
      };
      document.head.appendChild(tippyScript);
    };
    document.head.appendChild(script);
  }

  // Function to initialize Tippy tooltips
  function initializeTippyTooltips() {
    // Select all elements with any ms-code-tooltip-* attribute
    const elements = document.querySelectorAll('[ms-code-tooltip-top], [ms-code-tooltip-bottom], [ms-code-tooltip-left], [ms-code-tooltip-right], [ms-code-tooltip-content]');

    elements.forEach(element => {
      const tippyOptions = {};

      // Content and Placement
      if (element.hasAttribute('ms-code-tooltip-top')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-top');
        tippyOptions.placement = 'top';
      } else if (element.hasAttribute('ms-code-tooltip-bottom')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-bottom');
        tippyOptions.placement = 'bottom';
      } else if (element.hasAttribute('ms-code-tooltip-left')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-left');
        tippyOptions.placement = 'left';
      } else if (element.hasAttribute('ms-code-tooltip-right')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-right');
        tippyOptions.placement = 'right';
      } else if (element.hasAttribute('ms-code-tooltip-content')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-content');
      }

      if (element.hasAttribute('ms-code-tooltip-placement')) {
        tippyOptions.placement = element.getAttribute('ms-code-tooltip-placement');
      }

      // Theme
      if (element.hasAttribute('ms-code-tooltip-theme')) {
        tippyOptions.theme = element.getAttribute('ms-code-tooltip-theme');
      }

      // Animation
      if (element.hasAttribute('ms-code-tooltip-animation')) {
        tippyOptions.animation = element.getAttribute('ms-code-tooltip-animation');
      }

      // Max Width
      if (element.hasAttribute('ms-code-tooltip-maxwidth')) {
        tippyOptions.maxWidth = parseInt(element.getAttribute('ms-code-tooltip-maxwidth'));
      }

      // Delay
      if (element.hasAttribute('ms-code-tooltip-delay')) {
        tippyOptions.delay = JSON.parse(element.getAttribute('ms-code-tooltip-delay'));
      }

      // Duration
      if (element.hasAttribute('ms-code-tooltip-duration')) {
        tippyOptions.duration = JSON.parse(element.getAttribute('ms-code-tooltip-duration'));
      }

      // Interactive
      if (element.hasAttribute('ms-code-tooltip-interactive')) {
        tippyOptions.interactive = element.getAttribute('ms-code-tooltip-interactive') === 'true';
      }

      // Arrow
      if (element.hasAttribute('ms-code-tooltip-arrow')) {
        tippyOptions.arrow = element.getAttribute('ms-code-tooltip-arrow') === 'true';
      }

      // Trigger
      if (element.hasAttribute('ms-code-tooltip-trigger')) {
        tippyOptions.trigger = element.getAttribute('ms-code-tooltip-trigger');
      }

      // Hide On Click
      if (element.hasAttribute('ms-code-tooltip-hideOnClick')) {
        tippyOptions.hideOnClick = element.getAttribute('ms-code-tooltip-hideOnClick') === 'true';
      }

      // Follow Cursor
      if (element.hasAttribute('ms-code-tooltip-followCursor')) {
        tippyOptions.followCursor = element.getAttribute('ms-code-tooltip-followCursor');
      }

      // Offset
      if (element.hasAttribute('ms-code-tooltip-offset')) {
        tippyOptions.offset = JSON.parse(element.getAttribute('ms-code-tooltip-offset'));
      }

      // Z-Index
      if (element.hasAttribute('ms-code-tooltip-zIndex')) {
        tippyOptions.zIndex = parseInt(element.getAttribute('ms-code-tooltip-zIndex'));
      }

      // Allow HTML
      if (element.hasAttribute('ms-code-tooltip-allowHTML')) {
        tippyOptions.allowHTML = element.getAttribute('ms-code-tooltip-allowHTML') === 'true';
      }

      // Touch
      if (element.hasAttribute('ms-code-tooltip-touch')) {
        const touchValue = element.getAttribute('ms-code-tooltip-touch');
        tippyOptions.touch = touchValue === 'true' || touchValue === 'false' ? (touchValue === 'true') : JSON.parse(touchValue);
      }

      // Initialize Tippy instance
      tippy(element, tippyOptions);
    });
  }

  // Wait for the DOM to be fully loaded, then load Tippy and initialize tooltips
  document.addEventListener('DOMContentLoaded', function() {
    loadTippy(initializeTippyTooltips);
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #110 v0.1 💙 - TOOLTIPS FOR WEBFLOW -->
<script>
  // Function to load Tippy.js, its CSS, and additional theme/animation CSS
  function loadTippy(callback) {
    // Load Tippy.js script
    const script = document.createElement('script');
    script.src = 'https://unpkg.com/@popperjs/core@2';
    script.onload = function() {
      const tippyScript = document.createElement('script');
      tippyScript.src = 'https://unpkg.com/tippy.js@6';
      tippyScript.onload = function() {
        // Load Tippy.js CSS
        const cssFiles = [
          'https://unpkg.com/tippy.js@6/dist/tippy.css',
          'https://unpkg.com/tippy.js@6/themes/light.css',
          'https://unpkg.com/tippy.js@6/themes/light-border.css',
          'https://unpkg.com/tippy.js@6/animations/shift-away.css',
          'https://unpkg.com/tippy.js@6/animations/shift-toward.css',
          'https://unpkg.com/tippy.js@6/animations/scale.css',
          'https://unpkg.com/tippy.js@6/animations/perspective.css'
        ];
        
        let loadedCount = 0;
        cssFiles.forEach(file => {
          const link = document.createElement('link');
          link.href = file;
          link.rel = 'stylesheet';
          link.onload = function() {
            loadedCount++;
            if (loadedCount === cssFiles.length) {
              // Call the callback function when everything is loaded
              callback();
            }
          };
          document.head.appendChild(link);
        });
      };
      document.head.appendChild(tippyScript);
    };
    document.head.appendChild(script);
  }

  // Function to initialize Tippy tooltips
  function initializeTippyTooltips() {
    // Select all elements with any ms-code-tooltip-* attribute
    const elements = document.querySelectorAll('[ms-code-tooltip-top], [ms-code-tooltip-bottom], [ms-code-tooltip-left], [ms-code-tooltip-right], [ms-code-tooltip-content]');

    elements.forEach(element => {
      const tippyOptions = {};

      // Content and Placement
      if (element.hasAttribute('ms-code-tooltip-top')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-top');
        tippyOptions.placement = 'top';
      } else if (element.hasAttribute('ms-code-tooltip-bottom')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-bottom');
        tippyOptions.placement = 'bottom';
      } else if (element.hasAttribute('ms-code-tooltip-left')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-left');
        tippyOptions.placement = 'left';
      } else if (element.hasAttribute('ms-code-tooltip-right')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-right');
        tippyOptions.placement = 'right';
      } else if (element.hasAttribute('ms-code-tooltip-content')) {
        tippyOptions.content = element.getAttribute('ms-code-tooltip-content');
      }

      if (element.hasAttribute('ms-code-tooltip-placement')) {
        tippyOptions.placement = element.getAttribute('ms-code-tooltip-placement');
      }

      // Theme
      if (element.hasAttribute('ms-code-tooltip-theme')) {
        tippyOptions.theme = element.getAttribute('ms-code-tooltip-theme');
      }

      // Animation
      if (element.hasAttribute('ms-code-tooltip-animation')) {
        tippyOptions.animation = element.getAttribute('ms-code-tooltip-animation');
      }

      // Max Width
      if (element.hasAttribute('ms-code-tooltip-maxwidth')) {
        tippyOptions.maxWidth = parseInt(element.getAttribute('ms-code-tooltip-maxwidth'));
      }

      // Delay
      if (element.hasAttribute('ms-code-tooltip-delay')) {
        tippyOptions.delay = JSON.parse(element.getAttribute('ms-code-tooltip-delay'));
      }

      // Duration
      if (element.hasAttribute('ms-code-tooltip-duration')) {
        tippyOptions.duration = JSON.parse(element.getAttribute('ms-code-tooltip-duration'));
      }

      // Interactive
      if (element.hasAttribute('ms-code-tooltip-interactive')) {
        tippyOptions.interactive = element.getAttribute('ms-code-tooltip-interactive') === 'true';
      }

      // Arrow
      if (element.hasAttribute('ms-code-tooltip-arrow')) {
        tippyOptions.arrow = element.getAttribute('ms-code-tooltip-arrow') === 'true';
      }

      // Trigger
      if (element.hasAttribute('ms-code-tooltip-trigger')) {
        tippyOptions.trigger = element.getAttribute('ms-code-tooltip-trigger');
      }

      // Hide On Click
      if (element.hasAttribute('ms-code-tooltip-hideOnClick')) {
        tippyOptions.hideOnClick = element.getAttribute('ms-code-tooltip-hideOnClick') === 'true';
      }

      // Follow Cursor
      if (element.hasAttribute('ms-code-tooltip-followCursor')) {
        tippyOptions.followCursor = element.getAttribute('ms-code-tooltip-followCursor');
      }

      // Offset
      if (element.hasAttribute('ms-code-tooltip-offset')) {
        tippyOptions.offset = JSON.parse(element.getAttribute('ms-code-tooltip-offset'));
      }

      // Z-Index
      if (element.hasAttribute('ms-code-tooltip-zIndex')) {
        tippyOptions.zIndex = parseInt(element.getAttribute('ms-code-tooltip-zIndex'));
      }

      // Allow HTML
      if (element.hasAttribute('ms-code-tooltip-allowHTML')) {
        tippyOptions.allowHTML = element.getAttribute('ms-code-tooltip-allowHTML') === 'true';
      }

      // Touch
      if (element.hasAttribute('ms-code-tooltip-touch')) {
        const touchValue = element.getAttribute('ms-code-tooltip-touch');
        tippyOptions.touch = touchValue === 'true' || touchValue === 'false' ? (touchValue === 'true') : JSON.parse(touchValue);
      }

      // Initialize Tippy instance
      tippy(element, tippyOptions);
    });
  }

  // Wait for the DOM to be fully loaded, then load Tippy and initialize tooltips
  document.addEventListener('DOMContentLoaded', function() {
    loadTippy(initializeTippyTooltips);
  });
</script>
Ansicht Memberscript
Benutzerdefinierte Felder
UX

#109 - Benutzerdefinierte Multi Selects

Individuell gestaltete Mehrfachauswahlen mit Suche, Tastaturauswahl und mehr.


<!-- 💙 MEMBERSCRIPT #109 v0.1 💙 - CUSTOM MULTI SELECT -->
<script>
  $(document).ready(function() {
    $('[ms-code-select-wrapper]').each(function() {
      const $wrapper = $(this);
      const isMulti = $wrapper.attr('ms-code-select-wrapper') === 'multi';
      const $input = $wrapper.find('[ms-code-select="input"]');
      const $list = $wrapper.find('[ms-code-select="list"]');
      const $selectedWrapper = $wrapper.find('[ms-code-select="selected-wrapper"]');
      const $emptyState = $wrapper.find('[ms-code-select="empty-state"]');
      const options = $input.attr('ms-code-select-options').split(',').map(opt => opt.trim());

      let selectedOptions = [];
      let highlightedIndex = -1;

      const $templateSelectedTag = $selectedWrapper.find('[ms-code-select="tag"]');
      const templateSelectedTagHTML = $templateSelectedTag.prop('outerHTML');
      $templateSelectedTag.remove();

      const $templateNewTag = $list.find('[ms-code-select="tag-name-new"]');
      const templateNewTagHTML = $templateNewTag.prop('outerHTML');
      $templateNewTag.remove();

      function createSelectedTag(value) {
        const $newTag = $(templateSelectedTagHTML);
        $newTag.find('[ms-code-select="tag-name-selected"]').text(value);
        $newTag.find('[ms-code-select="tag-close"]').on('click', function(e) {
          e.stopPropagation();
          removeTag(value);
        });
        return $newTag;
      }

      function addTag(value) {
        if (!selectedOptions.includes(value) && options.includes(value)) {
          selectedOptions.push(value);
          $selectedWrapper.append(createSelectedTag(value));
          updateInput();
          filterOptions();
        }
      }

      function removeTag(value) {
        selectedOptions = selectedOptions.filter(option => option !== value);
        $selectedWrapper.find(`[ms-code-select="tag-name-selected"]:contains("${value}")`).closest('[ms-code-select="tag"]').remove();
        updateInput();
        if (isMulti && selectedOptions.length > 0) {
          $input.val($input.val() + ', ');
        }
        filterOptions();
      }

      function updateInput() {
        $input.val(selectedOptions.join(', '));
      }

      function toggleList(show) {
        $list.toggle(show);
      }

      function createOptionElement(value) {
        const $option = $(templateNewTagHTML);
        $option.text(value);
        $option.on('click', function() {
          selectOption(value);
        });
        return $option;
      }

      function selectOption(value) {
        if (isMulti) {
          addTag(value);
          $input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
          $input.focus();
        } else {
          selectedOptions = [value];
          $selectedWrapper.empty().append(createSelectedTag(value));
          updateInput();
          toggleList(false);
        }
        filterOptions();
      }

      function filterOptions() {
        const inputValue = $input.val();
        const searchTerm = isMulti ? inputValue.split(',').pop().trim() : inputValue.trim();
        let visibleOptionsCount = 0;

        $list.find('[ms-code-select="tag-name-new"]').each(function() {
          const $option = $(this);
          const optionText = $option.text().toLowerCase();
          const matches = optionText.includes(searchTerm.toLowerCase());
          const isSelected = selectedOptions.includes($option.text());
          $option.toggle(matches && !isSelected);
          if (matches && !isSelected) visibleOptionsCount++;
        });

        $emptyState.toggle(visibleOptionsCount === 0 && searchTerm !== '');
        highlightedIndex = -1;
        updateHighlight();
      }

      function cleanInput() {
        const inputValues = $input.val().split(',').map(v => v.trim()).filter(v => v);
        const validValues = inputValues.filter(v => options.includes(v));
        selectedOptions = validValues;
        $selectedWrapper.empty();
        selectedOptions.forEach(value => $selectedWrapper.append(createSelectedTag(value)));
        updateInput();
        filterOptions();
      }

      function handleInputChange() {
        const inputValue = $input.val();
        const inputValues = inputValue.split(',').map(v => v.trim());
        const lastValue = inputValues[inputValues.length - 1];

        if (inputValue.endsWith(',') || inputValue.endsWith(', ')) {
          inputValues.pop();
          const newValidValues = inputValues.filter(v => options.includes(v) && !selectedOptions.includes(v));
          newValidValues.forEach(addTag);
          $input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
        } else if (options.includes(lastValue) && !selectedOptions.includes(lastValue)) {
          addTag(lastValue);
          $input.val(selectedOptions.join(', ') + ', ');
        }

        filterOptions();
      }

      function initializeWithValue() {
        const initialValue = $input.val();
        if (initialValue) {
          const initialValues = initialValue.split(',').map(v => v.trim());
          initialValues.forEach(value => {
            if (options.includes(value)) {
              addTag(value);
            }
          });
          updateInput();
          filterOptions();
        }
      }

      function updateHighlight() {
        $list.find('[ms-code-select="tag-name-new"]').removeClass('highlighted').css('background-color', '');
        if (highlightedIndex >= 0) {
          $list.find('[ms-code-select="tag-name-new"]:visible').eq(highlightedIndex)
            .addClass('highlighted')
            .css('background-color', '#e0e0e0');
        }
      }

      function handleKeyDown(e) {
        const visibleOptions = $list.find('[ms-code-select="tag-name-new"]:visible');
        const optionCount = visibleOptions.length;

        switch (e.key) {
          case 'ArrowDown':
            e.preventDefault();
            highlightedIndex = (highlightedIndex + 1) % optionCount;
            updateHighlight();
            break;
          case 'ArrowUp':
            e.preventDefault();
            highlightedIndex = (highlightedIndex - 1 + optionCount) % optionCount;
            updateHighlight();
            break;
          case 'Enter':
            e.preventDefault();
            if (highlightedIndex >= 0) {
              const selectedValue = visibleOptions.eq(highlightedIndex).text();
              selectOption(selectedValue);
            }
            break;
        }
      }

      $.each(options, function(i, option) {
        $list.append(createOptionElement(option));
      });

      $input.on('focus', function() {
        toggleList(true);
        if (isMulti) {
          const currentVal = $input.val().trim();
          if (currentVal !== '' && !currentVal.endsWith(',')) {
            $input.val(currentVal + ', ');
          }
          this.selectionStart = this.selectionEnd = this.value.length;
        }
        filterOptions();
      });

      $input.on('click', function(e) {
        e.preventDefault();
        this.selectionStart = this.selectionEnd = this.value.length;
      });

      $input.on('blur', function() {
        setTimeout(function() {
          if (!$list.is(':hover')) {
            toggleList(false);
            cleanInput();
          }
        }, 100);
      });

      $input.on('input', handleInputChange);
      $input.on('keydown', handleKeyDown);

      $list.on('mouseenter', '[ms-code-select="tag-name-new"]', function() {
        $(this).css('background-color', '#e0e0e0');
      });

      $list.on('mouseleave', '[ms-code-select="tag-name-new"]', function() {
        if (!$(this).hasClass('highlighted')) {
          $(this).css('background-color', '');
        }
      });

      initializeWithValue();
      toggleList(false);
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #109 v0.1 💙 - CUSTOM MULTI SELECT -->
<script>
  $(document).ready(function() {
    $('[ms-code-select-wrapper]').each(function() {
      const $wrapper = $(this);
      const isMulti = $wrapper.attr('ms-code-select-wrapper') === 'multi';
      const $input = $wrapper.find('[ms-code-select="input"]');
      const $list = $wrapper.find('[ms-code-select="list"]');
      const $selectedWrapper = $wrapper.find('[ms-code-select="selected-wrapper"]');
      const $emptyState = $wrapper.find('[ms-code-select="empty-state"]');
      const options = $input.attr('ms-code-select-options').split(',').map(opt => opt.trim());

      let selectedOptions = [];
      let highlightedIndex = -1;

      const $templateSelectedTag = $selectedWrapper.find('[ms-code-select="tag"]');
      const templateSelectedTagHTML = $templateSelectedTag.prop('outerHTML');
      $templateSelectedTag.remove();

      const $templateNewTag = $list.find('[ms-code-select="tag-name-new"]');
      const templateNewTagHTML = $templateNewTag.prop('outerHTML');
      $templateNewTag.remove();

      function createSelectedTag(value) {
        const $newTag = $(templateSelectedTagHTML);
        $newTag.find('[ms-code-select="tag-name-selected"]').text(value);
        $newTag.find('[ms-code-select="tag-close"]').on('click', function(e) {
          e.stopPropagation();
          removeTag(value);
        });
        return $newTag;
      }

      function addTag(value) {
        if (!selectedOptions.includes(value) && options.includes(value)) {
          selectedOptions.push(value);
          $selectedWrapper.append(createSelectedTag(value));
          updateInput();
          filterOptions();
        }
      }

      function removeTag(value) {
        selectedOptions = selectedOptions.filter(option => option !== value);
        $selectedWrapper.find(`[ms-code-select="tag-name-selected"]:contains("${value}")`).closest('[ms-code-select="tag"]').remove();
        updateInput();
        if (isMulti && selectedOptions.length > 0) {
          $input.val($input.val() + ', ');
        }
        filterOptions();
      }

      function updateInput() {
        $input.val(selectedOptions.join(', '));
      }

      function toggleList(show) {
        $list.toggle(show);
      }

      function createOptionElement(value) {
        const $option = $(templateNewTagHTML);
        $option.text(value);
        $option.on('click', function() {
          selectOption(value);
        });
        return $option;
      }

      function selectOption(value) {
        if (isMulti) {
          addTag(value);
          $input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
          $input.focus();
        } else {
          selectedOptions = [value];
          $selectedWrapper.empty().append(createSelectedTag(value));
          updateInput();
          toggleList(false);
        }
        filterOptions();
      }

      function filterOptions() {
        const inputValue = $input.val();
        const searchTerm = isMulti ? inputValue.split(',').pop().trim() : inputValue.trim();
        let visibleOptionsCount = 0;

        $list.find('[ms-code-select="tag-name-new"]').each(function() {
          const $option = $(this);
          const optionText = $option.text().toLowerCase();
          const matches = optionText.includes(searchTerm.toLowerCase());
          const isSelected = selectedOptions.includes($option.text());
          $option.toggle(matches && !isSelected);
          if (matches && !isSelected) visibleOptionsCount++;
        });

        $emptyState.toggle(visibleOptionsCount === 0 && searchTerm !== '');
        highlightedIndex = -1;
        updateHighlight();
      }

      function cleanInput() {
        const inputValues = $input.val().split(',').map(v => v.trim()).filter(v => v);
        const validValues = inputValues.filter(v => options.includes(v));
        selectedOptions = validValues;
        $selectedWrapper.empty();
        selectedOptions.forEach(value => $selectedWrapper.append(createSelectedTag(value)));
        updateInput();
        filterOptions();
      }

      function handleInputChange() {
        const inputValue = $input.val();
        const inputValues = inputValue.split(',').map(v => v.trim());
        const lastValue = inputValues[inputValues.length - 1];

        if (inputValue.endsWith(',') || inputValue.endsWith(', ')) {
          inputValues.pop();
          const newValidValues = inputValues.filter(v => options.includes(v) && !selectedOptions.includes(v));
          newValidValues.forEach(addTag);
          $input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
        } else if (options.includes(lastValue) && !selectedOptions.includes(lastValue)) {
          addTag(lastValue);
          $input.val(selectedOptions.join(', ') + ', ');
        }

        filterOptions();
      }

      function initializeWithValue() {
        const initialValue = $input.val();
        if (initialValue) {
          const initialValues = initialValue.split(',').map(v => v.trim());
          initialValues.forEach(value => {
            if (options.includes(value)) {
              addTag(value);
            }
          });
          updateInput();
          filterOptions();
        }
      }

      function updateHighlight() {
        $list.find('[ms-code-select="tag-name-new"]').removeClass('highlighted').css('background-color', '');
        if (highlightedIndex >= 0) {
          $list.find('[ms-code-select="tag-name-new"]:visible').eq(highlightedIndex)
            .addClass('highlighted')
            .css('background-color', '#e0e0e0');
        }
      }

      function handleKeyDown(e) {
        const visibleOptions = $list.find('[ms-code-select="tag-name-new"]:visible');
        const optionCount = visibleOptions.length;

        switch (e.key) {
          case 'ArrowDown':
            e.preventDefault();
            highlightedIndex = (highlightedIndex + 1) % optionCount;
            updateHighlight();
            break;
          case 'ArrowUp':
            e.preventDefault();
            highlightedIndex = (highlightedIndex - 1 + optionCount) % optionCount;
            updateHighlight();
            break;
          case 'Enter':
            e.preventDefault();
            if (highlightedIndex >= 0) {
              const selectedValue = visibleOptions.eq(highlightedIndex).text();
              selectOption(selectedValue);
            }
            break;
        }
      }

      $.each(options, function(i, option) {
        $list.append(createOptionElement(option));
      });

      $input.on('focus', function() {
        toggleList(true);
        if (isMulti) {
          const currentVal = $input.val().trim();
          if (currentVal !== '' && !currentVal.endsWith(',')) {
            $input.val(currentVal + ', ');
          }
          this.selectionStart = this.selectionEnd = this.value.length;
        }
        filterOptions();
      });

      $input.on('click', function(e) {
        e.preventDefault();
        this.selectionStart = this.selectionEnd = this.value.length;
      });

      $input.on('blur', function() {
        setTimeout(function() {
          if (!$list.is(':hover')) {
            toggleList(false);
            cleanInput();
          }
        }, 100);
      });

      $input.on('input', handleInputChange);
      $input.on('keydown', handleKeyDown);

      $list.on('mouseenter', '[ms-code-select="tag-name-new"]', function() {
        $(this).css('background-color', '#e0e0e0');
      });

      $list.on('mouseleave', '[ms-code-select="tag-name-new"]', function() {
        if (!$(this).hasClass('highlighted')) {
          $(this).css('background-color', '');
        }
      });

      initializeWithValue();
      toggleList(false);
    });
  });
</script>
Ansicht Memberscript
UX

#Nr. 108 - Benutzerdefinierte Formularübermittlungsschaltflächen

Erstellen Sie ein beliebiges Element in Webflow und verwenden Sie es, um eine beliebige Art von Formular zu übermitteln.


<!-- 💙 MEMBERSCRIPT #108 v0.1 💙 CUSTOM FORM SUBMIT BUTTON -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
    // Find all elements with the ms-code-submit-new attribute
    const newSubmitButtons = document.querySelectorAll('[ms-code-submit-new]');

    // Add click event listeners to each new submit button
    newSubmitButtons.forEach(button => {
        button.addEventListener('click', function(e) {
            e.preventDefault(); // Prevent default action if it's a link

            // Get the value of the ms-code-submit-new attribute
            const submitId = this.getAttribute('ms-code-submit-new');

            // Find the corresponding old submit button
            const oldSubmitButton = document.querySelector(`[ms-code-submit-old="${submitId}"]`);

            // If found, trigger a click on the old submit button
            if (oldSubmitButton) {
                oldSubmitButton.click();
            } else {
                console.error(`No matching old submit button found for ID: ${submitId}`);
            }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #108 v0.1 💙 CUSTOM FORM SUBMIT BUTTON -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
    // Find all elements with the ms-code-submit-new attribute
    const newSubmitButtons = document.querySelectorAll('[ms-code-submit-new]');

    // Add click event listeners to each new submit button
    newSubmitButtons.forEach(button => {
        button.addEventListener('click', function(e) {
            e.preventDefault(); // Prevent default action if it's a link

            // Get the value of the ms-code-submit-new attribute
            const submitId = this.getAttribute('ms-code-submit-new');

            // Find the corresponding old submit button
            const oldSubmitButton = document.querySelector(`[ms-code-submit-old="${submitId}"]`);

            // If found, trigger a click on the old submit button
            if (oldSubmitButton) {
                oldSubmitButton.click();
            } else {
                console.error(`No matching old submit button found for ID: ${submitId}`);
            }
        });
    });
});
</script>
Ansicht Memberscript
UX

#Nr. 107 - Plan mit Funkgeräten auswählen

Fügen Sie den Anmeldeformularen und den Formularen zur Planaktualisierung ein Auswahlradio für den Plan hinzu.


<!-- 💙 MEMBERSCRIPT #107 v0.1 💙 SELECT PLAN WITH RADIO BUTTONS -->
<script>
  (function() {
    const PRICE_ATTRIBUTES = [
      'data-ms-plan:add',
      'data-ms-plan:update',
      'data-ms-price:add',
      'data-ms-price:update'
    ];

    function findElementWithAttribute(form) {
      // First, check if the form itself has one of the attributes
      for (let attr of PRICE_ATTRIBUTES) {
        if (form.hasAttribute(attr)) {
          return { element: form, attribute: attr };
        }
      }

      // If not found on form, search child elements
      for (let attr of PRICE_ATTRIBUTES) {
        let element = Array.from(form.querySelectorAll('*')).find(el => el.hasAttribute(attr));
        if (element) {
          return { element, attribute: attr };
        }
      }
      return null;
    }

    function updateAttribute(radio) {
      const form = radio.closest('form');
      if (!form) return;

      const result = findElementWithAttribute(form);
      if (result) {
        result.element.setAttribute(result.attribute, radio.value);
      }
    }

    function handleRadioChange(e) {
      updateAttribute(e.target);
    }

    function initializeRadioButtons() {
      const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
      forms.forEach(form => {
        const radios = form.querySelectorAll('input[type="radio"]');
        radios.forEach(radio => {
          radio.addEventListener('change', handleRadioChange);
          if (radio.checked) {
            updateAttribute(radio);
          }
        });
      });
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initializeRadioButtons);
    } else {
      initializeRadioButtons();
    }
  })();

  // Only keep this section if you have an update plan form on the page
  (function() {
    function updateRadioButtonState(radio) {
      radio.checked = true;
      radio.dispatchEvent(new Event('change'));

      // Update custom radio button UI if present
      const customRadio = radio.parentElement.querySelector('.w-radio-input');
      if (customRadio) {
        customRadio.classList.add('w--redirected-checked');
      }
    }

    function checkAndSelectPlan() {
      const msMemData = localStorage.getItem('_ms-mem');
      if (!msMemData) return;

      try {
        const memberData = JSON.parse(msMemData);
        const activePlanConnections = memberData.planConnections?.filter(conn => conn.active) || [];

        if (activePlanConnections.length === 0) return;

        const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
        forms.forEach(form => {
          const radios = form.querySelectorAll('input[type="radio"]');
          radios.forEach(radio => {
            const matchingPlan = activePlanConnections.find(conn => conn.payment.priceId === radio.value);
            if (matchingPlan) {
              updateRadioButtonState(radio);
            }
          });
        });
      } catch (error) {
        console.error('Error processing _ms-mem data:', error);
      }
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', checkAndSelectPlan);
    } else {
      checkAndSelectPlan();
    }
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #107 v0.1 💙 SELECT PLAN WITH RADIO BUTTONS -->
<script>
  (function() {
    const PRICE_ATTRIBUTES = [
      'data-ms-plan:add',
      'data-ms-plan:update',
      'data-ms-price:add',
      'data-ms-price:update'
    ];

    function findElementWithAttribute(form) {
      // First, check if the form itself has one of the attributes
      for (let attr of PRICE_ATTRIBUTES) {
        if (form.hasAttribute(attr)) {
          return { element: form, attribute: attr };
        }
      }

      // If not found on form, search child elements
      for (let attr of PRICE_ATTRIBUTES) {
        let element = Array.from(form.querySelectorAll('*')).find(el => el.hasAttribute(attr));
        if (element) {
          return { element, attribute: attr };
        }
      }
      return null;
    }

    function updateAttribute(radio) {
      const form = radio.closest('form');
      if (!form) return;

      const result = findElementWithAttribute(form);
      if (result) {
        result.element.setAttribute(result.attribute, radio.value);
      }
    }

    function handleRadioChange(e) {
      updateAttribute(e.target);
    }

    function initializeRadioButtons() {
      const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
      forms.forEach(form => {
        const radios = form.querySelectorAll('input[type="radio"]');
        radios.forEach(radio => {
          radio.addEventListener('change', handleRadioChange);
          if (radio.checked) {
            updateAttribute(radio);
          }
        });
      });
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initializeRadioButtons);
    } else {
      initializeRadioButtons();
    }
  })();

  // Only keep this section if you have an update plan form on the page
  (function() {
    function updateRadioButtonState(radio) {
      radio.checked = true;
      radio.dispatchEvent(new Event('change'));

      // Update custom radio button UI if present
      const customRadio = radio.parentElement.querySelector('.w-radio-input');
      if (customRadio) {
        customRadio.classList.add('w--redirected-checked');
      }
    }

    function checkAndSelectPlan() {
      const msMemData = localStorage.getItem('_ms-mem');
      if (!msMemData) return;

      try {
        const memberData = JSON.parse(msMemData);
        const activePlanConnections = memberData.planConnections?.filter(conn => conn.active) || [];

        if (activePlanConnections.length === 0) return;

        const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
        forms.forEach(form => {
          const radios = form.querySelectorAll('input[type="radio"]');
          radios.forEach(radio => {
            const matchingPlan = activePlanConnections.find(conn => conn.payment.priceId === radio.value);
            if (matchingPlan) {
              updateRadioButtonState(radio);
            }
          });
        });
      } catch (error) {
        console.error('Error processing _ms-mem data:', error);
      }
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', checkAndSelectPlan);
    } else {
      checkAndSelectPlan();
    }
  })();
</script>
Ansicht Memberscript
JSON

#Nr. 106 - Liken und Speichern von CMS-Artikeln

Erlauben Sie Ihren Mitgliedern, CMS-Elemente in ihrem Profil zu speichern.


<!-- 💙 MEMBERSCRIPT #106 v0.2 💙 SAVING & UNSAVING CMS ITEMS -->
<style>
  [ms-code-save], [ms-code-unsave] {
    display: none;
  }
  [ms-code-save-item] {
    display: none;
  }
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
  const memberstack = window.$memberstackDom;
  let isLoggedIn = false;
  let savedItems = [];

  async function checkMemberLogin() {
    try {
      const member = await memberstack.getCurrentMember();
      return !!member;
    } catch (error) {
      return false;
    }
  }

  function getSavedItems(memberData) {
    return memberData.savedItems || [];
  }

  function updateButtonVisibility() {
    const saveButtons = document.querySelectorAll('[ms-code-save]');
    const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');

    saveButtons.forEach(button => {
      const itemId = button.getAttribute('ms-code-save');
      button.style.display = !savedItems.includes(itemId) ? 'block' : 'none';
    });

    unsaveButtons.forEach(button => {
      const itemId = button.getAttribute('ms-code-unsave');
      button.style.display = savedItems.includes(itemId) ? 'block' : 'none';
    });
  }

  function updateItemVisibility() {
    const saveLists = document.querySelectorAll('[ms-code-save-list]');
    saveLists.forEach(list => {
      const filter = list.getAttribute('ms-code-save-list');
      const items = list.querySelectorAll('[ms-code-save-item]');
      items.forEach(item => {
        const saveButton = item.querySelector('[ms-code-save]');
        if (!saveButton) {
          item.style.display = 'block';
          return;
        }
        const itemId = saveButton.getAttribute('ms-code-save');
        
        if (!isLoggedIn || filter === 'all') {
          item.style.display = 'block';
        } else if (filter === 'saved' & savedItems.includes(itemId)) {
          item.style.display = 'block';
        } else if (filter === 'unsaved' & !savedItems.includes(itemId)) {
          item.style.display = 'block';
        } else {
          item.style.display = 'none';
        }
      });
    });
  }

  async function handleButtonClick(event) {
    if (!isLoggedIn) return;

    const button = event.currentTarget;
    const action = button.getAttribute('ms-code-save') ? 'save' : 'unsave';
    const itemId = button.getAttribute(action === 'save' ? 'ms-code-save' : 'ms-code-unsave');
    
    if (action === 'save' && !savedItems.includes(itemId)) {
      savedItems.push(itemId);
    } else if (action === 'unsave') {
      savedItems = savedItems.filter(id => id !== itemId);
    }

    try {
      await memberstack.updateMemberJSON({ json: { savedItems } });
    } catch (error) {
      // Silently handle the error
    }

    updateButtonVisibility();
    updateItemVisibility();
  }

  function addClickListeners() {
    const saveButtons = document.querySelectorAll('[ms-code-save]');
    const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
    saveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
    unsaveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
  }

  async function initializeScript() {
    isLoggedIn = await checkMemberLogin();

    if (isLoggedIn) {
      try {
        const result = await memberstack.getMemberJSON();
        const memberData = result.data || {};
        savedItems = getSavedItems(memberData);
      } catch (error) {
        // Silently handle the error
      }
    }

    updateButtonVisibility();
    updateItemVisibility();
    addClickListeners();

    // Set up a MutationObserver to watch for changes in the DOM
    const observer = new MutationObserver((mutations) => {
      let shouldUpdate = false;
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          shouldUpdate = true;
        }
      });
      if (shouldUpdate) {
        updateButtonVisibility();
        updateItemVisibility();
        addClickListeners();
      }
    });

    // Start observing the document with the configured parameters
    observer.observe(document.body, { childList: true, subtree: true });
  }

  initializeScript();
});
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #106 v0.2 💙 SAVING & UNSAVING CMS ITEMS -->
<style>
  [ms-code-save], [ms-code-unsave] {
    display: none;
  }
  [ms-code-save-item] {
    display: none;
  }
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
  const memberstack = window.$memberstackDom;
  let isLoggedIn = false;
  let savedItems = [];

  async function checkMemberLogin() {
    try {
      const member = await memberstack.getCurrentMember();
      return !!member;
    } catch (error) {
      return false;
    }
  }

  function getSavedItems(memberData) {
    return memberData.savedItems || [];
  }

  function updateButtonVisibility() {
    const saveButtons = document.querySelectorAll('[ms-code-save]');
    const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');

    saveButtons.forEach(button => {
      const itemId = button.getAttribute('ms-code-save');
      button.style.display = !savedItems.includes(itemId) ? 'block' : 'none';
    });

    unsaveButtons.forEach(button => {
      const itemId = button.getAttribute('ms-code-unsave');
      button.style.display = savedItems.includes(itemId) ? 'block' : 'none';
    });
  }

  function updateItemVisibility() {
    const saveLists = document.querySelectorAll('[ms-code-save-list]');
    saveLists.forEach(list => {
      const filter = list.getAttribute('ms-code-save-list');
      const items = list.querySelectorAll('[ms-code-save-item]');
      items.forEach(item => {
        const saveButton = item.querySelector('[ms-code-save]');
        if (!saveButton) {
          item.style.display = 'block';
          return;
        }
        const itemId = saveButton.getAttribute('ms-code-save');
        
        if (!isLoggedIn || filter === 'all') {
          item.style.display = 'block';
        } else if (filter === 'saved' & savedItems.includes(itemId)) {
          item.style.display = 'block';
        } else if (filter === 'unsaved' & !savedItems.includes(itemId)) {
          item.style.display = 'block';
        } else {
          item.style.display = 'none';
        }
      });
    });
  }

  async function handleButtonClick(event) {
    if (!isLoggedIn) return;

    const button = event.currentTarget;
    const action = button.getAttribute('ms-code-save') ? 'save' : 'unsave';
    const itemId = button.getAttribute(action === 'save' ? 'ms-code-save' : 'ms-code-unsave');
    
    if (action === 'save' && !savedItems.includes(itemId)) {
      savedItems.push(itemId);
    } else if (action === 'unsave') {
      savedItems = savedItems.filter(id => id !== itemId);
    }

    try {
      await memberstack.updateMemberJSON({ json: { savedItems } });
    } catch (error) {
      // Silently handle the error
    }

    updateButtonVisibility();
    updateItemVisibility();
  }

  function addClickListeners() {
    const saveButtons = document.querySelectorAll('[ms-code-save]');
    const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
    saveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
    unsaveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
  }

  async function initializeScript() {
    isLoggedIn = await checkMemberLogin();

    if (isLoggedIn) {
      try {
        const result = await memberstack.getMemberJSON();
        const memberData = result.data || {};
        savedItems = getSavedItems(memberData);
      } catch (error) {
        // Silently handle the error
      }
    }

    updateButtonVisibility();
    updateItemVisibility();
    addClickListeners();

    // Set up a MutationObserver to watch for changes in the DOM
    const observer = new MutationObserver((mutations) => {
      let shouldUpdate = false;
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          shouldUpdate = true;
        }
      });
      if (shouldUpdate) {
        updateButtonVisibility();
        updateItemVisibility();
        addClickListeners();
      }
    });

    // Start observing the document with the configured parameters
    observer.observe(document.body, { childList: true, subtree: true });
  }

  initializeScript();
});
</script>
Ansicht Memberscript
UX
Benutzerdefinierte Abläufe

#Nr. 105 - Auschecken nach Anmeldung

Automatischer Start der Kasse, wenn ein Mitglied einen Preis auswählt, bevor es sich einloggt.


<!-- 💙 MEMBERSCRIPT #105 v0.1 💙 CHECKOUT AFTER LOGIN -->
<script>
  /* Checks if the current URL matches the configured redirect URL, or if no specific URL is required */
  function isCorrectPage() {
    return redirectOnLoginURL === '' || window.location.pathname === redirectOnLoginURL;
  }

  /* Checks if Memberstack is fully loaded before running any Memberstack-specific code.*/
  function memberstackReady(callback) {
    function checkAndExecute() {
      if (window.$memberstackDom) {
        callback(); // Memberstack is ready, run the callback function.
      } else {
        setTimeout(checkAndExecute, 100); // Wait for 100ms and check again.
      }
    }

    checkAndExecute(); // Start checking if Memberstack is ready.
  }

  /* Initiates the Stripe checkout process with a specified price ID.*/
  async function initiateCheckout(priceId) {
    try {
      // Set a flag in session storage to indicate that the checkout page was accessed.
      sessionStorage.setItem('ms_checkout_viewed', 'true');
      await window.$memberstackDom.purchasePlansWithCheckout({
        priceId, // The price ID for the product being purchased.
        returnUrl: window.location.href, // Redirect the user back here after completing the checkout.
      });
    } catch (error) {
      console.error('Failed to initiate payment:', error); // Provide error details in the console.
    }
  }

  /* Main execution flow that starts once Memberstack is confirmed to be ready */
  memberstackReady(() => {
    window.$memberstackDom.getCurrentMember().then(({ data: member }) => {
      if (member && sessionStorage.getItem('ms_price') && !sessionStorage.getItem('ms_checkout_viewed')) {
        initiateCheckout(sessionStorage.getItem('ms_price')); // Start the checkout process if conditions are met.
      }
    }).catch(error => {
      console.error('Failed to retrieve user data:', error); // Log an error if fetching member data fails.
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #105 v0.1 💙 CHECKOUT AFTER LOGIN -->
<script>
  /* Checks if the current URL matches the configured redirect URL, or if no specific URL is required */
  function isCorrectPage() {
    return redirectOnLoginURL === '' || window.location.pathname === redirectOnLoginURL;
  }

  /* Checks if Memberstack is fully loaded before running any Memberstack-specific code.*/
  function memberstackReady(callback) {
    function checkAndExecute() {
      if (window.$memberstackDom) {
        callback(); // Memberstack is ready, run the callback function.
      } else {
        setTimeout(checkAndExecute, 100); // Wait for 100ms and check again.
      }
    }

    checkAndExecute(); // Start checking if Memberstack is ready.
  }

  /* Initiates the Stripe checkout process with a specified price ID.*/
  async function initiateCheckout(priceId) {
    try {
      // Set a flag in session storage to indicate that the checkout page was accessed.
      sessionStorage.setItem('ms_checkout_viewed', 'true');
      await window.$memberstackDom.purchasePlansWithCheckout({
        priceId, // The price ID for the product being purchased.
        returnUrl: window.location.href, // Redirect the user back here after completing the checkout.
      });
    } catch (error) {
      console.error('Failed to initiate payment:', error); // Provide error details in the console.
    }
  }

  /* Main execution flow that starts once Memberstack is confirmed to be ready */
  memberstackReady(() => {
    window.$memberstackDom.getCurrentMember().then(({ data: member }) => {
      if (member && sessionStorage.getItem('ms_price') && !sessionStorage.getItem('ms_checkout_viewed')) {
        initiateCheckout(sessionStorage.getItem('ms_price')); // Start the checkout process if conditions are met.
      }
    }).catch(error => {
      console.error('Failed to retrieve user data:', error); // Log an error if fetching member data fails.
    });
  });
</script>
Ansicht Memberscript
UX

#Nr. 104 - Online-Anzeige

Zeigen Sie Ihren Website-Besuchern Ihren Online-Status auf der Grundlage von Zeitzonen.


<!-- 💙 MEMBERSCRIPT #104 v0.1 💙 ONLINE INDICATOR -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    const businessHours = {
        start: 9, // Business hours start at 9 AM
        end: 17, // Business hours end at 5 PM
        days: [1, 2, 3, 4, 5] // Monday to Friday
    };
    const colors = {
        businessHours: '#34b426',
        outsideBusinessHours: '#F25022'
    };

    const wrappers = document.querySelectorAll('[ms-code-online-wrapper]');
    wrappers.forEach(wrapper => {
        const timeZone = wrapper.getAttribute('ms-code-online-wrapper');
        const dot = wrapper.querySelector('[ms-code-online="dot"]');
        const timeSpan = wrapper.querySelector('[ms-code-online="time"]');

        const now = new Date();
        const formatter = new Intl.DateTimeFormat('en-US', {
            hour: 'numeric',
            minute: '2-digit',
            timeZone: timeZone
        });
        const formattedTime = formatter.format(now);

        if (timeSpan) timeSpan.textContent = formattedTime;

        const currentDay = now.getDay();
        const currentHour = new Date().toLocaleTimeString('en-US', {
            hour: '2-digit',
            hour12: false,
            timeZone: timeZone
        });

        const isBusinessDay = businessHours.days.includes(currentDay);
        const isBusinessHour = currentHour >= businessHours.start && currentHour < businessHours.end;

        if (dot) {
            dot.style.backgroundColor = (isBusinessDay && isBusinessHour) ? colors.businessHours : colors.outsideBusinessHours;
        }
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #104 v0.1 💙 ONLINE INDICATOR -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    const businessHours = {
        start: 9, // Business hours start at 9 AM
        end: 17, // Business hours end at 5 PM
        days: [1, 2, 3, 4, 5] // Monday to Friday
    };
    const colors = {
        businessHours: '#34b426',
        outsideBusinessHours: '#F25022'
    };

    const wrappers = document.querySelectorAll('[ms-code-online-wrapper]');
    wrappers.forEach(wrapper => {
        const timeZone = wrapper.getAttribute('ms-code-online-wrapper');
        const dot = wrapper.querySelector('[ms-code-online="dot"]');
        const timeSpan = wrapper.querySelector('[ms-code-online="time"]');

        const now = new Date();
        const formatter = new Intl.DateTimeFormat('en-US', {
            hour: 'numeric',
            minute: '2-digit',
            timeZone: timeZone
        });
        const formattedTime = formatter.format(now);

        if (timeSpan) timeSpan.textContent = formattedTime;

        const currentDay = now.getDay();
        const currentHour = new Date().toLocaleTimeString('en-US', {
            hour: '2-digit',
            hour12: false,
            timeZone: timeZone
        });

        const isBusinessDay = businessHours.days.includes(currentDay);
        const isBusinessHour = currentHour >= businessHours.start && currentHour < businessHours.end;

        if (dot) {
            dot.style.backgroundColor = (isBusinessDay && isBusinessHour) ? colors.businessHours : colors.outsideBusinessHours;
        }
    });
});
</script>
Ansicht Memberscript
Integration

#Nr. 103 - Benutzerdefiniertes Buchungsformular

Fügen Sie Ihrer Website ein benutzerdefiniertes Buchungsformular hinzu, das ein Google-Kalenderereignis erstellt.


<!-- 💙 MEMBERSCRIPT #103 v0.1 💙 CUSTOM BOOKING FORM -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
  function getNextBusinessDays() {
    let businessDays = [];
    let currentDate = moment();
    currentDate.add(1, 'days');
    
    while (businessDays.length < 14) {
      if (currentDate.day() !== 0 && currentDate.day() !== 6) {
        let formattedDay = currentDate.format('dddd, MMMM D');
        let rawDay = currentDate.format('YYYY-MM-DD');
        businessDays.push({formattedDay, rawDay});
      }
      currentDate.add(1, 'days');
    }
    return businessDays;
  }

  function generateTimeSlots() {
    let slots = [];
    let startHour = 9;
    let endHour = 16.5;
    let currentTime = moment().startOf('day').add(startHour, 'hours');
    
    while (currentTime.hour() + (currentTime.minute() / 60) <= endHour) {
      let formattedTime = currentTime.format('h:mm A');
      let timeValue = currentTime.format('HH:mm');
      slots.push({formattedTime, timeValue});
      currentTime.add(30, 'minutes');
    }
    return slots;
  }

  function updateTimestamp(day, time, timezone) {
    let timestampInput = document.getElementById('timestamp');
    if (!timestampInput) {
        timestampInput = document.createElement('input');
        timestampInput.type = 'hidden';
        timestampInput.id = 'timestamp';
        timestampInput.name = 'timestamp';
        document.querySelector('form').appendChild(timestampInput);
    }

    let datetime = moment.tz(`${day} ${time}`, "YYYY-MM-DD HH:mm", timezone);
    timestampInput.value = datetime.valueOf();
  }

  function populateFields() {
    const days = getNextBusinessDays();
    const times = generateTimeSlots();
    const daySelect = document.querySelector('[ms-code-booking="day"]');
    const timeSelect = document.querySelector('[ms-code-booking="time"]');
    const form = daySelect.closest('form');
    const timezone = form.getAttribute('ms-code-booking-timezone') || moment.tz.guess();
    
    days.forEach(({formattedDay, rawDay}) => {
      let option = new Option(formattedDay, rawDay);
      daySelect.appendChild(option);
    });
    
    times.forEach(({formattedTime, timeValue}) => {
      let option = new Option(formattedTime, timeValue);
      timeSelect.appendChild(option);
    });

    function handleSelectChange() {
      if (daySelect.value && timeSelect.value) {
        updateTimestamp(daySelect.value, timeSelect.value, timezone);
      }
    }

    daySelect.addEventListener('change', handleSelectChange);
    timeSelect.addEventListener('change', handleSelectChange);
  }

  populateFields();
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #103 v0.1 💙 CUSTOM BOOKING FORM -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
  function getNextBusinessDays() {
    let businessDays = [];
    let currentDate = moment();
    currentDate.add(1, 'days');
    
    while (businessDays.length < 14) {
      if (currentDate.day() !== 0 && currentDate.day() !== 6) {
        let formattedDay = currentDate.format('dddd, MMMM D');
        let rawDay = currentDate.format('YYYY-MM-DD');
        businessDays.push({formattedDay, rawDay});
      }
      currentDate.add(1, 'days');
    }
    return businessDays;
  }

  function generateTimeSlots() {
    let slots = [];
    let startHour = 9;
    let endHour = 16.5;
    let currentTime = moment().startOf('day').add(startHour, 'hours');
    
    while (currentTime.hour() + (currentTime.minute() / 60) <= endHour) {
      let formattedTime = currentTime.format('h:mm A');
      let timeValue = currentTime.format('HH:mm');
      slots.push({formattedTime, timeValue});
      currentTime.add(30, 'minutes');
    }
    return slots;
  }

  function updateTimestamp(day, time, timezone) {
    let timestampInput = document.getElementById('timestamp');
    if (!timestampInput) {
        timestampInput = document.createElement('input');
        timestampInput.type = 'hidden';
        timestampInput.id = 'timestamp';
        timestampInput.name = 'timestamp';
        document.querySelector('form').appendChild(timestampInput);
    }

    let datetime = moment.tz(`${day} ${time}`, "YYYY-MM-DD HH:mm", timezone);
    timestampInput.value = datetime.valueOf();
  }

  function populateFields() {
    const days = getNextBusinessDays();
    const times = generateTimeSlots();
    const daySelect = document.querySelector('[ms-code-booking="day"]');
    const timeSelect = document.querySelector('[ms-code-booking="time"]');
    const form = daySelect.closest('form');
    const timezone = form.getAttribute('ms-code-booking-timezone') || moment.tz.guess();
    
    days.forEach(({formattedDay, rawDay}) => {
      let option = new Option(formattedDay, rawDay);
      daySelect.appendChild(option);
    });
    
    times.forEach(({formattedTime, timeValue}) => {
      let option = new Option(formattedTime, timeValue);
      timeSelect.appendChild(option);
    });

    function handleSelectChange() {
      if (daySelect.value && timeSelect.value) {
        updateTimestamp(daySelect.value, timeSelect.value, timezone);
      }
    }

    daySelect.addEventListener('change', handleSelectChange);
    timeSelect.addEventListener('change', handleSelectChange);
  }

  populateFields();
});
</script>
Ansicht Memberscript
Benutzerdefinierte Felder
UX

#Nr. 102 - Automatische Anpassung der Textarea-Höhe

Vergrößern oder verkleinern Sie die Höhe einer Textarea je nach ihrem Inhalt.


<!-- 💙 MEMBERSCRIPT #102 v0.1 💙 RESIZE TEXTAREA VERTICALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    const elements = document.querySelectorAll('[data-ms-post="content"], [ms-code-resize-input="height"]');

    elements.forEach(element => {
        if (element.tagName.toLowerCase() === 'textarea') {
            element.addEventListener('input', function() {
                autoResize(this);
            }, false);
        }
    });

    function autoResize(element) {
        const maxHeight = parseInt(getComputedStyle(element).maxHeight, 10);
        element.style.height = 'auto';
        element.style.overflow = 'hidden'; // Prevents scrollbar appearance during height adjustment

        if (element.scrollHeight > maxHeight) {
            element.style.height = `${maxHeight}px`;
            element.style.overflow = 'auto'; // Adds scrollbar when content exceeds max height
        } else {
            element.style.height = `${element.scrollHeight}px`;
        }
    }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #102 v0.1 💙 RESIZE TEXTAREA VERTICALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    const elements = document.querySelectorAll('[data-ms-post="content"], [ms-code-resize-input="height"]');

    elements.forEach(element => {
        if (element.tagName.toLowerCase() === 'textarea') {
            element.addEventListener('input', function() {
                autoResize(this);
            }, false);
        }
    });

    function autoResize(element) {
        const maxHeight = parseInt(getComputedStyle(element).maxHeight, 10);
        element.style.height = 'auto';
        element.style.overflow = 'hidden'; // Prevents scrollbar appearance during height adjustment

        if (element.scrollHeight > maxHeight) {
            element.style.height = `${maxHeight}px`;
            element.style.overflow = 'auto'; // Adds scrollbar when content exceeds max height
        } else {
            element.style.height = `${element.scrollHeight}px`;
        }
    }
});
</script>
Ansicht Memberscript
Benutzerdefinierte Felder
UX

#101 - Automatische Anpassung der Eingabebreite

Erhöhen oder verringern Sie die Breite einer Eingabe je nach Inhalt.


<!-- 💙 MEMBERSCRIPT #101 v0.1 💙 RESIZE INPUT HORIZONTALLY -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const elements = document.querySelectorAll('[ms-code-resize-input="width"]');

    // Store the initial widths
    const initialWidths = new Map();
    elements.forEach(element => {
        initialWidths.set(element, element.offsetWidth);
    });

    elements.forEach(element => {
        element.addEventListener('input', function() {
            autoResizeWidth(this);
        });
    });

    function autoResizeWidth(element) {
        // Find the nearest hidden measure element
        const measurer = element.nextElementSibling.getAttribute('ms-code-resize-input') === 'hidden-measure' 
                         ? element.nextElementSibling 
                         : null;
        if (!measurer) return; // Exit if no measurer is found

        measurer.textContent = element.value;

        const maxWidth = parseInt(getComputedStyle(element).maxWidth, 10);
        const minWidth = initialWidths.get(element);
        const contentWidth = measurer.offsetWidth;

        if (contentWidth > minWidth && contentWidth < maxWidth) {
            element.style.width = `${contentWidth}px`;
        } else if (contentWidth >= maxWidth) {
            element.style.width = `${maxWidth}px`;
        } else {
            element.style.width = `${minWidth}px`;
        }
    }
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #101 v0.1 💙 RESIZE INPUT HORIZONTALLY -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const elements = document.querySelectorAll('[ms-code-resize-input="width"]');

    // Store the initial widths
    const initialWidths = new Map();
    elements.forEach(element => {
        initialWidths.set(element, element.offsetWidth);
    });

    elements.forEach(element => {
        element.addEventListener('input', function() {
            autoResizeWidth(this);
        });
    });

    function autoResizeWidth(element) {
        // Find the nearest hidden measure element
        const measurer = element.nextElementSibling.getAttribute('ms-code-resize-input') === 'hidden-measure' 
                         ? element.nextElementSibling 
                         : null;
        if (!measurer) return; // Exit if no measurer is found

        measurer.textContent = element.value;

        const maxWidth = parseInt(getComputedStyle(element).maxWidth, 10);
        const minWidth = initialWidths.get(element);
        const contentWidth = measurer.offsetWidth;

        if (contentWidth > minWidth && contentWidth < maxWidth) {
            element.style.width = `${contentWidth}px`;
        } else if (contentWidth >= maxWidth) {
            element.style.width = `${maxWidth}px`;
        } else {
            element.style.width = `${minWidth}px`;
        }
    }
  });
</script>
Ansicht Memberscript
Benutzerdefinierte Felder
Integration

#Nr. 100 - Bild-Uploads automatisch komprimieren

Komprimieren Sie Bild-Uploads, einschließlich Profilbilder.


<!-- 💙 MEMBERSCRIPT #100 v0.1 💙 AUTO-COMPRESSED IMAGE UPLOADS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/compressorjs/1.2.1/compressor.min.js" integrity="sha512-MgYeYFj8R3S6rvZHiJ1xA9cM/VDGcT4eRRFQwGA7qDP7NHbnWKNmAm28z0LVjOuUqjD0T9JxpDMdVqsZOSHaSA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
  const compressibleInputs = document.querySelectorAll('input[type="file"][ms-code-file_compress]');

  compressibleInputs.forEach(fileInput => {
    let isCompressing = false;

    fileInput.addEventListener('change', function (event) {
      if (isCompressing) {
        isCompressing = false;
        return;
      }

      if (fileInput.files.length === 0) {
        return;
      }

      const originalFile = fileInput.files[0];
      const compressionLevel = parseFloat(fileInput.getAttribute('ms-code-file_compress'));

      new Compressor(originalFile, {
        quality: compressionLevel,
        maxWidth: 2000,
        maxHeight: 2000,
        success(compressedResult) {
          const compressedFile = new File([compressedResult], originalFile.name, {
            type: compressedResult.type,
            lastModified: Date.now(),
          });

          const dataTransfer = new DataTransfer();
          dataTransfer.items.add(compressedFile);
          fileInput.files = dataTransfer.files;

          isCompressing = true;
          fileInput.dispatchEvent(new Event('change', { bubbles: true }));
        },
        error(err) {
          console.error('Compression Error: ', err.message);
        },
      });

      event.stopPropagation();
    }, true);
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #100 v0.1 💙 AUTO-COMPRESSED IMAGE UPLOADS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/compressorjs/1.2.1/compressor.min.js" integrity="sha512-MgYeYFj8R3S6rvZHiJ1xA9cM/VDGcT4eRRFQwGA7qDP7NHbnWKNmAm28z0LVjOuUqjD0T9JxpDMdVqsZOSHaSA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
  const compressibleInputs = document.querySelectorAll('input[type="file"][ms-code-file_compress]');

  compressibleInputs.forEach(fileInput => {
    let isCompressing = false;

    fileInput.addEventListener('change', function (event) {
      if (isCompressing) {
        isCompressing = false;
        return;
      }

      if (fileInput.files.length === 0) {
        return;
      }

      const originalFile = fileInput.files[0];
      const compressionLevel = parseFloat(fileInput.getAttribute('ms-code-file_compress'));

      new Compressor(originalFile, {
        quality: compressionLevel,
        maxWidth: 2000,
        maxHeight: 2000,
        success(compressedResult) {
          const compressedFile = new File([compressedResult], originalFile.name, {
            type: compressedResult.type,
            lastModified: Date.now(),
          });

          const dataTransfer = new DataTransfer();
          dataTransfer.items.add(compressedFile);
          fileInput.files = dataTransfer.files;

          isCompressing = true;
          fileInput.dispatchEvent(new Event('change', { bubbles: true }));
        },
        error(err) {
          console.error('Compression Error: ', err.message);
        },
      });

      event.stopPropagation();
    }, true);
  });
});
</script>
Ansicht Memberscript
Benutzerdefinierte Felder

#Nr. 99 - Benutzerdefinierte Datei-Eingaben

Verwandeln Sie alles in eine Dateieingabe!


<!-- 💙 MEMBERSCRIPT #99 v0.1 💙 CUSTOM FILE UPLOAD INPUT -->
<script>
document.addEventListener('DOMContentLoaded', function () {
  const uploadButtons = document.querySelectorAll('[ms-code-file_uploader]');

  uploadButtons.forEach(button => {
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.style.display = 'none';
    fileInput.name = button.getAttribute('ms-code-file_uploader');
    fileInput.accept = button.getAttribute('ms-code-file_types');

    document.body.appendChild(fileInput);

    button.addEventListener('click', function (e) {
      e.preventDefault();
      fileInput.click();
    });

    fileInput.addEventListener('change', function () {
      const fileName = fileInput.files[0].name;
      button.querySelector('div').textContent = fileName;
    });
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #99 v0.1 💙 CUSTOM FILE UPLOAD INPUT -->
<script>
document.addEventListener('DOMContentLoaded', function () {
  const uploadButtons = document.querySelectorAll('[ms-code-file_uploader]');

  uploadButtons.forEach(button => {
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.style.display = 'none';
    fileInput.name = button.getAttribute('ms-code-file_uploader');
    fileInput.accept = button.getAttribute('ms-code-file_types');

    document.body.appendChild(fileInput);

    button.addEventListener('click', function (e) {
      e.preventDefault();
      fileInput.click();
    });

    fileInput.addEventListener('change', function () {
      const fileName = fileInput.files[0].name;
      button.querySelector('div').textContent = fileName;
    });
  });
});
</script>
Ansicht Memberscript
Bedingte Sichtbarkeit

#98 - Altersfreigabe

Die Benutzer müssen ihr Alter bestätigen, bevor sie fortfahren.


<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const form = document.querySelector('form[ms-code-age-gate]');
  const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
  const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
  const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
  const backButton = document.querySelector('a[ms-code-age-gate="back"]');
  const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
  const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');

  if (localStorage.getItem('ageVerified') === 'true') {
    wrapper.remove();
    return;
  }

  backButton.addEventListener('click', (e) => {
    e.preventDefault();
    window.history.back();
  });

  const inputs = [dayInput, monthInput, yearInput];

  inputs.forEach((input, index) => {
    input.addEventListener('keyup', (e) => {
      const maxChars = input === yearInput ? 4 : 2;
      let value = e.target.value;

      if (input === dayInput && value.length === maxChars) {
        value = value > 31 ? '31' : value.padStart(2, '0');
      } else if (input === monthInput && value.length === maxChars) {
        value = value > 12 ? '12' : value.padStart(2, '0');
      }
      e.target.value = value;

      if (value.length === maxChars) {
        const nextInput = inputs[index + 1];
        if (nextInput) {
          nextInput.focus();
        }
      }
    });
  });

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
    const currentDate = new Date();
    const ageDifference = new Date(currentDate - enteredDate);
    const age = Math.abs(ageDifference.getUTCFullYear() - 1970);

    const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);

    if (age >= ageLimit) {
      console.log('Age verified.');
      errorDiv.style.display = 'none';
      localStorage.setItem('ageVerified', 'true');
      wrapper.remove();
    } else {
      console.log('Age verification failed, user is under the age limit.');
      errorDiv.style.display = 'block';
    }
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const form = document.querySelector('form[ms-code-age-gate]');
  const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
  const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
  const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
  const backButton = document.querySelector('a[ms-code-age-gate="back"]');
  const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
  const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');

  if (localStorage.getItem('ageVerified') === 'true') {
    wrapper.remove();
    return;
  }

  backButton.addEventListener('click', (e) => {
    e.preventDefault();
    window.history.back();
  });

  const inputs = [dayInput, monthInput, yearInput];

  inputs.forEach((input, index) => {
    input.addEventListener('keyup', (e) => {
      const maxChars = input === yearInput ? 4 : 2;
      let value = e.target.value;

      if (input === dayInput && value.length === maxChars) {
        value = value > 31 ? '31' : value.padStart(2, '0');
      } else if (input === monthInput && value.length === maxChars) {
        value = value > 12 ? '12' : value.padStart(2, '0');
      }
      e.target.value = value;

      if (value.length === maxChars) {
        const nextInput = inputs[index + 1];
        if (nextInput) {
          nextInput.focus();
        }
      }
    });
  });

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
    const currentDate = new Date();
    const ageDifference = new Date(currentDate - enteredDate);
    const age = Math.abs(ageDifference.getUTCFullYear() - 1970);

    const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);

    if (age >= ageLimit) {
      console.log('Age verified.');
      errorDiv.style.display = 'none';
      localStorage.setItem('ageVerified', 'true');
      wrapper.remove();
    } else {
      console.log('Age verification failed, user is under the age limit.');
      errorDiv.style.display = 'block';
    }
  });
});
</script>
Ansicht Memberscript
Integration

#Nr. 97 - Dateien in S3 Bucket hochladen

Erlauben Sie Uploads zu einem S3-Bucket aus einem Webflow-Formular.


<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
    document.addEventListener('DOMContentLoaded', function() {
        function generateUUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
            input.addEventListener('change', function() {
                if (this.files.length > 0) {
                    const file = this.files[0];
                    const uuid = generateUUID();
                    const extension = file.name.split('.').pop();
                    const newFileName = `${uuid}.${extension}`;
                    const wrapper = this.closest('div[ms-code-s3-wrapper]');
                    const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');

                    s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);

                    const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));

                    fetch(apiGatewayUrl, {
                        method: 'PUT',
                        body: file,
                        headers: { 'Content-Type': file.type }
                    })
                    .then(response => {
                        if (response.status !== 200) {
                            throw new Error(`Upload failed with status: ${response.status}`);
                        }
                        console.log('File uploaded successfully:', newFileName);
                    })
                    .catch(error => {
                        console.error('Upload error:', error);
                        alert('Upload failed.');
                    });
                }
            });
        });

        document.querySelectorAll('form').forEach(form => {
            form.addEventListener('submit', function(event) {
                const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
                const allUrlsSet = s3Inputs.every(input => input.value);

                if (!allUrlsSet) {
                    event.preventDefault();
                    alert('Please wait for all files to finish uploading before submitting.');
                }
            });
        });
    });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
    document.addEventListener('DOMContentLoaded', function() {
        function generateUUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
            input.addEventListener('change', function() {
                if (this.files.length > 0) {
                    const file = this.files[0];
                    const uuid = generateUUID();
                    const extension = file.name.split('.').pop();
                    const newFileName = `${uuid}.${extension}`;
                    const wrapper = this.closest('div[ms-code-s3-wrapper]');
                    const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');

                    s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);

                    const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));

                    fetch(apiGatewayUrl, {
                        method: 'PUT',
                        body: file,
                        headers: { 'Content-Type': file.type }
                    })
                    .then(response => {
                        if (response.status !== 200) {
                            throw new Error(`Upload failed with status: ${response.status}`);
                        }
                        console.log('File uploaded successfully:', newFileName);
                    })
                    .catch(error => {
                        console.error('Upload error:', error);
                        alert('Upload failed.');
                    });
                }
            });
        });

        document.querySelectorAll('form').forEach(form => {
            form.addEventListener('submit', function(event) {
                const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
                const allUrlsSet = s3Inputs.every(input => input.value);

                if (!allUrlsSet) {
                    event.preventDefault();
                    alert('Please wait for all files to finish uploading before submitting.');
                }
            });
        });
    });
</script>
Ansicht Memberscript
Benutzerdefinierte Felder

#96 - Strichliste speichern

Erstellen und aktualisieren Sie eine Zählung/Tabelle, die in einem benutzerdefinierten Feld gespeichert wird!


<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;
    const addButtons = document.querySelectorAll("[ms-code-add-tally]");

    addButtons.forEach(button => {
        button.addEventListener("click", async () => {
            const tallyKey = button.getAttribute("ms-code-add-tally");
            const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
           
            if(tallyText){
                let currentCount = parseInt(tallyText.textContent, 10);
                currentCount += 1;
                tallyText.textContent = currentCount;

                // Store the new tally count to Memberstack
                let newFields = {};
                newFields[tallyKey] = currentCount;
                await memberstack.updateMember({customFields: newFields});
           }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;
    const addButtons = document.querySelectorAll("[ms-code-add-tally]");

    addButtons.forEach(button => {
        button.addEventListener("click", async () => {
            const tallyKey = button.getAttribute("ms-code-add-tally");
            const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
           
            if(tallyText){
                let currentCount = parseInt(tallyText.textContent, 10);
                currentCount += 1;
                tallyText.textContent = currentCount;

                // Store the new tally count to Memberstack
                let newFields = {};
                newFields[tallyKey] = currentCount;
                await memberstack.updateMember({customFields: newFields});
           }
        });
    });
});
</script>
Ansicht Memberscript
UX

#95 - Konfetti auf Klick

Lass lustiges Konfetti auf Klick fliegen!


<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
    const confettiElems = document.querySelectorAll("[ms-code-confetti]");

    confettiElems.forEach(item => {
        item.addEventListener("click", () => {
            const effect = item.getAttribute("ms-code-confetti");
            switch (effect) {
                case "falling":
                    const makeFall = () => {
                        confetti({
                            particleCount: 100,
                            startVelocity: 30,
                            spread: 360,
                            origin: { x: Math.random(), y: 0 },
                            colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
                        });
                    }
                    setInterval(makeFall, 2000);
                    break;
                case "single":
                    confetti({
                        particleCount: 1,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: Math.random() }
                    });
                    break;
                case "sides":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: 0.5 }
                    });
                    break;
                case "explosions":
                    confetti({
                        particleCount: 100,
                        startVelocity: 50,
                        spread: 360
                    });
                    break;
                case "bottom":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: 0.5, y: 1 }
                    });
                    break;
                default:
                    console.log("Unknown confetti effect");
            }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
    const confettiElems = document.querySelectorAll("[ms-code-confetti]");

    confettiElems.forEach(item => {
        item.addEventListener("click", () => {
            const effect = item.getAttribute("ms-code-confetti");
            switch (effect) {
                case "falling":
                    const makeFall = () => {
                        confetti({
                            particleCount: 100,
                            startVelocity: 30,
                            spread: 360,
                            origin: { x: Math.random(), y: 0 },
                            colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
                        });
                    }
                    setInterval(makeFall, 2000);
                    break;
                case "single":
                    confetti({
                        particleCount: 1,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: Math.random() }
                    });
                    break;
                case "sides":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: 0.5 }
                    });
                    break;
                case "explosions":
                    confetti({
                        particleCount: 100,
                        startVelocity: 50,
                        spread: 360
                    });
                    break;
                case "bottom":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: 0.5, y: 1 }
                    });
                    break;
                default:
                    console.log("Unknown confetti effect");
            }
        });
    });
});
</script>
Ansicht Memberscript
UX

#94 - href-Attribut setzen

Dynamisches Setzen eines Links über das Webflow CMS (oder etwas anderes)


<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
  var elements = document.querySelectorAll('[ms-code-href]');
  elements.forEach(function(element) {
    var url = element.getAttribute('ms-code-href');
    element.setAttribute('href', url);
  });
};
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
  var elements = document.querySelectorAll('[ms-code-href]');
  elements.forEach(function(element) {
    var url = element.getAttribute('ms-code-href');
    element.setAttribute('href', url);
  });
};
</script>
Ansicht Memberscript
Benutzerdefinierte Felder
UX

#93 - Erzwingen Sie gültige URLs in Formulareingaben

Konvertieren Sie alle Eingaben automatisch in eine gültige URL.


<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');

// Add event listener to each form field
formFields.forEach((field) => {
  field.addEventListener('input', convertToLink);
});

// Function to convert input to a link
function convertToLink(event) {
  const input = event.target;

  // Get user input
  const userInput = input.value.trim();

  // Check if input starts with http:// or https://
  if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
    input.value = userInput; // No conversion needed for valid links
  } else {
    input.value = `http://${userInput}`; // Prepend http:// for simplicity
  }
}
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');

// Add event listener to each form field
formFields.forEach((field) => {
  field.addEventListener('input', convertToLink);
});

// Function to convert input to a link
function convertToLink(event) {
  const input = event.target;

  // Get user input
  const userInput = input.value.trim();

  // Check if input starts with http:// or https://
  if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
    input.value = userInput; // No conversion needed for valid links
  } else {
    input.value = `http://${userInput}`; // Prepend http:// for simplicity
  }
}
</script>
Ansicht Memberscript
UX

#Nr. 92 - Seitenwechsel bei Klick

Ändern Sie die aktuelle Seiten-URL, wenn Sie auf ein beliebiges Element klicken.


<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
    let target = event.target;

    // Traverse up the DOM tree to find an element with the ms-code-navigate attribute
    while (target && !target.getAttribute('ms-code-navigate')) {
        target = target.parentElement;
    }

    // If we found an element with the ms-code-navigate attribute
    if (target) {
        const navigateUrl = target.getAttribute('ms-code-navigate');

        if (navigateUrl) {
            event.preventDefault();
            // Always open in a new tab
            window.open(navigateUrl, '_blank');
        }
    }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
    let target = event.target;

    // Traverse up the DOM tree to find an element with the ms-code-navigate attribute
    while (target && !target.getAttribute('ms-code-navigate')) {
        target = target.parentElement;
    }

    // If we found an element with the ms-code-navigate attribute
    if (target) {
        const navigateUrl = target.getAttribute('ms-code-navigate');

        if (navigateUrl) {
            event.preventDefault();
            // Always open in a new tab
            window.open(navigateUrl, '_blank');
        }
    }
});
</script>
Ansicht Memberscript
Wir konnten keine Skripte für diese Suche finden... bitte versuchen Sie es erneut.
Slack

Brauchen Sie Hilfe mit MemberScripts? Treten Sie unserer Slack-Community mit über 5.500 Mitgliedern bei! 🙌

MemberScripts sind eine Community-Ressource von Memberstack - wenn du Hilfe brauchst, damit sie mit deinem Projekt funktionieren, melde dich bitte im Memberstack 2.0 Slack an und bitte um Hilfe!

Unserem Slack beitreten
Schaukasten

Entdecken Sie echte Unternehmen, die mit Memberstack erfolgreich waren

Verlassen Sie sich nicht nur auf unser Wort - schauen Sie sich die Unternehmen aller Größen an, die sich auf Memberstack für ihre Authentifizierung und Zahlungen verlassen.

Alle Erfolgsgeschichten anzeigen
Auch Webflow verwendet Memberstack!
Mit dem Bau beginnen

Bauen Sie Ihre Träume

Memberstack ist 100% kostenlos, bis Sie bereit sind, zu starten - worauf warten Sie also noch? Erstellen Sie Ihre erste App und beginnen Sie noch heute mit der Entwicklung.