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
Flux personnalisés

#105 - Vérification après la connexion

Lancer automatiquement la caisse si un membre sélectionne un prix avant de se connecter.


<!-- 💙 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>
Voir le Memberscript
UX

#104 - Indicateur en ligne

Montrez aux visiteurs de votre site votre statut en ligne en fonction des fuseaux horaires.


<!-- 💙 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>
Voir le Memberscript
Intégration

#103 - Formulaire de réservation personnalisé

Ajoutez un formulaire de réservation personnalisé à votre site web qui crée un événement dans le calendrier Google.


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés
UX

#102 - Redimensionner automatiquement la hauteur du Textarea

Augmenter ou diminuer la hauteur d'un textarea en fonction de son contenu.


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés
UX

#101 - Redimensionner automatiquement la largeur d'entrée

Augmenter ou réduire la largeur d'une entrée en fonction de son contenu.


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés
Intégration

#100 - Compression automatique des téléchargements d'images

Compression des images téléchargées, y compris les images de profil.


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés

#99 - Fichiers d'entrée personnalisés

Transformez n'importe quoi en fichier d'entrée !


<!-- 💙 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>
Voir le Memberscript
Visibilité conditionnelle

#N° 98 - Limitation de l'âge

Les utilisateurs doivent confirmer leur âge avant de continuer.


<!-- 💙 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>
Voir le Memberscript
Intégration

#97 - Uploader des fichiers vers le S3 Bucket

Autoriser les téléchargements vers un godet S3 à partir d'un formulaire Webflow.


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés

#96 - Enregistrer un décompte

Créez et mettez à jour un décompte qui s'enregistre dans un champ personnalisé !


<!-- 💙 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>
Voir le Memberscript
UX

#95 - Confetti On Click

Faites voler des confettis amusants en cliquant !


<!-- 💙 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>
Voir le Memberscript
UX

#94 - Définir l'attribut href

Définir dynamiquement un lien à l'aide du CMS Webflow (ou autre)


<!-- 💙 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>
Voir le Memberscript
Champs personnalisés
UX

#93 - Forcer les URL valides dans les formulaires de saisie

Convertit automatiquement tout ce qui est saisi en URL valide.


<!-- 💙 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>
Voir le Memberscript
UX

#92 - Changer de page en cliquant

Modifier l'URL de la page en cours lorsque l'on clique sur un élément.


<!-- 💙 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>
Voir le Memberscript
Modaux
UX

#91 - Masquer la fenêtre popup pour une durée déterminée

Masquer une fenêtre contextuelle pendant une durée X lorsqu'un bouton est cliqué.


<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
  $(document).ready(function() {
    // Look for elements with 'ms-code-hide-popup' attribute
    var items = $('[ms-code-hide-popup]');

    var button;
    var timeElement;

    // Determine which element is the button and which is the timer
    items.each(function(index, item) {
      var value = $(item).attr('ms-code-hide-popup');
      if (value === "button") {
        button = $(item);
      } else {
        timeElement = $(item);
      }
    });

    // Calculate the target date
    var calculateTargetDate = function(timeStr) {
      var splitTime = timeStr.split(':');
      var now = new Date();
      now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
      now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
      now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
      now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
      return now;
    };

    // Check if element should be removed from DOM
    var checkTimeAndRemoveElement = function() {
      var targetDate = localStorage.getItem('targetDate');
      if (targetDate && new Date() < new Date(targetDate)) {
        timeElement.remove();
      } else {
        localStorage.removeItem('targetDate');
      }
    };

    // Action on button click
    button.on('click', function() {
      var time = timeElement.attr('ms-code-hide-popup');
      localStorage.setItem('targetDate', calculateTargetDate(time));
      checkTimeAndRemoveElement();
    });

    // Initial check
    checkTimeAndRemove AndRemoveElement();
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
  $(document).ready(function() {
    // Look for elements with 'ms-code-hide-popup' attribute
    var items = $('[ms-code-hide-popup]');

    var button;
    var timeElement;

    // Determine which element is the button and which is the timer
    items.each(function(index, item) {
      var value = $(item).attr('ms-code-hide-popup');
      if (value === "button") {
        button = $(item);
      } else {
        timeElement = $(item);
      }
    });

    // Calculate the target date
    var calculateTargetDate = function(timeStr) {
      var splitTime = timeStr.split(':');
      var now = new Date();
      now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
      now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
      now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
      now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
      return now;
    };

    // Check if element should be removed from DOM
    var checkTimeAndRemoveElement = function() {
      var targetDate = localStorage.getItem('targetDate');
      if (targetDate && new Date() < new Date(targetDate)) {
        timeElement.remove();
      } else {
        localStorage.removeItem('targetDate');
      }
    };

    // Action on button click
    button.on('click', function() {
      var time = timeElement.attr('ms-code-hide-popup');
      localStorage.setItem('targetDate', calculateTargetDate(time));
      checkTimeAndRemoveElement();
    });

    // Initial check
    checkTimeAndRemove AndRemoveElement();
  });
</script>
Voir le Memberscript
Champs personnalisés
UX

#90 - Afficher les éléments lors d'un changement d'entrée

Afficher 1 ou plusieurs éléments lorsque l'utilisateur modifie la valeur d'entrée.


<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    // Initially hide all elements
    $('[ms-code-show-item]').css('display', 'none');

    setTimeout(function() {
        $('[ms-code-show-field]').change(function() {
            var field = $(this).attr('ms-code-show-field');
            $('[ms-code-show-item=' + field + ']').css('display', 'block');
        });
    }, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    // Initially hide all elements
    $('[ms-code-show-item]').css('display', 'none');

    setTimeout(function() {
        $('[ms-code-show-field]').change(function() {
            var field = $(this).attr('ms-code-show-field');
            $('[ms-code-show-item=' + field + ']').css('display', 'block');
        });
    }, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>
Voir le Memberscript
UX

#89 - Menus contextuels personnalisés

Afficher un menu contextuel personnalisé et intégré à Webflow lorsque l'on clique avec le bouton droit de la souris sur votre élément.


<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");

// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
    element.addEventListener('contextmenu', event => {
        event.preventDefault(); // Prevents showing the default context menu
        hideAllMenus(); // Make sure other menus are hidden

        // fetch the related menu, make it visible
        const menuItemId = element.getAttribute("ms-code-context-item");
        const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);

        if (menu) {
            menu.classList.remove('hidden');
            menu.classList.add('visible');
        }
    });
});

// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
    menu.addEventListener('click', event => {
        event.stopPropagation();
    });
});

// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);

// Helper function to hide all custom context menus
function hideAllMenus() {
    menus.forEach(menu => {
        menu.classList.remove('visible');
        menu.classList.add('hidden');
    });
}
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");

// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
    element.addEventListener('contextmenu', event => {
        event.preventDefault(); // Prevents showing the default context menu
        hideAllMenus(); // Make sure other menus are hidden

        // fetch the related menu, make it visible
        const menuItemId = element.getAttribute("ms-code-context-item");
        const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);

        if (menu) {
            menu.classList.remove('hidden');
            menu.classList.add('visible');
        }
    });
});

// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
    menu.addEventListener('click', event => {
        event.stopPropagation();
    });
});

// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);

// Helper function to hide all custom context menus
function hideAllMenus() {
    menus.forEach(menu => {
        menu.classList.remove('visible');
        menu.classList.add('hidden');
    });
}
</script>
Voir le Memberscript
UX

#88 - Afficher l'état actuel du CMS, liens vers les dossiers

Affichez l'état "actuel" du Webflow sur vos pages imbriquées et les éléments du CMS.


<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
  var currentUrl = window.location.href;
  var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute

  elements.forEach(function (element) {
    var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
    if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
      element.classList.add('w--current'); // apply the class 
    }
  });
};
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
  var currentUrl = window.location.href;
  var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute

  elements.forEach(function (element) {
    var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
    if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
      element.classList.add('w--current'); // apply the class 
    }
  });
};
</script>
Voir le Memberscript
Flux personnalisés

#87 - Supprimer un plan après le compte à rebours

Créez des contenus sécurisés sensibles au facteur temps !


<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));

// Check if date has passed
const checkDate = async () => {
  const now = new Date();
  if (now > countdown) {
    // Remove member's free plan
    await memberstack.removePlan({
      planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
    });
    console.log("Plan removed");

    // Reload the page
    location.reload();
  }
}

// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));

// Check if date has passed
const checkDate = async () => {
  const now = new Date();
  if (now > countdown) {
    // Remove member's free plan
    await memberstack.removePlan({
      planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
    });
    console.log("Plan removed");

    // Reload the page
    location.reload();
  }
}

// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>
Voir le Memberscript
UX

#86 - Text-To-Speech simple et gratuit

Ajoutez un bouton qui permet aux visiteurs d'écouter votre article.


<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
    const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
    const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
    let utterance = new SpeechSynthesisUtterance();

    speakButton.addEventListener('click', () => {
        if(speechSynthesis.speaking || speechSynthesis.paused) {
            speechSynthesis.cancel(); // stops current speech
        } else {
            utterance.text = textDiv.innerText;

            speechSynthesis.speak(utterance); // starts speaking
        }
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
    const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
    const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
    let utterance = new SpeechSynthesisUtterance();

    speakButton.addEventListener('click', () => {
        if(speechSynthesis.speaking || speechSynthesis.paused) {
            speechSynthesis.cancel(); // stops current speech
        } else {
            utterance.text = textDiv.innerText;

            speechSynthesis.speak(utterance); // starts speaking
        }
    });
});
</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.