Composants
Modèles
Attributs
Intégrations
Testeur de site
Code personnalisé

Scripts des membres

Une solution basée sur les attributs pour ajouter des fonctionnalités à votre site Webflow.
Il suffit de copier un peu de code, d'ajouter quelques attributs et le tour est joué.

Nous vous remercions ! Votre demande a bien été reçue !
Oups ! Un problème s'est produit lors de l'envoi du formulaire.
Besoin d'aide avec MemberScripts ?

Tous les clients de Memberstack peuvent demander de l'aide dans le Slack 2.0. Veuillez noter qu'il ne s'agit pas de fonctionnalités officielles et que le support ne peut être garanti.

UX
Marketing

#141 - Démarrer l'intégration de YouTube à un moment précis

Activez les liens partageables et lancez la lecture des vidéos à une heure donnée.


<!-- 💙 MEMBERSCRIPT #141 v0.1 💙 - START YOUTUBE VIDEO AT SPECIFIC TIME -->
<script>
  (function() {
    // Function to get URL parameters
    function getUrlParameter(name) {
      name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
      var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
      var results = regex.exec(location.search);
      return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    // Function to update YouTube embed src within Embedly iframe
    function updateYouTubeEmbed(embedly_iframe, startTime) {
      var embedly_src = embedly_iframe.src;
      var youtube_src_match = embedly_src.match(/src=([^&]+)/);
      if (youtube_src_match) {
        var youtube_src = decodeURIComponent(youtube_src_match[1]);
        var new_youtube_src = youtube_src.replace(/(\?|&)start=\d+/, '');
        new_youtube_src += (new_youtube_src.includes('?') ? '&' : '?') + 'start=' + startTime;
        var new_embedly_src = embedly_src.replace(/src=([^&]+)/, 'src=' + encodeURIComponent(new_youtube_src));
        embedly_iframe.src = new_embedly_src;
      }
    }

    // Get all elements with ms-code-yt-start attribute
    var elements = document.querySelectorAll('[ms-code-yt-start]');

    elements.forEach(function(element) {
      var paramName = element.getAttribute('ms-code-yt-start');
      var startTime = getUrlParameter(paramName);
      var defaultStartTime = element.getAttribute('ms-code-yt-start-default');

      // If no URL parameter, use the default start time (if specified)
      if (!startTime && defaultStartTime) {
        startTime = defaultStartTime;
      }

      // If we have a start time (either from URL or default), update the embed
      if (startTime) {
        var iframe = element.querySelector('iframe.embedly-embed');
        if (iframe) {
          updateYouTubeEmbed(iframe, startTime);
        }
      }
    });
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #141 v0.1 💙 - START YOUTUBE VIDEO AT SPECIFIC TIME -->
<script>
  (function() {
    // Function to get URL parameters
    function getUrlParameter(name) {
      name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
      var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
      var results = regex.exec(location.search);
      return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    // Function to update YouTube embed src within Embedly iframe
    function updateYouTubeEmbed(embedly_iframe, startTime) {
      var embedly_src = embedly_iframe.src;
      var youtube_src_match = embedly_src.match(/src=([^&]+)/);
      if (youtube_src_match) {
        var youtube_src = decodeURIComponent(youtube_src_match[1]);
        var new_youtube_src = youtube_src.replace(/(\?|&)start=\d+/, '');
        new_youtube_src += (new_youtube_src.includes('?') ? '&' : '?') + 'start=' + startTime;
        var new_embedly_src = embedly_src.replace(/src=([^&]+)/, 'src=' + encodeURIComponent(new_youtube_src));
        embedly_iframe.src = new_embedly_src;
      }
    }

    // Get all elements with ms-code-yt-start attribute
    var elements = document.querySelectorAll('[ms-code-yt-start]');

    elements.forEach(function(element) {
      var paramName = element.getAttribute('ms-code-yt-start');
      var startTime = getUrlParameter(paramName);
      var defaultStartTime = element.getAttribute('ms-code-yt-start-default');

      // If no URL parameter, use the default start time (if specified)
      if (!startTime && defaultStartTime) {
        startTime = defaultStartTime;
      }

      // If we have a start time (either from URL or default), update the embed
      if (startTime) {
        var iframe = element.querySelector('iframe.embedly-embed');
        if (iframe) {
          updateYouTubeEmbed(iframe, startTime);
        }
      }
    });
  })();
</script>
Voir le Memberscript
Flux personnalisés
UX

#140 - Confirmer la concordance des entrées

Vérifier une entrée avant d'autoriser la soumission - idéal pour éviter les informations erronées !


<!-- 💙 MEMBERSCRIPT #140 v0.1 💙 - CONFIRM MATCHING INPUTS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form');

    forms.forEach(form => {
      const inputPairs = form.querySelectorAll('[ms-code-conf-input]');
      const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');

      if (!submitButton) {
        console.error('Submit button not found in the form');
        return;
      }

      function validateForm() {
        let fieldsMatch = true;

        inputPairs.forEach(input => {
          const confType = input.getAttribute('ms-code-conf-input');
          const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);
          const errorElement = form.querySelector(`[ms-code-conf-error="${confType}"]`);

          if (confirmInput && errorElement) {
            if (input.value && confirmInput.value) {
              if (input.value !== confirmInput.value) {
                errorElement.style.removeProperty('display');
                fieldsMatch = false;
              } else {
                errorElement.style.display = 'none';
              }
            } else {
              errorElement.style.display = 'none';
            }
          }
        });

        if (fieldsMatch) {
          submitButton.style.removeProperty('pointer-events');
          submitButton.disabled = false;
        } else {
          submitButton.style.pointerEvents = 'none';
          submitButton.disabled = true;
        }
      }

      inputPairs.forEach(input => {
        const confType = input.getAttribute('ms-code-conf-input');
        const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);

        if (confirmInput) {
          input.addEventListener('input', validateForm);
          confirmInput.addEventListener('input', validateForm);
        }
      });

      // Initial validation
      validateForm();

      // Extra precaution: prevent form submission if fields don't match
      form.addEventListener('submit', function(event) {
        if (submitButton.disabled) {
          event.preventDefault();
          console.log('Form submission blocked: Fields do not match');
        }
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #140 v0.1 💙 - CONFIRM MATCHING INPUTS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form');

    forms.forEach(form => {
      const inputPairs = form.querySelectorAll('[ms-code-conf-input]');
      const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');

      if (!submitButton) {
        console.error('Submit button not found in the form');
        return;
      }

      function validateForm() {
        let fieldsMatch = true;

        inputPairs.forEach(input => {
          const confType = input.getAttribute('ms-code-conf-input');
          const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);
          const errorElement = form.querySelector(`[ms-code-conf-error="${confType}"]`);

          if (confirmInput && errorElement) {
            if (input.value && confirmInput.value) {
              if (input.value !== confirmInput.value) {
                errorElement.style.removeProperty('display');
                fieldsMatch = false;
              } else {
                errorElement.style.display = 'none';
              }
            } else {
              errorElement.style.display = 'none';
            }
          }
        });

        if (fieldsMatch) {
          submitButton.style.removeProperty('pointer-events');
          submitButton.disabled = false;
        } else {
          submitButton.style.pointerEvents = 'none';
          submitButton.disabled = true;
        }
      }

      inputPairs.forEach(input => {
        const confType = input.getAttribute('ms-code-conf-input');
        const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);

        if (confirmInput) {
          input.addEventListener('input', validateForm);
          confirmInput.addEventListener('input', validateForm);
        }
      });

      // Initial validation
      validateForm();

      // Extra precaution: prevent form submission if fields don't match
      form.addEventListener('submit', function(event) {
        if (submitButton.disabled) {
          event.preventDefault();
          console.log('Form submission blocked: Fields do not match');
        }
      });
    });
  });
</script>
Voir le Memberscript
UX

#139 - Réinitialiser le formulaire après l'avoir soumis

Créer un bouton dans l'état de réussite du formulaire qui permet de le soumettre à nouveau.


<!-- 💙 MEMBERSCRIPT #139 v0.1 💙 - RESET FORM BUTTON -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Find all "Add another" buttons
    const resetButtons = document.querySelectorAll('[ms-code-reset-form]');

    // Add click event listener to each button
    resetButtons.forEach(function(resetButton) {
      resetButton.addEventListener('click', function(e) {
        e.preventDefault(); // Prevent default link behavior

        // Find the closest form and success message elements
        const formWrapper = this.closest('.w-form');
        const form = formWrapper.querySelector('form');
        const successMessage = formWrapper.querySelector('.w-form-done');

        // Reset the form
        form.reset();

        // Hide the success message
        successMessage.style.display = 'none';

        // Show the form
        form.style.display = 'block';
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #139 v0.1 💙 - RESET FORM BUTTON -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Find all "Add another" buttons
    const resetButtons = document.querySelectorAll('[ms-code-reset-form]');

    // Add click event listener to each button
    resetButtons.forEach(function(resetButton) {
      resetButton.addEventListener('click', function(e) {
        e.preventDefault(); // Prevent default link behavior

        // Find the closest form and success message elements
        const formWrapper = this.closest('.w-form');
        const form = formWrapper.querySelector('form');
        const successMessage = formWrapper.querySelector('.w-form-done');

        // Reset the form
        form.reset();

        // Hide the success message
        successMessage.style.display = 'none';

        // Show the form
        form.style.display = 'block';
      });
    });
  });
</script>
Voir le Memberscript
UX

#138 - Décalage du défilement du lien d'ancrage

Correction du problème avec les liens d'ancrage et les barres de navigation collantes/fixées dans Webflow.


<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
  // Disable Webflow's built-in smooth scrolling
  var Webflow = Webflow || [];
  Webflow.push(function() {
    $(function() { 
      $(document).off('click.wf-scroll');
    });
  });

  // Smooth scroll implementation with customizable settings
  (function() {
    // Customizable settings
    const SCROLL_SETTINGS = {
      duration: 1000, // in milliseconds
      easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
    };

    const EASING_FUNCTIONS = {
      linear: t => t,
      easeInQuad: t => t * t,
      easeOutQuad: t => t * (2 - t),
      easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
      easeInCubic: t => t * t * t,
      easeOutCubic: t => (--t) * t * t + 1,
      easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
    };

    function getOffset() {
      const navbar = document.querySelector('[ms-code-scroll-offset]');
      if (!navbar) return 0;
      const navbarHeight = navbar.offsetHeight;
      const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
      return navbarHeight + customOffset;
    }

    function smoothScroll(target) {
      const startPosition = window.pageYOffset;
      const offset = getOffset();
      const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
      const distance = targetPosition - startPosition;
      let startTime = null;

      function animation(currentTime) {
        if (startTime === null) startTime = currentTime;
        const timeElapsed = currentTime - startTime;
        const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
        const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
        window.scrollTo(0, startPosition + distance * easeProgress);
        if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
      }

      requestAnimationFrame(animation);
    }

    function handleClick(e) {
      const href = e.currentTarget.getAttribute('href');
      if (href.startsWith('#')) {
        e.preventDefault();
        const target = document.getElementById(href.slice(1));
        if (target) smoothScroll(target);
      }
    }

    function handleHashChange() {
      if (window.location.hash) {
        const target = document.getElementById(window.location.hash.slice(1));
        if (target) {
          setTimeout(() => smoothScroll(target), 0);
        }
      }
    }

    function init() {
      document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', handleClick);
      });
      window.addEventListener('hashchange', handleHashChange);
      handleHashChange(); // Handle initial hash on page load
    }

    document.addEventListener('DOMContentLoaded', init);
    window.Webflow && window.Webflow.push(init);
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
  // Disable Webflow's built-in smooth scrolling
  var Webflow = Webflow || [];
  Webflow.push(function() {
    $(function() { 
      $(document).off('click.wf-scroll');
    });
  });

  // Smooth scroll implementation with customizable settings
  (function() {
    // Customizable settings
    const SCROLL_SETTINGS = {
      duration: 1000, // in milliseconds
      easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
    };

    const EASING_FUNCTIONS = {
      linear: t => t,
      easeInQuad: t => t * t,
      easeOutQuad: t => t * (2 - t),
      easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
      easeInCubic: t => t * t * t,
      easeOutCubic: t => (--t) * t * t + 1,
      easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
    };

    function getOffset() {
      const navbar = document.querySelector('[ms-code-scroll-offset]');
      if (!navbar) return 0;
      const navbarHeight = navbar.offsetHeight;
      const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
      return navbarHeight + customOffset;
    }

    function smoothScroll(target) {
      const startPosition = window.pageYOffset;
      const offset = getOffset();
      const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
      const distance = targetPosition - startPosition;
      let startTime = null;

      function animation(currentTime) {
        if (startTime === null) startTime = currentTime;
        const timeElapsed = currentTime - startTime;
        const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
        const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
        window.scrollTo(0, startPosition + distance * easeProgress);
        if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
      }

      requestAnimationFrame(animation);
    }

    function handleClick(e) {
      const href = e.currentTarget.getAttribute('href');
      if (href.startsWith('#')) {
        e.preventDefault();
        const target = document.getElementById(href.slice(1));
        if (target) smoothScroll(target);
      }
    }

    function handleHashChange() {
      if (window.location.hash) {
        const target = document.getElementById(window.location.hash.slice(1));
        if (target) {
          setTimeout(() => smoothScroll(target), 0);
        }
      }
    }

    function init() {
      document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', handleClick);
      });
      window.addEventListener('hashchange', handleHashChange);
      handleHashChange(); // Handle initial hash on page load
    }

    document.addEventListener('DOMContentLoaded', init);
    window.Webflow && window.Webflow.push(init);
  })();
</script>
Voir le Memberscript
UX

#137 - Afficher le nom du pays des visiteurs

Remplacer le texte par le pays dans lequel se trouve l'utilisateur en fonction de son adresse IP.


<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        if (data.country_name) {
          const countryElements = document.querySelectorAll('[ms-code-display-country]');
          countryElements.forEach(element => {
            element.textContent = data.country_name;
          });
        }
      })
      .catch(error => {
        console.error('Error fetching country:', error);
      });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        if (data.country_name) {
          const countryElements = document.querySelectorAll('[ms-code-display-country]');
          countryElements.forEach(element => {
            element.textContent = data.country_name;
          });
        }
      })
      .catch(error => {
        console.error('Error fetching country:', error);
      });
  });
</script>
Voir le Memberscript
UX

#136 - Supprimer le chemin de section de l'URL

Lorsque l'on navigue vers une section, le chemin du lien d'ancrage est supprimé.


<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Check if there's a hash in the URL
    if (window.location.hash) {
        // Get the target element
        const targetId = window.location.hash.substring(1);
        const targetElement = document.getElementById(targetId);

        if (targetElement) {
            // Scroll to the target element
            targetElement.scrollIntoView({behavior: 'smooth'});

            // Remove the hash after a short delay (to allow scrolling to complete)
            setTimeout(function() {
                history.pushState("", document.title, window.location.pathname + window.location.search);
            }, 100);
        }
    }

    // Add click event listeners to all internal links
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function(e) {
            e.preventDefault();

            const targetId = this.getAttribute('href').substring(1);
            const targetElement = document.getElementById(targetId);

            if (targetElement) {
                targetElement.scrollIntoView({behavior: 'smooth'});

                // Remove the hash after a short delay (to allow scrolling to complete)
                setTimeout(function() {
                    history.pushState("", document.title, window.location.pathname + window.location.search);
                }, 100);
            }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Check if there's a hash in the URL
    if (window.location.hash) {
        // Get the target element
        const targetId = window.location.hash.substring(1);
        const targetElement = document.getElementById(targetId);

        if (targetElement) {
            // Scroll to the target element
            targetElement.scrollIntoView({behavior: 'smooth'});

            // Remove the hash after a short delay (to allow scrolling to complete)
            setTimeout(function() {
                history.pushState("", document.title, window.location.pathname + window.location.search);
            }, 100);
        }
    }

    // Add click event listeners to all internal links
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function(e) {
            e.preventDefault();

            const targetId = this.getAttribute('href').substring(1);
            const targetElement = document.getElementById(targetId);

            if (targetElement) {
                targetElement.scrollIntoView({behavior: 'smooth'});

                // Remove the hash after a short delay (to allow scrolling to complete)
                setTimeout(function() {
                    history.pushState("", document.title, window.location.pathname + window.location.search);
                }, 100);
            }
        });
    });
});
</script>
Voir le Memberscript
Flux personnalisés

#135 - Redirection basée sur une valeur sélectionnée

Définir dynamiquement la redirection du formulaire en fonction de la sélection de l'utilisateur.


<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
  (function() {
    'use strict';

    function initDropdownRedirect() {
      const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
      if (!dropdown) return;

      const form = dropdown.closest('form');
      if (!form) return;

      function updateRedirect() {
        const selectedValue = dropdown.value;
        form.setAttribute('redirect', selectedValue);
        form.setAttribute('data-redirect', selectedValue);
      }

      function handleSubmit(event) {
        event.preventDefault();
        const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
        if (redirectUrl) {
          setTimeout(() => {
            window.location.href = redirectUrl;
          }, 500); // Delay redirect by 500 milliseconds
        } else {
          form.submit(); // Fall back to normal form submission if no redirect is set
        }
      }


      dropdown.addEventListener('change', updateRedirect);
      form.addEventListener('submit', handleSubmit);

      // Initialize redirect on page load
      updateRedirect();
    }

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

<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
  (function() {
    'use strict';

    function initDropdownRedirect() {
      const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
      if (!dropdown) return;

      const form = dropdown.closest('form');
      if (!form) return;

      function updateRedirect() {
        const selectedValue = dropdown.value;
        form.setAttribute('redirect', selectedValue);
        form.setAttribute('data-redirect', selectedValue);
      }

      function handleSubmit(event) {
        event.preventDefault();
        const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
        if (redirectUrl) {
          setTimeout(() => {
            window.location.href = redirectUrl;
          }, 500); // Delay redirect by 500 milliseconds
        } else {
          form.submit(); // Fall back to normal form submission if no redirect is set
        }
      }


      dropdown.addEventListener('change', updateRedirect);
      form.addEventListener('submit', handleSubmit);

      // Initialize redirect on page load
      updateRedirect();
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initDropdownRedirect);
    } else {
      initDropdownRedirect();
    }
  })();
</script>
Voir le Memberscript
UX

#134 - Défilement vers le haut lors d'un changement d'onglet

Lorsque l'onglet est modifié, la page défile jusqu'en haut de la section de l'onglet.


<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all tab containers with the ms-code-tab-scroll-top attribute
    const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');

    tabContainers.forEach(container => {
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');

      tabLinks.forEach(link => {
        link.addEventListener('click', function(e) {
          // Small delay to ensure the new tab content is rendered
          setTimeout(() => {
            // Find the active tab pane within this container
            const activePane = container.querySelector('.w-tab-pane.w--tab-active');

            if (activePane) {
              // Calculate the new scroll position
              const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;

              // Scroll to the new position
              window.scrollTo({
                top: newScrollPosition,
                behavior: 'smooth'
              });
            }
          }, 50); // 50ms delay, adjust if needed
        });
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all tab containers with the ms-code-tab-scroll-top attribute
    const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');

    tabContainers.forEach(container => {
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');

      tabLinks.forEach(link => {
        link.addEventListener('click', function(e) {
          // Small delay to ensure the new tab content is rendered
          setTimeout(() => {
            // Find the active tab pane within this container
            const activePane = container.querySelector('.w-tab-pane.w--tab-active');

            if (activePane) {
              // Calculate the new scroll position
              const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;

              // Scroll to the new position
              window.scrollTo({
                top: newScrollPosition,
                behavior: 'smooth'
              });
            }
          }, 50); // 50ms delay, adjust if needed
        });
      });
    });
  });
</script>
Voir le Memberscript
Sécurité

#133 - Filigrane automatique des images

Ajoutez facilement un filigrane aux images de votre site Webflow.


<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
  function addWatermarkToImages() {
    const images = document.querySelectorAll('img[ms-code-watermark]');

    images.forEach(img => {
      img.crossOrigin = "Anonymous";  // This allows us to work with images from other domains
      img.onload = function() {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        // Set canvas size to match the image
        canvas.width = img.width;
        canvas.height = img.height;

        // Draw the original image onto the canvas
        ctx.drawImage(img, 0, 0, img.width, img.height);

        // Get watermark text from attribute
        const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';

        // Add watermark
        ctx.font = `${img.width / 20}px Arial`;  // Adjust font size based on image width
        ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // Rotate and draw the watermark text
        ctx.save();
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(-Math.PI / 4);  // Rotate 45 degrees
        ctx.fillText(watermarkText, 0, 0);
        ctx.restore();

        // Preserve the original image's classes and other attributes
        canvas.className = img.className;
        for (let i = 0; i < img.attributes.length; i++) {
          const attr = img.attributes[i];
          if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
            canvas.setAttribute(attr.name, attr.value);
          }
        }

        // Replace the original image with the watermarked canvas
        img.parentNode.replaceChild(canvas, img);
      };

      // Trigger onload event (in case the image is already loaded)
      if (img.complete) {
        img.onload();
      }
    });
  }

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

<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
  function addWatermarkToImages() {
    const images = document.querySelectorAll('img[ms-code-watermark]');

    images.forEach(img => {
      img.crossOrigin = "Anonymous";  // This allows us to work with images from other domains
      img.onload = function() {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        // Set canvas size to match the image
        canvas.width = img.width;
        canvas.height = img.height;

        // Draw the original image onto the canvas
        ctx.drawImage(img, 0, 0, img.width, img.height);

        // Get watermark text from attribute
        const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';

        // Add watermark
        ctx.font = `${img.width / 20}px Arial`;  // Adjust font size based on image width
        ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // Rotate and draw the watermark text
        ctx.save();
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(-Math.PI / 4);  // Rotate 45 degrees
        ctx.fillText(watermarkText, 0, 0);
        ctx.restore();

        // Preserve the original image's classes and other attributes
        canvas.className = img.className;
        for (let i = 0; i < img.attributes.length; i++) {
          const attr = img.attributes[i];
          if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
            canvas.setAttribute(attr.name, attr.value);
          }
        }

        // Replace the original image with the watermarked canvas
        img.parentNode.replaceChild(canvas, img);
      };

      // Trigger onload event (in case the image is already loaded)
      if (img.complete) {
        img.onload();
      }
    });
  }

  // Run the function when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', addWatermarkToImages);
</script>
Voir le Memberscript
Accessibilité
Modaux

#132 - Masquer des éléments avec la touche Escape

Ajoutez un attribut et lorsque vous cliquez sur la touche esc, l'élément sera affiché en mode "none".


<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
  document.addEventListener('keydown', function(event) {
    // Check if the pressed key is ESC (key code 27)
    if (event.key === 'Escape' || event.keyCode === 27) {
      // Find all elements with the attribute ms-code-close-esc
      const elements = document.querySelectorAll('[ms-code-close-esc]');

      // Loop through the elements and set their display to 'none'
      elements.forEach(function(element) {
        element.style.display = 'none';
      });
    }
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
  document.addEventListener('keydown', function(event) {
    // Check if the pressed key is ESC (key code 27)
    if (event.key === 'Escape' || event.keyCode === 27) {
      // Find all elements with the attribute ms-code-close-esc
      const elements = document.querySelectorAll('[ms-code-close-esc]');

      // Loop through the elements and set their display to 'none'
      elements.forEach(function(element) {
        element.style.display = 'none';
      });
    }
  });
</script>
Voir le Memberscript
Champs personnalisés

#131 - Additionner les entrées numériques

Prendre la valeur des entrées numériques et l'afficher dans une valeur d'entrée ou dans une zone de texte.


<!-- 💙 MEMBERSCRIPT #131 v0.1 💙 - CALCULATE NUMBER INPUTS -->
<script>
  // Function to initialize the counter functionality
  function initializeCounter() {
    const counters = {};

    // Find all elements with ms-code-show-number attribute
    document.querySelectorAll('[ms-code-show-number]').forEach(el => {
      const counterName = el.getAttribute('ms-code-show-number');
      if (!counters[counterName]) {
        counters[counterName] = { total: 0, displays: [] };
      }
      counters[counterName].displays.push(el);
    });

    // Find all input elements with ms-code-add-number attribute
    document.querySelectorAll('input[ms-code-add-number]').forEach(input => {
      const counterName = input.getAttribute('ms-code-add-number');
      if (counters[counterName]) {
        input.addEventListener('input', updateCounter);
      }
    });

    // Function to update counter when input changes
    function updateCounter(event) {
      const input = event.target;
      const counterName = input.getAttribute('ms-code-add-number');
      const counter = counters[counterName];

      if (counter) {
        counter.total = 0;
        document.querySelectorAll(`input[ms-code-add-number="${counterName}"]`).forEach(input => {
          counter.total += parseInt(input.value) || 0;
        });

        counter.displays.forEach(display => {
          if (display.tagName === 'INPUT') {
            display.value = counter.total;
          } else {
            display.textContent = counter.total;
          }
        });
      }
    }

    // Initial update for all counters
    Object.keys(counters).forEach(counterName => {
      const input = document.querySelector(`input[ms-code-add-number="${counterName}"]`);
      if (input) {
        input.dispatchEvent(new Event('input'));
      }
    });
  }

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

<!-- 💙 MEMBERSCRIPT #131 v0.1 💙 - CALCULATE NUMBER INPUTS -->
<script>
  // Function to initialize the counter functionality
  function initializeCounter() {
    const counters = {};

    // Find all elements with ms-code-show-number attribute
    document.querySelectorAll('[ms-code-show-number]').forEach(el => {
      const counterName = el.getAttribute('ms-code-show-number');
      if (!counters[counterName]) {
        counters[counterName] = { total: 0, displays: [] };
      }
      counters[counterName].displays.push(el);
    });

    // Find all input elements with ms-code-add-number attribute
    document.querySelectorAll('input[ms-code-add-number]').forEach(input => {
      const counterName = input.getAttribute('ms-code-add-number');
      if (counters[counterName]) {
        input.addEventListener('input', updateCounter);
      }
    });

    // Function to update counter when input changes
    function updateCounter(event) {
      const input = event.target;
      const counterName = input.getAttribute('ms-code-add-number');
      const counter = counters[counterName];

      if (counter) {
        counter.total = 0;
        document.querySelectorAll(`input[ms-code-add-number="${counterName}"]`).forEach(input => {
          counter.total += parseInt(input.value) || 0;
        });

        counter.displays.forEach(display => {
          if (display.tagName === 'INPUT') {
            display.value = counter.total;
          } else {
            display.textContent = counter.total;
          }
        });
      }
    }

    // Initial update for all counters
    Object.keys(counters).forEach(counterName => {
      const input = document.querySelector(`input[ms-code-add-number="${counterName}"]`);
      if (input) {
        input.dispatchEvent(new Event('input'));
      }
    });
  }

  // Run the initialization when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', initializeCounter);
</script>
Voir le Memberscript
UX

#130 - Soumettre automatiquement le formulaire lorsque toutes les entrées sont modifiées

Il n'est pas nécessaire d'avoir un bouton d'envoi et le formulaire est envoyé lorsque toutes les entrées sont modifiées.


<!-- 💙 MEMBERSCRIPT #130 v0.1 💙 - AUTO SUBMIT FORMS FROM INPUT CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const forms = document.querySelectorAll('form[ms-code-auto-submit]');

    forms.forEach(form => {
      const fields = form.querySelectorAll('input:not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea');
      const fieldStates = new Map(Array.from(fields).map(field => [field, false]));

      function updateFieldState(field, checkImmediately = false) {
        switch (field.type) {
          case 'checkbox':
            fieldStates.set(field, true); // Considered interacted with once changed
            break;
          case 'radio':
            const radioGroup = form.querySelectorAll(`input[type="radio"][name="${field.name}"]`);
            radioGroup.forEach(radio => fieldStates.set(radio, true));
            break;
          case 'select-one':
          case 'select-multiple':
            fieldStates.set(field, field.value !== '');
            break;
          case 'file':
            fieldStates.set(field, field.files.length > 0);
            break;
          case 'hidden':
            fieldStates.set(field, true); // Always consider hidden fields as filled
            break;
          default:
            // For text inputs, only update on blur or if checkImmediately is true
            if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
              if (checkImmediately || !field.dataset.blurred) {
                fieldStates.set(field, field.value.trim() !== '');
              }
            } else {
              fieldStates.set(field, field.value.trim() !== '');
            }
        }
        if (checkImmediately) {
          checkAndSubmit();
        }
      }

      function checkAndSubmit() {
        if (Array.from(fieldStates.values()).every(state => state)) {
          // Create and dispatch a submit event
          const submitEvent = new Event('submit', {
            bubbles: true,
            cancelable: true
          });

          const submitted = form.dispatchEvent(submitEvent);

          // If the event wasn't prevented, manually submit the form
          if (submitted) {
            form.submit();
          }
        }
      }

      fields.forEach(field => {
        // Use 'change' event for checkboxes, radios, file inputs, and selects
        if (['checkbox', 'radio', 'file', 'select-one', 'select-multiple'].includes(field.type) || field.tagName === 'SELECT') {
          field.addEventListener('change', () => updateFieldState(field, true));
        }

        // For text-like inputs, use 'blur' event
        if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
          field.addEventListener('blur', () => {
            field.dataset.blurred = 'true';
            updateFieldState(field, true);
          });
          // Also check on input, but don't submit immediately
          field.addEventListener('input', () => updateFieldState(field, false));
        }
      });

      // Initial check for pre-filled fields (e.g., browser autofill)
      fields.forEach(field => updateFieldState(field, false));
      checkAndSubmit();
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #130 v0.1 💙 - AUTO SUBMIT FORMS FROM INPUT CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const forms = document.querySelectorAll('form[ms-code-auto-submit]');

    forms.forEach(form => {
      const fields = form.querySelectorAll('input:not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea');
      const fieldStates = new Map(Array.from(fields).map(field => [field, false]));

      function updateFieldState(field, checkImmediately = false) {
        switch (field.type) {
          case 'checkbox':
            fieldStates.set(field, true); // Considered interacted with once changed
            break;
          case 'radio':
            const radioGroup = form.querySelectorAll(`input[type="radio"][name="${field.name}"]`);
            radioGroup.forEach(radio => fieldStates.set(radio, true));
            break;
          case 'select-one':
          case 'select-multiple':
            fieldStates.set(field, field.value !== '');
            break;
          case 'file':
            fieldStates.set(field, field.files.length > 0);
            break;
          case 'hidden':
            fieldStates.set(field, true); // Always consider hidden fields as filled
            break;
          default:
            // For text inputs, only update on blur or if checkImmediately is true
            if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
              if (checkImmediately || !field.dataset.blurred) {
                fieldStates.set(field, field.value.trim() !== '');
              }
            } else {
              fieldStates.set(field, field.value.trim() !== '');
            }
        }
        if (checkImmediately) {
          checkAndSubmit();
        }
      }

      function checkAndSubmit() {
        if (Array.from(fieldStates.values()).every(state => state)) {
          // Create and dispatch a submit event
          const submitEvent = new Event('submit', {
            bubbles: true,
            cancelable: true
          });

          const submitted = form.dispatchEvent(submitEvent);

          // If the event wasn't prevented, manually submit the form
          if (submitted) {
            form.submit();
          }
        }
      }

      fields.forEach(field => {
        // Use 'change' event for checkboxes, radios, file inputs, and selects
        if (['checkbox', 'radio', 'file', 'select-one', 'select-multiple'].includes(field.type) || field.tagName === 'SELECT') {
          field.addEventListener('change', () => updateFieldState(field, true));
        }

        // For text-like inputs, use 'blur' event
        if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
          field.addEventListener('blur', () => {
            field.dataset.blurred = 'true';
            updateFieldState(field, true);
          });
          // Also check on input, but don't submit immediately
          field.addEventListener('input', () => updateFieldState(field, false));
        }
      });

      // Initial check for pre-filled fields (e.g., browser autofill)
      fields.forEach(field => updateFieldState(field, false));
      checkAndSubmit();
    });
  });
</script>
Voir le Memberscript
Visibilité conditionnelle

#129 - La mise en place d'une barrière de sécurité dans les pays

Empêcher les visiteurs de consulter votre site s'ils se trouvent dans l'un des pays interdits.


<!-- 💙 MEMBERSCRIPT #129 v0.1 💙 - COUNTRY GATING -->
<script>
  // Configuration
  const ACCESS_DENIED_PAGE = '/access-denied';

  // List of disallowed countries using ISO 3166-1 alpha-2 country codes
  const DISALLOWED_COUNTRIES = [
    // "US",  // United States
    // "CN",  // China
    // "RU",  // Russia
    // "IN",  // India
    // "JP",  // Japan
    // "DE",  // Germany
    // "GB",  // United Kingdom
    // "FR",  // France
    // "BR",  // Brazil
    // "IT",  // Italy
    // Add more countries as needed
  ];

  // Function to get visitor's country and check access
  function checkCountryAccess() {
    // Check if we're already on the access denied page
    if (window.location.pathname === ACCESS_DENIED_PAGE) {
      return; // Don't redirect if already on the access denied page
    }

    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        const visitorCountry = data.country_code; // This returns the ISO 3166-1 alpha-2 country code
        if (DISALLOWED_COUNTRIES.includes(visitorCountry)) {
          window.location.href = ACCESS_DENIED_PAGE;
        }
      })
      .catch(error => {
        console.error('Error fetching IP data:', error);
      });
  }

  // Run the check when the page loads
  document.addEventListener('DOMContentLoaded', checkCountryAccess);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #129 v0.1 💙 - COUNTRY GATING -->
<script>
  // Configuration
  const ACCESS_DENIED_PAGE = '/access-denied';

  // List of disallowed countries using ISO 3166-1 alpha-2 country codes
  const DISALLOWED_COUNTRIES = [
    // "US",  // United States
    // "CN",  // China
    // "RU",  // Russia
    // "IN",  // India
    // "JP",  // Japan
    // "DE",  // Germany
    // "GB",  // United Kingdom
    // "FR",  // France
    // "BR",  // Brazil
    // "IT",  // Italy
    // Add more countries as needed
  ];

  // Function to get visitor's country and check access
  function checkCountryAccess() {
    // Check if we're already on the access denied page
    if (window.location.pathname === ACCESS_DENIED_PAGE) {
      return; // Don't redirect if already on the access denied page
    }

    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        const visitorCountry = data.country_code; // This returns the ISO 3166-1 alpha-2 country code
        if (DISALLOWED_COUNTRIES.includes(visitorCountry)) {
          window.location.href = ACCESS_DENIED_PAGE;
        }
      })
      .catch(error => {
        console.error('Error fetching IP data:', error);
      });
  }

  // Run the check when the page loads
  document.addEventListener('DOMContentLoaded', checkCountryAccess);
</script>
Voir le Memberscript
Visibilité conditionnelle
UX

#128 - Masquer les éléments après qu'ils aient été vus

Ajoutez un attribut et vos visiteurs ne verront cet élément qu'une seule fois. Après actualisation, il aura disparu.


<!-- 💙 MEMBERSCRIPT #128 💙 - ONLY SHOW ELEMENT ONCE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Find all elements with the ms-code-show-once attribute
    const elements = document.querySelectorAll('[ms-code-show-once]');

    elements.forEach(element => {
      const identifier = element.getAttribute('ms-code-show-once');
      const storageKey = `ms-code-shown-${identifier}`;

      // Check if the element has been seen before
      if (localStorage.getItem(storageKey) !== 'true') {
        // If not seen, show the element
        element.style.display = 'block';

        // Mark it as seen in localStorage
        localStorage.setItem(storageKey, 'true');
      } else {
        // If already seen, hide the element
        element.style.display = 'none';
      }
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #128 💙 - ONLY SHOW ELEMENT ONCE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Find all elements with the ms-code-show-once attribute
    const elements = document.querySelectorAll('[ms-code-show-once]');

    elements.forEach(element => {
      const identifier = element.getAttribute('ms-code-show-once');
      const storageKey = `ms-code-shown-${identifier}`;

      // Check if the element has been seen before
      if (localStorage.getItem(storageKey) !== 'true') {
        // If not seen, show the element
        element.style.display = 'block';

        // Mark it as seen in localStorage
        localStorage.setItem(storageKey, 'true');
      } else {
        // If already seen, hide the element
        element.style.display = 'none';
      }
    });
  });
</script>
Voir le Memberscript
UX

#127 - Valider les entrées de texte

Valider les entrées de texte par rapport à n'importe quelle liste de chaînes de caractères, y compris les caractères génériques !


<!-- 💙 MEMBERSCRIPT #127 v0.1 💙 - TEXT INPUT VALIDATION -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Debounce function
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // Find all fields with ms-code-require attribute
    const fields = document.querySelectorAll('[ms-code-require]');
    fields.forEach(field => {
        // Get the error element for this field
        const errorElement = document.querySelector(`[ms-code-require-error="${field.getAttribute('ms-code-require')}"]`);
        // Hide error message initially
        if (errorElement) {
            errorElement.style.display = 'none';
        }
        // Get the form containing the field
        const form = field.closest('form');
        // Get the submit button
        const submitButton = form ? form.querySelector(`[ms-code-submit-button="${field.getAttribute('ms-code-require')}"]`) : null;
        // Get the require-list attribute value
        const requireList = field.getAttribute('ms-code-require-list');
        if (requireList) {
            // Convert the require-list to an array of regex patterns
            const patterns = requireList.split(',').map(pattern => {
                return pattern.replace(/\{([^}]+)\}/g, (match, p1) => {
                    return p1.split('').map(char => {
                        switch(char) {
                            case '0': return '\\d';
                            case 'A': return '[A-Z]';
                            case 'a': return '[a-z]';
                            default: return char;
                        }
                    }).join('');
                });
            });
            // Validate function
            function validateField() {
                const value = field.value;
                const isValid = patterns.some(pattern => new RegExp(`^${pattern}$`).test(value));
                if (errorElement) {
                    errorElement.style.display = isValid ? 'none' : 'block';
                }
                if (submitButton) {
                    submitButton.style.opacity = isValid ? '1' : '0.5';
                    submitButton.style.pointerEvents = isValid ? 'auto' : 'none';
                }
                return isValid;
            }
            // Debounced validate function
            const debouncedValidate = debounce(validateField, 500);
            // Add blur event listener
            field.addEventListener('blur', validateField);
            // Add input event listener for debounced validation
            field.addEventListener('input', debouncedValidate);
            // Handle form submission
            if (form) {
                form.addEventListener('submit', function(event) {
                    if (!validateField() && submitButton) {
                        event.preventDefault();
                        field.focus();
                    }
                });
            }
        }
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #127 v0.1 💙 - TEXT INPUT VALIDATION -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Debounce function
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // Find all fields with ms-code-require attribute
    const fields = document.querySelectorAll('[ms-code-require]');
    fields.forEach(field => {
        // Get the error element for this field
        const errorElement = document.querySelector(`[ms-code-require-error="${field.getAttribute('ms-code-require')}"]`);
        // Hide error message initially
        if (errorElement) {
            errorElement.style.display = 'none';
        }
        // Get the form containing the field
        const form = field.closest('form');
        // Get the submit button
        const submitButton = form ? form.querySelector(`[ms-code-submit-button="${field.getAttribute('ms-code-require')}"]`) : null;
        // Get the require-list attribute value
        const requireList = field.getAttribute('ms-code-require-list');
        if (requireList) {
            // Convert the require-list to an array of regex patterns
            const patterns = requireList.split(',').map(pattern => {
                return pattern.replace(/\{([^}]+)\}/g, (match, p1) => {
                    return p1.split('').map(char => {
                        switch(char) {
                            case '0': return '\\d';
                            case 'A': return '[A-Z]';
                            case 'a': return '[a-z]';
                            default: return char;
                        }
                    }).join('');
                });
            });
            // Validate function
            function validateField() {
                const value = field.value;
                const isValid = patterns.some(pattern => new RegExp(`^${pattern}$`).test(value));
                if (errorElement) {
                    errorElement.style.display = isValid ? 'none' : 'block';
                }
                if (submitButton) {
                    submitButton.style.opacity = isValid ? '1' : '0.5';
                    submitButton.style.pointerEvents = isValid ? 'auto' : 'none';
                }
                return isValid;
            }
            // Debounced validate function
            const debouncedValidate = debounce(validateField, 500);
            // Add blur event listener
            field.addEventListener('blur', validateField);
            // Add input event listener for debounced validation
            field.addEventListener('input', debouncedValidate);
            // Handle form submission
            if (form) {
                form.addEventListener('submit', function(event) {
                    if (!validateField() && submitButton) {
                        event.preventDefault();
                        field.focus();
                    }
                });
            }
        }
    });
});
</script>
Voir le Memberscript
Flux personnalisés

#126 - Poster un formulaire vers un Webhook sans redirection

Envoyer des données à un webhook et conserver le comportement par défaut du formulaire Webflow.


<!-- 💙 MEMBERSCRIPT #126 v0.1 💙 - POST FORM DATA TO WEBHOOK WITHOUT REDIRECTING -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Select all forms with the ms-code-form-no-redirect attribute
    const forms = document.querySelectorAll('form[ms-code-form-no-redirect]');

    forms.forEach(form => {
      // Select the success and error message elements for this form
      const formWrapper = form.closest('.w-form');
      const successMessage = formWrapper.querySelector('.w-form-done');
      const errorMessage = formWrapper.querySelector('.w-form-fail');

      // Add submit event listener to the form
      form.addEventListener('submit', function(event) {
        // Prevent the default form submission
        event.preventDefault();

        // Get the form data
        const formData = new FormData(form);

        // Get the submit button and set its text to the waiting message
        const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
        const originalButtonText = submitButton.value || submitButton.textContent;
        const waitingText = submitButton.getAttribute('data-wait') || 'Please wait...';

        if (submitButton.tagName === 'INPUT') {
          submitButton.value = waitingText;
        } else {
          submitButton.textContent = waitingText;
        }

        // Disable the submit button
        submitButton.disabled = true;

        // Send the form data to the form's action URL using fetch
        fetch(form.action, {
          method: 'POST',
          body: formData
        })
          .then(response => {
            if (response.ok) {
              // If the submission was successful, show the success message
              form.style.display = 'none';
              successMessage.style.display = 'block';
              errorMessage.style.display = 'none';
            } else {
              // If there was an error, show the error message
              throw new Error('Form submission failed');
            }
          })
          .catch(error => {
            // If there was a network error or the submission failed, show the error message
            console.error('Error:', error);
            errorMessage.style.display = 'block';
            successMessage.style.display = 'none';
          })
          .finally(() => {
            // Reset the submit button text and re-enable it
            if (submitButton.tagName === 'INPUT') {
              submitButton.value = originalButtonText;
            } else {
              submitButton.textContent = originalButtonText;
            }
            submitButton.disabled = false;
          });
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #126 v0.1 💙 - POST FORM DATA TO WEBHOOK WITHOUT REDIRECTING -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Select all forms with the ms-code-form-no-redirect attribute
    const forms = document.querySelectorAll('form[ms-code-form-no-redirect]');

    forms.forEach(form => {
      // Select the success and error message elements for this form
      const formWrapper = form.closest('.w-form');
      const successMessage = formWrapper.querySelector('.w-form-done');
      const errorMessage = formWrapper.querySelector('.w-form-fail');

      // Add submit event listener to the form
      form.addEventListener('submit', function(event) {
        // Prevent the default form submission
        event.preventDefault();

        // Get the form data
        const formData = new FormData(form);

        // Get the submit button and set its text to the waiting message
        const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
        const originalButtonText = submitButton.value || submitButton.textContent;
        const waitingText = submitButton.getAttribute('data-wait') || 'Please wait...';

        if (submitButton.tagName === 'INPUT') {
          submitButton.value = waitingText;
        } else {
          submitButton.textContent = waitingText;
        }

        // Disable the submit button
        submitButton.disabled = true;

        // Send the form data to the form's action URL using fetch
        fetch(form.action, {
          method: 'POST',
          body: formData
        })
          .then(response => {
            if (response.ok) {
              // If the submission was successful, show the success message
              form.style.display = 'none';
              successMessage.style.display = 'block';
              errorMessage.style.display = 'none';
            } else {
              // If there was an error, show the error message
              throw new Error('Form submission failed');
            }
          })
          .catch(error => {
            // If there was a network error or the submission failed, show the error message
            console.error('Error:', error);
            errorMessage.style.display = 'block';
            successMessage.style.display = 'none';
          })
          .finally(() => {
            // Reset the submit button text and re-enable it
            if (submitButton.tagName === 'INPUT') {
              submitButton.value = originalButtonText;
            } else {
              submitButton.textContent = originalButtonText;
            }
            submitButton.disabled = false;
          });
      });
    });
  });
</script>
Voir le Memberscript
Visibilité conditionnelle
UX

#125 - Contrôler l'état requis avec une case à cocher

Définir les champs du formulaire comme obligatoires/facultatifs en fonction de l'état d'une case à cocher.


<!-- 💙 MEMBERSCRIPT #125 v0.1 💙 - CHECKBOX TOGGLE REQUIRED ON FORM FIELDS -->
<script>
  // Function to initialize the form field requirements
  function initFormFieldRequirements() {
    // Handle checkbox changes
    document.querySelectorAll('[ms-code-req-if-checked], [ms-code-req-if-unchecked]').forEach(checkbox => {
      checkbox.addEventListener('change', updateFieldRequirements);
      // Initial update
      updateFieldRequirements.call(checkbox);
    });
  }

  // Function to update field requirements based on checkbox state
  function updateFieldRequirements() {
    const isChecked = this.checked;
    const group = this.getAttribute('ms-code-req-if-checked') || this.getAttribute('ms-code-req-if-unchecked');
    const ifChecked = this.hasAttribute('ms-code-req-if-checked');
    const shouldBeRequired = ifChecked ? isChecked : !isChecked;
    const shouldDisableIfNotReq = this.hasAttribute('ms-code-disable-if-not-req');

    // Update associated input fields
    document.querySelectorAll(`[ms-code-req-input="${group}"]`).forEach(input => {
      input.required = shouldBeRequired;
      updateInputStyle(input, shouldBeRequired, shouldDisableIfNotReq);
    });

    // Update associated labels
    document.querySelectorAll(`[ms-code-req-label="${group}"]`).forEach(label => {
      label.style.display = shouldBeRequired ? '' : 'none';
    });
  }

  // Function to update input style based on required state and disable setting
  function updateInputStyle(input, isRequired, shouldDisableIfNotReq) {
    if (shouldDisableIfNotReq) {
      input.style.opacity = isRequired ? '1' : '0.4';
      input.style.pointerEvents = isRequired ? '' : 'none';
    } else {
      input.style.opacity = '';
      input.style.pointerEvents = '';
    }
  }

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

<!-- 💙 MEMBERSCRIPT #125 v0.1 💙 - CHECKBOX TOGGLE REQUIRED ON FORM FIELDS -->
<script>
  // Function to initialize the form field requirements
  function initFormFieldRequirements() {
    // Handle checkbox changes
    document.querySelectorAll('[ms-code-req-if-checked], [ms-code-req-if-unchecked]').forEach(checkbox => {
      checkbox.addEventListener('change', updateFieldRequirements);
      // Initial update
      updateFieldRequirements.call(checkbox);
    });
  }

  // Function to update field requirements based on checkbox state
  function updateFieldRequirements() {
    const isChecked = this.checked;
    const group = this.getAttribute('ms-code-req-if-checked') || this.getAttribute('ms-code-req-if-unchecked');
    const ifChecked = this.hasAttribute('ms-code-req-if-checked');
    const shouldBeRequired = ifChecked ? isChecked : !isChecked;
    const shouldDisableIfNotReq = this.hasAttribute('ms-code-disable-if-not-req');

    // Update associated input fields
    document.querySelectorAll(`[ms-code-req-input="${group}"]`).forEach(input => {
      input.required = shouldBeRequired;
      updateInputStyle(input, shouldBeRequired, shouldDisableIfNotReq);
    });

    // Update associated labels
    document.querySelectorAll(`[ms-code-req-label="${group}"]`).forEach(label => {
      label.style.display = shouldBeRequired ? '' : 'none';
    });
  }

  // Function to update input style based on required state and disable setting
  function updateInputStyle(input, isRequired, shouldDisableIfNotReq) {
    if (shouldDisableIfNotReq) {
      input.style.opacity = isRequired ? '1' : '0.4';
      input.style.pointerEvents = isRequired ? '' : 'none';
    } else {
      input.style.opacity = '';
      input.style.pointerEvents = '';
    }
  }

  // Initialize when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', initFormFieldRequirements);
</script>
Voir le Memberscript
UX

#124 - Visibilité de l'article

Utilisez des boutons personnalisés pour masquer et afficher des éléments, les états étant enregistrés dans la mémoire locale.


<!-- 💙 MEMBERSCRIPT #124 v0.1 💙 - TOGGLE ITEM VISIBILITY -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const STORAGE_KEY = 'ms-code-vis-states';

    // Load saved states from local storage
    let savedStates = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};

    // Find all unique group identifiers
    const groups = new Set([...document.querySelectorAll('[ms-code-vis-item]')].map(el => el.getAttribute('ms-code-vis-item')));

    groups.forEach(group => {
      const item = document.querySelector(`[ms-code-vis-item="${group}"]`);
      const showButton = document.querySelector(`[ms-code-vis-show="${group}"]`);
      const hideButton = document.querySelector(`[ms-code-vis-hide="${group}"]`);

      if (!item || !showButton || !hideButton) return;

      // Function to update visibility
      function updateVisibility(isVisible) {
        if (isVisible) {
          item.style.removeProperty('display');
          showButton.style.display = 'none';
          hideButton.style.removeProperty('display');
        } else {
          item.style.display = 'none';
          showButton.style.removeProperty('display');
          hideButton.style.display = 'none';
        }
        // Save state to local storage
        savedStates[group] = isVisible;
        localStorage.setItem(STORAGE_KEY, JSON.stringify(savedStates));
      }

      // Set initial visibility
      const defaultState = item.getAttribute('ms-code-vis-default');
      const savedState = savedStates[group];

      if (savedState !== undefined) {
        updateVisibility(savedState);
      } else if (defaultState === 'hide') {
        updateVisibility(false);
      } else {
        updateVisibility(true);
      }

      // Add click event listeners
      showButton.addEventListener('click', function(e) {
        e.preventDefault();
        updateVisibility(true);
      });

      hideButton.addEventListener('click', function(e) {
        e.preventDefault();
        updateVisibility(false);
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #124 v0.1 💙 - TOGGLE ITEM VISIBILITY -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const STORAGE_KEY = 'ms-code-vis-states';

    // Load saved states from local storage
    let savedStates = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};

    // Find all unique group identifiers
    const groups = new Set([...document.querySelectorAll('[ms-code-vis-item]')].map(el => el.getAttribute('ms-code-vis-item')));

    groups.forEach(group => {
      const item = document.querySelector(`[ms-code-vis-item="${group}"]`);
      const showButton = document.querySelector(`[ms-code-vis-show="${group}"]`);
      const hideButton = document.querySelector(`[ms-code-vis-hide="${group}"]`);

      if (!item || !showButton || !hideButton) return;

      // Function to update visibility
      function updateVisibility(isVisible) {
        if (isVisible) {
          item.style.removeProperty('display');
          showButton.style.display = 'none';
          hideButton.style.removeProperty('display');
        } else {
          item.style.display = 'none';
          showButton.style.removeProperty('display');
          hideButton.style.display = 'none';
        }
        // Save state to local storage
        savedStates[group] = isVisible;
        localStorage.setItem(STORAGE_KEY, JSON.stringify(savedStates));
      }

      // Set initial visibility
      const defaultState = item.getAttribute('ms-code-vis-default');
      const savedState = savedStates[group];

      if (savedState !== undefined) {
        updateVisibility(savedState);
      } else if (defaultState === 'hide') {
        updateVisibility(false);
      } else {
        updateVisibility(true);
      }

      // Add click event listeners
      showButton.addEventListener('click', function(e) {
        e.preventDefault();
        updateVisibility(true);
      });

      hideButton.addEventListener('click', function(e) {
        e.preventDefault();
        updateVisibility(false);
      });
    });
  });
</script>
Voir le Memberscript
UX
Accessibilité

#123 - Une lecture audio à la fois

Mettre automatiquement en pause tous les autres audioplayers lorsqu'un utilisateur clique sur l'un d'entre eux.


<!-- 💙 MEMBERSCRIPT #123 💙 - ONE AUDIO AT A TIME -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const pauseOthers = current => 
      document.querySelectorAll('audio, video').forEach(el => el !== current && !el.paused && el.pause());
  
    const addPlayListener = el => el.addEventListener('play', e => pauseOthers(e.target));
  
    new MutationObserver(mutations => 
      mutations.forEach(m => m.addedNodes.forEach(n => 
        (n.nodeName === 'AUDIO' || n.nodeName === 'VIDEO') && addPlayListener(n)
      ))
    ).observe(document.body, { childList: true, subtree: true });
  
    document.querySelectorAll('audio, video').forEach(addPlayListener);
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #123 💙 - ONE AUDIO AT A TIME -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const pauseOthers = current => 
      document.querySelectorAll('audio, video').forEach(el => el !== current && !el.paused && el.pause());
  
    const addPlayListener = el => el.addEventListener('play', e => pauseOthers(e.target));
  
    new MutationObserver(mutations => 
      mutations.forEach(m => m.addedNodes.forEach(n => 
        (n.nodeName === 'AUDIO' || n.nodeName === 'VIDEO') && addPlayListener(n)
      ))
    ).observe(document.body, { childList: true, subtree: true });
  
    document.querySelectorAll('audio, video').forEach(addPlayListener);
  });
</script>
Voir le Memberscript
RÉFÉRENCEMENT
UX

#122 - Ouvrir les liens externes dans un nouvel onglet

Les liens externes s'ouvrent automatiquement dans un nouvel onglet et les attributs nofollow et noreferrer sont ajoutés.


<!-- 💙 MEMBERSCRIPT #122 💙 - OPEN EXTERNAL LINKS IN NEW TAB -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const thisDomain = location.hostname;
    const externalSelector = `a:not([href*="${thisDomain}"]):not([href^="/"]):not([href^="#"]):not([ms-code-ext-link="ignore"])`;

    document.querySelectorAll(externalSelector).forEach(link => {
      link.target = '_blank';  // Open in new tab (comment out to disable)
      link.rel = (link.rel ? link.rel + ' ' : '') + 'noreferrer';  // Add noreferrer (comment out to disable)
      link.rel = (link.rel ? link.rel + ' ' : '') + 'nofollow';  // Add nofollow (comment out to disable)
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #122 💙 - OPEN EXTERNAL LINKS IN NEW TAB -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const thisDomain = location.hostname;
    const externalSelector = `a:not([href*="${thisDomain}"]):not([href^="/"]):not([href^="#"]):not([ms-code-ext-link="ignore"])`;

    document.querySelectorAll(externalSelector).forEach(link => {
      link.target = '_blank';  // Open in new tab (comment out to disable)
      link.rel = (link.rel ? link.rel + ' ' : '') + 'noreferrer';  // Add noreferrer (comment out to disable)
      link.rel = (link.rel ? link.rel + ' ' : '') + 'nofollow';  // Add nofollow (comment out to disable)
    });
  });
</script>
Voir le Memberscript
Nous n'avons pas trouvé de scripts pour cette recherche... veuillez réessayer.
Slack

Besoin d'aide avec MemberScripts ? Rejoignez notre communauté Slack de plus de 5 500 membres ! 🙌

Les MemberScripts sont une ressource communautaire de Memberstack - si vous avez besoin d'aide pour les faire fonctionner avec votre projet, rejoignez le Slack de Memberstack 2.0 et demandez de l'aide !

Rejoignez notre Slack
Vitrine

Découvrez les entreprises qui ont réussi avec Memberstack

Ne vous contentez pas de nous croire sur parole, consultez les entreprises de toutes tailles qui font confiance à Memberstack pour leur authentification et leurs paiements.

Voir tous les exemples de réussite
Même Webflow utilise Memberstack !
Commencer à construire

Commencez à construire vos rêves

Memberstack est 100% gratuit jusqu'à ce que vous soyez prêt à vous lancer - alors, qu'attendez-vous ? Créez votre première application et commencez à construire dès aujourd'hui.