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.

Champs personnalisés

#41 - Des entrées de numéros de téléphone parfaites

Les entrées de numéros de téléphone internationaux, comme elles devraient l'être.

Avec recherche d'IP

Utilisez cette option si vous souhaitez que le pays IP de l'utilisateur soit automatiquement prérempli. IMPORTANT : N'utilisez pas cette option avec les formulaires de profil, car elle se comporterait de manière erratique.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      $.get("https://ipinfo.io", function(response) {
        var countryCode = response.country;
        iti.setCountry(countryCode);
      }, "jsonp");

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>

Sans recherche d'IP

Utilisez cette option pour les formulaires de profil et/ou si vous ne souhaitez pas procéder à un pré-remplissage automatique sur la base de l'adresse IP de l'utilisateur.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>
v0.2

Avec recherche d'IP

Utilisez cette option si vous souhaitez que le pays IP de l'utilisateur soit automatiquement prérempli. IMPORTANT : N'utilisez pas cette option avec les formulaires de profil, car elle se comporterait de manière erratique.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      $.get("https://ipinfo.io", function(response) {
        var countryCode = response.country;
        iti.setCountry(countryCode);
      }, "jsonp");

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>

Sans recherche d'IP

Utilisez cette option pour les formulaires de profil et/ou si vous ne souhaitez pas procéder à un pré-remplissage automatique sur la base de l'adresse IP de l'utilisateur.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>
Voir le Memberscript
Champs personnalisés
UX

#40 - Drag And Drop File Uploader (téléchargeur de fichiers par glisser-déposer)

Ajoutez facilement une fonction de téléchargement de fichiers par glisser-déposer à votre site Webflow !

Important

Si vous utilisez le MemberScript #38, assurez-vous de placer ce script APRES !


<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
      credits: false,
      name: 'fileToUpload',
      storeAsFile: true
      // for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
    });
  });
</script>
v0.1

Important

Si vous utilisez le MemberScript #38, assurez-vous de placer ce script APRES !


<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
      credits: false,
      name: 'fileToUpload',
      storeAsFile: true
      // for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
    });
  });
</script>
Voir le Memberscript
Champs personnalisés
UX

#39 - Améliorer les champs de sélection

Ajouter des recherches et une meilleure interface utilisateur pour sélectionner et multi-sélectionner les champs !

Code de la tête

Put this in the <head> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />

Code du corps

Put this in the </body> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
    $('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
v0.1

Code de la tête

Put this in the <head> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />

Code du corps

Put this in the </body> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
    $('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
Voir le Memberscript
Champs personnalisés

#38 - Champ de téléchargement de fichiers

Ajoutez un outil de téléchargement de fichiers à n'importe quel site et envoyez la soumission à Google Drive, à un courriel ou à n'importe quel endroit de votre choix.


<!-- 💙 MEMBERSCRIPT #38 v0.1 💙 FORM FILE UPLOADER -->
<script>
const forms = document.querySelectorAll('form[ms-code-file-upload="form"]');

forms.forEach((form) => {
  form.setAttribute('enctype', 'multipart/form-data');
  const uploadInputs = form.querySelectorAll('[ms-code-file-upload-input]');

  uploadInputs.forEach((uploadInput) => {
    const inputName = uploadInput.getAttribute('ms-code-file-upload-input');

    const fileInput = document.createElement('input');
    fileInput.setAttribute('type', 'file');
    fileInput.setAttribute('name', inputName);
    fileInput.setAttribute('id', inputName);
    fileInput.required = true; // delete this line to make the input optional

    uploadInput.appendChild(fileInput);
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #38 v0.1 💙 FORM FILE UPLOADER -->
<script>
const forms = document.querySelectorAll('form[ms-code-file-upload="form"]');

forms.forEach((form) => {
  form.setAttribute('enctype', 'multipart/form-data');
  const uploadInputs = form.querySelectorAll('[ms-code-file-upload-input]');

  uploadInputs.forEach((uploadInput) => {
    const inputName = uploadInput.getAttribute('ms-code-file-upload-input');

    const fileInput = document.createElement('input');
    fileInput.setAttribute('type', 'file');
    fileInput.setAttribute('name', inputName);
    fileInput.setAttribute('id', inputName);
    fileInput.required = true; // delete this line to make the input optional

    uploadInput.appendChild(fileInput);
  });
});
</script>
Voir le Memberscript
JSON
Marketing

#37 - Suppression automatique du plan gratuit

Supprimez automatiquement un plan gratuit après une durée déterminée !


<!-- 💙 MEMBERSCRIPT #37 v0.1 💙 MAKE FREE TRIAL EXPIRE AFTER SET TIME -->
<script>
let memberPlanId = "your_plan_ID"; // replace with your actual FREE plan ID

document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  // Fetch the member's data
  const member = await memberstack.getMemberJSON();

  // Fetch the member's planConnections from local storage
  const memberDataFromLocalStorage = JSON.parse(localStorage.getItem('_ms-mem'));
  const planConnections = memberDataFromLocalStorage.planConnections;

  // Check if the member has x plan
  let hasPlan = false;
  if (planConnections) {
    hasPlan = planConnections.some(planConnection => planConnection.planId === memberPlanId);
  }

  if (hasPlan) {
    // Check the members one-time-date
    let currentDate = new Date();
    let oneTimeDate = new Date(member.data['one-time-date']);

    if (currentDate > oneTimeDate) {
      // If the members' one time date has passed, remove x plan
      memberstack.removePlan({
        planId: memberPlanId
      }).then(() => {
        // Redirect to /free-trial-expired
        window.location.href = "/free-trial-expired";
      }).catch(error => {
        // Handle error
      });
    }
  }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #37 v0.1 💙 MAKE FREE TRIAL EXPIRE AFTER SET TIME -->
<script>
let memberPlanId = "your_plan_ID"; // replace with your actual FREE plan ID

document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  // Fetch the member's data
  const member = await memberstack.getMemberJSON();

  // Fetch the member's planConnections from local storage
  const memberDataFromLocalStorage = JSON.parse(localStorage.getItem('_ms-mem'));
  const planConnections = memberDataFromLocalStorage.planConnections;

  // Check if the member has x plan
  let hasPlan = false;
  if (planConnections) {
    hasPlan = planConnections.some(planConnection => planConnection.planId === memberPlanId);
  }

  if (hasPlan) {
    // Check the members one-time-date
    let currentDate = new Date();
    let oneTimeDate = new Date(member.data['one-time-date']);

    if (currentDate > oneTimeDate) {
      // If the members' one time date has passed, remove x plan
      memberstack.removePlan({
        planId: memberPlanId
      }).then(() => {
        // Redirect to /free-trial-expired
        window.location.href = "/free-trial-expired";
      }).catch(error => {
        // Handle error
      });
    }
  }
});
</script>
Voir le Memberscript
Visibilité conditionnelle
UX

#36 - Validation du mot de passe

Utilisez cette méthode simple pour confirmer que vos membres ont saisi un mot de passe fort.


<!-- 💙 MEMBERSCRIPT #36 v0.1 💙 PASSWORD VALIDATION -->
<script>
window.addEventListener('load', function() {
    const passwordInput = document.querySelector('input[data-ms-member="password"]');
    const submitButton = document.querySelector('[ms-code-submit-button]');
    
    if (!passwordInput || !submitButton) return;  // Return if essential elements are not found

    function checkAllValid() {
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        return Array.from(validationPoints).every(validationPoint => {
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            return validIcon && validIcon.style.display === 'flex';  // Check for validIcon existence before accessing style
        });
    }

    passwordInput.addEventListener('keyup', function() {
        const password = passwordInput.value;
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        
        validationPoints.forEach(function(validationPoint) {
            const rule = validationPoint.getAttribute('ms-code-pw-validation');
            let isValid = false;
            
            // MINIMUM LENGTH VALIDATION POINT
            if (rule.startsWith('minlength-')) {
                const minLength = parseInt(rule.split('-')[1]);
                isValid = password.length >= minLength;
            }

            // SPECIAL CHARACTER VALIDATION POINT
            else if (rule === 'special-character') {
                isValid = /[!@#$%^&*(),.?":{}|<>]/g.test(password);
            }

            // UPPER AND LOWER CASE VALIDATION POINT
            else if (rule === 'upper-lower-case') {
                isValid = /[a-z]/.test(password) && /[A-Z]/.test(password);
            }

            // NUMBER VALIDATION POINT
            else if (rule === 'number') {
                isValid = /\d/.test(password);
            }
            
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            const invalidIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="false"]');
            
            if (validIcon && invalidIcon) {  // Check for existence before accessing style
                if (isValid) {
                    validIcon.style.display = 'flex';
                    invalidIcon.style.display = 'none';
                } else {
                    validIcon.style.display = 'none';
                    invalidIcon.style.display = 'flex';
                }
            }
        });

        if (checkAllValid()) {
            submitButton.classList.remove('disabled');
        } else {
            submitButton.classList.add('disabled');
        }
    });

    // Trigger keyup event after adding event listener
    var event = new Event('keyup');
    passwordInput.dispatchEvent(event);
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #36 v0.1 💙 PASSWORD VALIDATION -->
<script>
window.addEventListener('load', function() {
    const passwordInput = document.querySelector('input[data-ms-member="password"]');
    const submitButton = document.querySelector('[ms-code-submit-button]');
    
    if (!passwordInput || !submitButton) return;  // Return if essential elements are not found

    function checkAllValid() {
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        return Array.from(validationPoints).every(validationPoint => {
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            return validIcon && validIcon.style.display === 'flex';  // Check for validIcon existence before accessing style
        });
    }

    passwordInput.addEventListener('keyup', function() {
        const password = passwordInput.value;
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        
        validationPoints.forEach(function(validationPoint) {
            const rule = validationPoint.getAttribute('ms-code-pw-validation');
            let isValid = false;
            
            // MINIMUM LENGTH VALIDATION POINT
            if (rule.startsWith('minlength-')) {
                const minLength = parseInt(rule.split('-')[1]);
                isValid = password.length >= minLength;
            }

            // SPECIAL CHARACTER VALIDATION POINT
            else if (rule === 'special-character') {
                isValid = /[!@#$%^&*(),.?":{}|<>]/g.test(password);
            }

            // UPPER AND LOWER CASE VALIDATION POINT
            else if (rule === 'upper-lower-case') {
                isValid = /[a-z]/.test(password) && /[A-Z]/.test(password);
            }

            // NUMBER VALIDATION POINT
            else if (rule === 'number') {
                isValid = /\d/.test(password);
            }
            
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            const invalidIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="false"]');
            
            if (validIcon && invalidIcon) {  // Check for existence before accessing style
                if (isValid) {
                    validIcon.style.display = 'flex';
                    invalidIcon.style.display = 'none';
                } else {
                    validIcon.style.display = 'none';
                    invalidIcon.style.display = 'flex';
                }
            }
        });

        if (checkAllValid()) {
            submitButton.classList.remove('disabled');
        } else {
            submitButton.classList.add('disabled');
        }
    });

    // Trigger keyup event after adding event listener
    var event = new Event('keyup');
    passwordInput.dispatchEvent(event);
});
</script>
Voir le Memberscript
RÉFÉRENCEMENT

#35 - Ajouter facilement des FAQ Schema/Rich Snippets

Ajoutez un script et 2 attributs pour permettre la mise à jour constante des rich snippets sur votre page.


<!-- 💙 MEMBERSCRIPT #35 v0.1 💙 FAQ RICH SNIPPETS -->
<script>
let faqArray = [];
let questionElements = document.querySelectorAll('[ms-code-snippet-q]');
let answerElements = document.querySelectorAll('[ms-code-snippet-a]');

for (let i = 0; i < questionElements.length; i++) {
  let question = questionElements[i].innerText;
  let answer = '';

  for (let j = 0; j < answerElements.length; j++) {
    if (questionElements[i].getAttribute('ms-code-snippet-q') === answerElements[j].getAttribute('ms-code-snippet-a')) {
      answer = answerElements[j].innerText;
      break;
    }
  }

  faqArray.push({
    "@type": "Question",
    "name": question,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": answer
    }
  });
}

let faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqArray
}

let script = document.createElement('script');
script.type = "application/ld+json";
script.innerHTML = JSON.stringify(faqSchema);

document.getElementsByTagName('head')[0].appendChild(script);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #35 v0.1 💙 FAQ RICH SNIPPETS -->
<script>
let faqArray = [];
let questionElements = document.querySelectorAll('[ms-code-snippet-q]');
let answerElements = document.querySelectorAll('[ms-code-snippet-a]');

for (let i = 0; i < questionElements.length; i++) {
  let question = questionElements[i].innerText;
  let answer = '';

  for (let j = 0; j < answerElements.length; j++) {
    if (questionElements[i].getAttribute('ms-code-snippet-q') === answerElements[j].getAttribute('ms-code-snippet-a')) {
      answer = answerElements[j].innerText;
      break;
    }
  }

  faqArray.push({
    "@type": "Question",
    "name": question,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": answer
    }
  });
}

let faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqArray
}

let script = document.createElement('script');
script.type = "application/ld+json";
script.innerHTML = JSON.stringify(faqSchema);

document.getElementsByTagName('head')[0].appendChild(script);
</script>
Voir le Memberscript
UX

#34 - Exiger l'email professionnel pour la soumission de formulaires

Empêcher les personnes de soumettre un formulaire si leur adresse électronique est une adresse personnelle telle que gmail.


<!-- 💙 MEMBERSCRIPT #34 v0.1 💙 REQUIRE BUSINESS EMAILS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"> </script>
<script>
function isPersonalEmail(email) {
  var personalDomains = [
    "gmail.com", 
    "yahoo.com", 
    "hotmail.com", 
    "aol.com", 
    "msn.com", 
    "comcast.net", 
    "live.com", 
    "outlook.com", 
    "ymail.com",
    "icloud.com"
  ];
  var emailDomain = email.split('@')[1];
  return personalDomains.includes(emailDomain);
}

window.Parsley.addValidator('businessEmail', {
  validateString: function(value) {
    return !isPersonalEmail(value);
  },
  messages: {
    en: 'Please enter a business email.'
  }
});

$(document).ready(function() {
  $('form[ms-code-validate-form]').attr('data-parsley-validate', '');
  $('input[ms-code-business-email]').attr('data-parsley-business-email', '');
  $('form').parsley();
});
  $('form').parsley().on('form:error', function() {
  $('.parsley-errors-list').addClass('ms-code-validation-error');
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #34 v0.1 💙 REQUIRE BUSINESS EMAILS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"> </script>
<script>
function isPersonalEmail(email) {
  var personalDomains = [
    "gmail.com", 
    "yahoo.com", 
    "hotmail.com", 
    "aol.com", 
    "msn.com", 
    "comcast.net", 
    "live.com", 
    "outlook.com", 
    "ymail.com",
    "icloud.com"
  ];
  var emailDomain = email.split('@')[1];
  return personalDomains.includes(emailDomain);
}

window.Parsley.addValidator('businessEmail', {
  validateString: function(value) {
    return !isPersonalEmail(value);
  },
  messages: {
    en: 'Please enter a business email.'
  }
});

$(document).ready(function() {
  $('form[ms-code-validate-form]').attr('data-parsley-validate', '');
  $('input[ms-code-business-email]').attr('data-parsley-business-email', '');
  $('form').parsley();
});
  $('form').parsley().on('form:error', function() {
  $('.parsley-errors-list').addClass('ms-code-validation-error');
});
</script>
Voir le Memberscript
Visibilité conditionnelle
UX

#33 - Formater automatiquement les entrées de formulaire

Obliger les entrées de formulaire à suivre un format défini, tel que JJ/MM/AAAA.


<!-- 💙 MEMBERSCRIPT #33 v0.2 💙 AUTOMATICALLY FORMAT FORM INPUTS -->
<script src="https://cdn.jsdelivr.net/npm/cleave.js@1.6.0"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cleave.js/1.6.0/addons/cleave-phone.us.js"> </script>
<script>
document.addEventListener('DOMContentLoaded', function(){
    // SELECT ALL ELEMENTS WITH THE ATTRIBUTE "ms-code-autoformat" OR "ms-code-autoformat-prefix"
    const elements = document.querySelectorAll('[ms-code-autoformat], [ms-code-autoformat-prefix]');

    for (let element of elements) {
        const formatType = element.getAttribute('ms-code-autoformat');
        const prefix = element.getAttribute('ms-code-autoformat-prefix');
        
        // SET PREFIX
        let cleaveOptions = {
            prefix: prefix || '',
            blocks: [Infinity]
        };
        
        // BASED ON THE VALUE OF "ms-code-autoformat", FORMAT THE INPUT
        if (formatType) {
            switch (formatType) {
                // FORMAT PHONE NUMBERS
                case 'phone-number':
                    cleaveOptions.phone = true;
                    cleaveOptions.phoneRegionCode = 'US';
                    break;
                    
                // FORMAT DATES IN 'YYYY-MM-DD' FORMAT
                case 'date-yyyy-mm-dd':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['Y', 'm', 'd'];
                    break;
                    
                // FORMAT DATES IN 'MM-DD-YYYY' FORMAT
                case 'date-mm-dd-yyyy':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['m', 'd', 'Y'];
                    break;
                    
                // FORMAT DATES IN 'DD-MM-YYYY' FORMAT
                case 'date-dd-mm-yyyy':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['d', 'm', 'Y'];
                    break;
                    
                // FORMAT TIMES IN 'HH-MM-SS' FORMAT
                case 'time-hh-mm-ss':
                    cleaveOptions.time = true;
                    cleaveOptions.timePattern = ['h', 'm', 's'];
                    break;
                    
                // FORMAT TIMES IN 'HH-MM' FORMAT
                case 'time-hh-mm':
                    cleaveOptions.time = true;
                    cleaveOptions.timePattern = ['h', 'm'];
                    break;
                    
                // FORMAT NUMBERS WITH THOUSANDS SEPARATORS
                case 'number-thousand':
                    cleaveOptions.numeral = true;
                    cleaveOptions.numeralThousandsGroupStyle = 'thousand';
                    break;
            }
        }
        
        new Cleave(element, cleaveOptions);
    }
});
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #33 v0.2 💙 AUTOMATICALLY FORMAT FORM INPUTS -->
<script src="https://cdn.jsdelivr.net/npm/cleave.js@1.6.0"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cleave.js/1.6.0/addons/cleave-phone.us.js"> </script>
<script>
document.addEventListener('DOMContentLoaded', function(){
    // SELECT ALL ELEMENTS WITH THE ATTRIBUTE "ms-code-autoformat" OR "ms-code-autoformat-prefix"
    const elements = document.querySelectorAll('[ms-code-autoformat], [ms-code-autoformat-prefix]');

    for (let element of elements) {
        const formatType = element.getAttribute('ms-code-autoformat');
        const prefix = element.getAttribute('ms-code-autoformat-prefix');
        
        // SET PREFIX
        let cleaveOptions = {
            prefix: prefix || '',
            blocks: [Infinity]
        };
        
        // BASED ON THE VALUE OF "ms-code-autoformat", FORMAT THE INPUT
        if (formatType) {
            switch (formatType) {
                // FORMAT PHONE NUMBERS
                case 'phone-number':
                    cleaveOptions.phone = true;
                    cleaveOptions.phoneRegionCode = 'US';
                    break;
                    
                // FORMAT DATES IN 'YYYY-MM-DD' FORMAT
                case 'date-yyyy-mm-dd':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['Y', 'm', 'd'];
                    break;
                    
                // FORMAT DATES IN 'MM-DD-YYYY' FORMAT
                case 'date-mm-dd-yyyy':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['m', 'd', 'Y'];
                    break;
                    
                // FORMAT DATES IN 'DD-MM-YYYY' FORMAT
                case 'date-dd-mm-yyyy':
                    cleaveOptions.date = true;
                    cleaveOptions.datePattern = ['d', 'm', 'Y'];
                    break;
                    
                // FORMAT TIMES IN 'HH-MM-SS' FORMAT
                case 'time-hh-mm-ss':
                    cleaveOptions.time = true;
                    cleaveOptions.timePattern = ['h', 'm', 's'];
                    break;
                    
                // FORMAT TIMES IN 'HH-MM' FORMAT
                case 'time-hh-mm':
                    cleaveOptions.time = true;
                    cleaveOptions.timePattern = ['h', 'm'];
                    break;
                    
                // FORMAT NUMBERS WITH THOUSANDS SEPARATORS
                case 'number-thousand':
                    cleaveOptions.numeral = true;
                    cleaveOptions.numeralThousandsGroupStyle = 'thousand';
                    break;
            }
        }
        
        new Cleave(element, cleaveOptions);
    }
});
</script>
Voir le Memberscript
Visibilité conditionnelle
Champs personnalisés

#32 - Définir l'entrée comme obligatoire si elle est visible

Créez des formulaires conditionnels en affichant ou en masquant les données requises.


<!-- 💙 MEMBERSCRIPT #32 v0.1 💙 REQUIRE INPUT IF VISIBLE -->
<script>
document.addEventListener("DOMContentLoaded", function() {

  // Function to check if an element is visible
  function isElementVisible(element) {
    return element.offsetParent !== null;
  }

  // Every time the user clicks on the document
  document.addEventListener('click', function() {

    // Get all inputs with the ms-code attribute
    const inputs = document.querySelectorAll('[ms-code="required-if-visible"]');

    // Loop through each input
    inputs.forEach(function(input) {

      // Check if the input or its parent is visible
      if (isElementVisible(input)) {

        // If the input is visible, add the required attribute
        input.required = true;
      } else {

        // If the input is not visible, remove the required attribute
        input.required = false;
      }
    });
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #32 v0.1 💙 REQUIRE INPUT IF VISIBLE -->
<script>
document.addEventListener("DOMContentLoaded", function() {

  // Function to check if an element is visible
  function isElementVisible(element) {
    return element.offsetParent !== null;
  }

  // Every time the user clicks on the document
  document.addEventListener('click', function() {

    // Get all inputs with the ms-code attribute
    const inputs = document.querySelectorAll('[ms-code="required-if-visible"]');

    // Loop through each input
    inputs.forEach(function(input) {

      // Check if the input or its parent is visible
      if (isElementVisible(input)) {

        // If the input is visible, add the required attribute
        input.required = true;
      } else {

        // If the input is not visible, remove the required attribute
        input.required = false;
      }
    });
  });
});
</script>
Voir le Memberscript
UX

#31 - Ouvrir un onglet du Webflow avec un lien

Ce script génère automatiquement des liens vers vos onglets Webflow.


<!-- 💙 MEMBERSCRIPT #31 v0.2 💙 OPEN WEBFLOW TAB w/ LINK -->
<!-- You can link to tabs like this 👉 www.yoursite.com#tab-name-lowercase -->
<!-- And sub tabs like this 👉 www.yoursite.com#tab-name/sub-tab-name -->
<script>
  var Webflow = Webflow || [];
  Webflow.push(() => {
    function changeTab(shouldScroll = false) {
      const hashSegments = window.location.hash.substring(1).split('/');
      const offset = 90; // change this to match your fixed header height if you have one
      let lastTabTarget;

      for (const segment of hashSegments) {
        const tabTarget = document.querySelector(`[data-w-tab="${segment}"]`);

        if (tabTarget) {
          tabTarget.click();
          lastTabTarget = tabTarget;
        }
      }

      if (shouldScroll && lastTabTarget) {
        window.scrollTo({
          top: $(lastTabTarget).offset().top - offset, behavior: 'smooth'
        });
      }
    }
    
    const tabs = document.querySelectorAll('[data-w-tab]');
    
    tabs.forEach(tab => {
      const dataWTabValue = tab.dataset.wTab;
      const parsedDataTab = dataWTabValue.replace(/\s+/g,"-").toLowerCase();
      tab.dataset.wTab = parsedDataTab;
      tab.addEventListener('click', () => {
        history.pushState({}, '', `#${parsedDataTab}`);
      });
    });
 
  	if (window.location.hash) {
      requestAnimationFrame(() => { changeTab(true); });
    }

    window.addEventListener('hashchange', () => { changeTab() });
  });
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #31 v0.2 💙 OPEN WEBFLOW TAB w/ LINK -->
<!-- You can link to tabs like this 👉 www.yoursite.com#tab-name-lowercase -->
<!-- And sub tabs like this 👉 www.yoursite.com#tab-name/sub-tab-name -->
<script>
  var Webflow = Webflow || [];
  Webflow.push(() => {
    function changeTab(shouldScroll = false) {
      const hashSegments = window.location.hash.substring(1).split('/');
      const offset = 90; // change this to match your fixed header height if you have one
      let lastTabTarget;

      for (const segment of hashSegments) {
        const tabTarget = document.querySelector(`[data-w-tab="${segment}"]`);

        if (tabTarget) {
          tabTarget.click();
          lastTabTarget = tabTarget;
        }
      }

      if (shouldScroll && lastTabTarget) {
        window.scrollTo({
          top: $(lastTabTarget).offset().top - offset, behavior: 'smooth'
        });
      }
    }
    
    const tabs = document.querySelectorAll('[data-w-tab]');
    
    tabs.forEach(tab => {
      const dataWTabValue = tab.dataset.wTab;
      const parsedDataTab = dataWTabValue.replace(/\s+/g,"-").toLowerCase();
      tab.dataset.wTab = parsedDataTab;
      tab.addEventListener('click', () => {
        history.pushState({}, '', `#${parsedDataTab}`);
      });
    });
 
  	if (window.location.hash) {
      requestAnimationFrame(() => { changeTab(true); });
    }

    window.addEventListener('hashchange', () => { changeTab() });
  });
</script>
Voir le Memberscript
UX

#30 - Compter les éléments sur la page et mettre à jour le nombre

Vérifier combien d'éléments dotés d'un attribut donné se trouvent sur la page et appliquer ce nombre à un texte.


<!-- 💙 MEMBERSCRIPT #30 v0.1 💙 COUNT ITEMS AND DISPLAY COUNT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  setTimeout(function() {
    const rollupItems = document.querySelectorAll('[ms-code-rollup-item]');
    const rollupNumbers = document.querySelectorAll('[ms-code-rollup-number]');

    const updateRollupNumbers = function() {
      const rollupCountMap = new Map();

      rollupItems.forEach(item => {
        const rollupKey = item.getAttribute('ms-code-rollup-item');
        const count = rollupCountMap.get(rollupKey) || 0;
        rollupCountMap.set(rollupKey, count + 1);
      });

      rollupNumbers.forEach(number => {
        const rollupKey = number.getAttribute('ms-code-rollup-number');
        const count = rollupCountMap.get(rollupKey) || 0;
        number.textContent = count;
      });
    };

    updateRollupNumbers(); // Initial update

    // Polling function to periodically update rollup numbers
    const pollRollupNumbers = function() {
      updateRollupNumbers();
      setTimeout(pollRollupNumbers, 1000); // Adjust the polling interval as needed (in milliseconds)
    };

    pollRollupNumbers(); // Start polling
  }, 2000);
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #30 v0.1 💙 COUNT ITEMS AND DISPLAY COUNT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  setTimeout(function() {
    const rollupItems = document.querySelectorAll('[ms-code-rollup-item]');
    const rollupNumbers = document.querySelectorAll('[ms-code-rollup-number]');

    const updateRollupNumbers = function() {
      const rollupCountMap = new Map();

      rollupItems.forEach(item => {
        const rollupKey = item.getAttribute('ms-code-rollup-item');
        const count = rollupCountMap.get(rollupKey) || 0;
        rollupCountMap.set(rollupKey, count + 1);
      });

      rollupNumbers.forEach(number => {
        const rollupKey = number.getAttribute('ms-code-rollup-number');
        const count = rollupCountMap.get(rollupKey) || 0;
        number.textContent = count;
      });
    };

    updateRollupNumbers(); // Initial update

    // Polling function to periodically update rollup numbers
    const pollRollupNumbers = function() {
      updateRollupNumbers();
      setTimeout(pollRollupNumbers, 1000); // Adjust the polling interval as needed (in milliseconds)
    };

    pollRollupNumbers(); // Start polling
  }, 2000);
});
</script>
Voir le Memberscript
UX

#29 - Fixer temporairement la hauteur des éléments au chargement

Forcer un élément à avoir une hauteur déterminée pendant une certaine durée lors du chargement de la page.


<!-- 💙 MEMBERSCRIPT #29 v0.1 💙 TEMPORARILY FIX ELEMENT HEIGHT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const elements = document.querySelectorAll('[ms-code-temp-height]');
  
  elements.forEach(element => {
    const attributeValue = element.getAttribute('ms-code-temp-height');
    
    if (attributeValue) {
      const [time, height] = attributeValue.split(':');
      
      if (!isNaN(time) && !isNaN(height)) {
        const defaultHeight = element.style.height;
        
        setTimeout(() => {
          element.style.height = defaultHeight;
        }, parseInt(time));
        
        element.style.height = height + 'px';
      }
    }
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #29 v0.1 💙 TEMPORARILY FIX ELEMENT HEIGHT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const elements = document.querySelectorAll('[ms-code-temp-height]');
  
  elements.forEach(element => {
    const attributeValue = element.getAttribute('ms-code-temp-height');
    
    if (attributeValue) {
      const [time, height] = attributeValue.split(':');
      
      if (!isNaN(time) && !isNaN(height)) {
        const defaultHeight = element.style.height;
        
        setTimeout(() => {
          element.style.height = defaultHeight;
        }, parseInt(time));
        
        element.style.height = height + 'px';
      }
    }
  });
});
</script>
Voir le Memberscript
Visibilité conditionnelle
JSON

#28 - Affichage d'un élément basé sur la transmission d'une date JSON

Vérifiez la date unique du point 27 et affichez/masquez un élément en fonction de cette date.


<!-- 💙 MEMBERSCRIPT #28 v0.1 💙 CHECK ONE-TIME DATE AND UPDATE ELEMENT DISPLAY -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  const updateElementVisibility = async function() {
    const member = await memberstack.getMemberJSON();

    if (!member.data || !member.data['one-time-date']) {
      // Member data or expiration date not available, do nothing
      return;
    }

    const expirationDate = new Date(member.data['one-time-date']);
    const currentDate = new Date();

    if (currentDate < expirationDate) {
      // Expiration date has not passed, update element visibility
      const elements = document.querySelectorAll('[ms-code-element-temporary]');

      elements.forEach(element => {
        const displayValue = element.getAttribute('ms-code-element-temporary');

        // Update element visibility based on the attribute value
        element.style.display = displayValue;
      });
    }
  };

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

<!-- 💙 MEMBERSCRIPT #28 v0.1 💙 CHECK ONE-TIME DATE AND UPDATE ELEMENT DISPLAY -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  const updateElementVisibility = async function() {
    const member = await memberstack.getMemberJSON();

    if (!member.data || !member.data['one-time-date']) {
      // Member data or expiration date not available, do nothing
      return;
    }

    const expirationDate = new Date(member.data['one-time-date']);
    const currentDate = new Date();

    if (currentDate < expirationDate) {
      // Expiration date has not passed, update element visibility
      const elements = document.querySelectorAll('[ms-code-element-temporary]');

      elements.forEach(element => {
        const displayValue = element.getAttribute('ms-code-element-temporary');

        // Update element visibility based on the attribute value
        element.style.display = displayValue;
      });
    }
  };

  updateElementVisibility();
});
</script>
Voir le Memberscript
Visibilité conditionnelle
JSON

#27 - Fixer une date unique pour l'inscription

Appliquez une date à votre JSON de membre après l'inscription, qui peut être utilisée pour n'importe quoi.

JSON uniquement

Si vous n'avez pas besoin d'ajouter la date à un champ personnalisé, utilisez ceci.


<!-- 💙 MEMBERSCRIPT #27 v0.1 💙 SET ONE TIME DATE -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  const updateDate = async function() {
    const member = await memberstack.getMemberJSON();

    if (!member.data) {
      member.data = {};
    }

    if (!member.data['one-time-date']) {
      const currentTime = new Date();
      const expirationTime = new Date(currentTime.getTime() + (3 * 60 * 60 * 1000)); // Set the expiration time to 3 hours (adjust as needed)
      member.data['one-time-date'] = expirationTime.toISOString();
      
      // Update member JSON
      await memberstack.updateMemberJSON({
        json: member.data
      });
    }
  };

  updateDate();
});
</script>

JSON + Champ personnalisé

Utilisez cette option si vous devez ajouter la date à un champ personnalisé (généralement dans le cadre d'automatismes).


<!-- 💙💙 MEMBERSCRIPT #27 v0.1.1 (CUSTOM FIELD) 💙 SET ONE TIME DATE -->
<script>
  document.addEventListener('DOMContentLoaded', async function() {
    const memberstack = window.$memberstackDom;
    const msMem = JSON.parse(localStorage.getItem('_ms-mem'));
    const member = await memberstack.getMemberJSON();

    if (!member.data) {
      member.data = {};
    }

    // Check if the user has the 'one-time-date' custom field in Memberstack
    if (!msMem.customFields || !msMem.customFields['one-time-date']) {
      const currentTime = new Date();
      const expirationTime = new Date(currentTime.getTime() + (3 * 60 * 60 * 1000)); // Set the expiration time to 3 hours (adjust as needed)
      const updatedCustomFields = {
        ...msMem.customFields,
        'one-time-date': expirationTime.toISOString()
      };

      member.data['one-time-date'] = expirationTime.toISOString();
      await memberstack.updateMemberJSON({
        json: member.data
      });

      await memberstack.updateMember({
        customFields: updatedCustomFields
      });
    }
  });
</script>
v0.1.1

JSON uniquement

Si vous n'avez pas besoin d'ajouter la date à un champ personnalisé, utilisez ceci.


<!-- 💙 MEMBERSCRIPT #27 v0.1 💙 SET ONE TIME DATE -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  const updateDate = async function() {
    const member = await memberstack.getMemberJSON();

    if (!member.data) {
      member.data = {};
    }

    if (!member.data['one-time-date']) {
      const currentTime = new Date();
      const expirationTime = new Date(currentTime.getTime() + (3 * 60 * 60 * 1000)); // Set the expiration time to 3 hours (adjust as needed)
      member.data['one-time-date'] = expirationTime.toISOString();
      
      // Update member JSON
      await memberstack.updateMemberJSON({
        json: member.data
      });
    }
  };

  updateDate();
});
</script>

JSON + Champ personnalisé

Utilisez cette option si vous devez ajouter la date à un champ personnalisé (généralement dans le cadre d'automatismes).


<!-- 💙💙 MEMBERSCRIPT #27 v0.1.1 (CUSTOM FIELD) 💙 SET ONE TIME DATE -->
<script>
  document.addEventListener('DOMContentLoaded', async function() {
    const memberstack = window.$memberstackDom;
    const msMem = JSON.parse(localStorage.getItem('_ms-mem'));
    const member = await memberstack.getMemberJSON();

    if (!member.data) {
      member.data = {};
    }

    // Check if the user has the 'one-time-date' custom field in Memberstack
    if (!msMem.customFields || !msMem.customFields['one-time-date']) {
      const currentTime = new Date();
      const expirationTime = new Date(currentTime.getTime() + (3 * 60 * 60 * 1000)); // Set the expiration time to 3 hours (adjust as needed)
      const updatedCustomFields = {
        ...msMem.customFields,
        'one-time-date': expirationTime.toISOString()
      };

      member.data['one-time-date'] = expirationTime.toISOString();
      await memberstack.updateMemberJSON({
        json: member.data
      });

      await memberstack.updateMember({
        customFields: updatedCustomFields
      });
    }
  });
</script>
Voir le Memberscript
Visibilité conditionnelle
UX
Marketing

#26 - Gate Content With Custom Modals

Utilisez des modèles personnalisés pour inciter vos visiteurs à obtenir un compte payant !


<!-- 💙 MEMBERSCRIPT #26 v0.1 💙 GATE CONTENT WITH MODALS -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
  if (!data) {
    // Member is not logged in
    const triggers = document.querySelectorAll('[ms-code-gate-modal-trigger]');
    const boxes = document.querySelectorAll('[ms-code-gate-modal-box]');
    
    triggers.forEach(trigger => {
      trigger.addEventListener('click', () => {
        const targetId = trigger.getAttribute('ms-code-gate-modal-trigger');
        const box = document.querySelector(`[ms-code-gate-modal-box="${targetId}"]`);
        if (box) {
          box.style.display = 'flex';
        }
      });
      // Remove links and attributes from trigger
      // Uncomment the lines below to enable this functionality
      // trigger.removeAttribute('href');
      // trigger.removeAttribute('target');
      // trigger.removeAttribute('rel');
      // trigger.removeAttribute('onclick');
    });
  }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #26 v0.1 💙 GATE CONTENT WITH MODALS -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
  if (!data) {
    // Member is not logged in
    const triggers = document.querySelectorAll('[ms-code-gate-modal-trigger]');
    const boxes = document.querySelectorAll('[ms-code-gate-modal-box]');
    
    triggers.forEach(trigger => {
      trigger.addEventListener('click', () => {
        const targetId = trigger.getAttribute('ms-code-gate-modal-trigger');
        const box = document.querySelector(`[ms-code-gate-modal-box="${targetId}"]`);
        if (box) {
          box.style.display = 'flex';
        }
      });
      // Remove links and attributes from trigger
      // Uncomment the lines below to enable this functionality
      // trigger.removeAttribute('href');
      // trigger.removeAttribute('target');
      // trigger.removeAttribute('rel');
      // trigger.removeAttribute('onclick');
    });
  }
});
</script>
Voir le Memberscript
Visibilité conditionnelle
UX

#25 - Masquer un élément en fonction des enfants d'un autre élément

Supprime un élément de la page si un autre élément défini n'a pas d'éléments enfants.


<!-- 💙 MEMBERSCRIPT #25 v0.1 💙 HIDE ELEMENT BASED ON OTHER ELEMENT CHILDREN -->
<script>
window.addEventListener('DOMContentLoaded', function() {
  const subjectAttribute = 'ms-code-visibility-subject';
  const targetAttribute = 'ms-code-visibility-target';

  const subjectElement = document.querySelector(`[${subjectAttribute}]`);
  const targetElement = document.querySelector(`[${targetAttribute}]`);

  if (!subjectElement || !targetElement) {
    console.error('Subject or target element not found');
    return;
  }

  function checkVisibility() {
    const children = subjectElement.children;
    let allHidden = true;

    for (let i = 0; i < children.length; i++) {
      const child = children[i];
      const computedStyle = window.getComputedStyle(child);

      if (computedStyle.display !== 'none') {
        allHidden = false;
        break;
      }
    }

    if (children.length === 0 || allHidden) {
      targetElement.style.display = 'none';
    } else {
      targetElement.style.display = '';
    }
  }

  // Check visibility initially
  checkVisibility();

  // Check visibility whenever the subject element or its children change
  const observer = new MutationObserver(checkVisibility);
  observer.observe(subjectElement, { childList: true, subtree: true });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #25 v0.1 💙 HIDE ELEMENT BASED ON OTHER ELEMENT CHILDREN -->
<script>
window.addEventListener('DOMContentLoaded', function() {
  const subjectAttribute = 'ms-code-visibility-subject';
  const targetAttribute = 'ms-code-visibility-target';

  const subjectElement = document.querySelector(`[${subjectAttribute}]`);
  const targetElement = document.querySelector(`[${targetAttribute}]`);

  if (!subjectElement || !targetElement) {
    console.error('Subject or target element not found');
    return;
  }

  function checkVisibility() {
    const children = subjectElement.children;
    let allHidden = true;

    for (let i = 0; i < children.length; i++) {
      const child = children[i];
      const computedStyle = window.getComputedStyle(child);

      if (computedStyle.display !== 'none') {
        allHidden = false;
        break;
      }
    }

    if (children.length === 0 || allHidden) {
      targetElement.style.display = 'none';
    } else {
      targetElement.style.display = '';
    }
  }

  // Check visibility initially
  checkVisibility();

  // Check visibility whenever the subject element or its children change
  const observer = new MutationObserver(checkVisibility);
  observer.observe(subjectElement, { childList: true, subtree: true });
});
</script>
Voir le Memberscript
Visibilité conditionnelle

#24 - Filtrer les listes en fonction de l'élément

Filtrer n'importe quel type de liste en fonction de la présence d'un élément parmi ses enfants.

Option standard

Cela fonctionne dans la plupart des cas.


<!-- 💙 MEMBERSCRIPT #24 v0.1 💙 FILTER ITEMS WITHIN LIST BASED ON ELEMENT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const filterListItems = function(list, filterAttribute) {
    const items = list.querySelectorAll(`[ms-code-filter-item="${filterAttribute}"]`);

    items.forEach(item => {
      const target = item.querySelector(`[ms-code-filter-target="${filterAttribute}"]`);

      if (!target || window.getComputedStyle(target).display === 'none') {
        item.style.display = 'none';
      } else {
        item.style.display = '';
      }
    });
  };

  const filterLists = document.querySelectorAll('[ms-code-filter-list]');

  const updateFiltering = function() {
    filterLists.forEach(list => {
      const filterAttribute = list.getAttribute('ms-code-filter-list');
      filterListItems(list, filterAttribute);
    });
  };

  const observeListChanges = function() {
    const observer = new MutationObserver(updateFiltering);
    filterLists.forEach(list => observer.observe(list, { childList: true, subtree: true }));
  };

  updateFiltering();
  observeListChanges();
});
</script>

Option d'interrogation

Si la norme ne fonctionne pas, essayez ceci.


<!-- 💙 MEMBERSCRIPT #24 v0.1.1 💙 FILTER ITEMS WITHIN LIST BASED ON ELEMENT (POLLING) -->
<script>
window.addEventListener("DOMContentLoaded", function() {
  const filterListItems = function(list, filterAttribute) {
    const items = list.querySelectorAll(`[ms-code-filter-item="${filterAttribute}"]`);

    items.forEach(item => {
      const target = item.querySelector(`[ms-code-filter-target="${filterAttribute}"]`);

      if (!target || window.getComputedStyle(target).display === 'none') {
        item.style.display = 'none';
      } else {
        item.style.display = '';
      }
    });
  };

  const filterLists = document.querySelectorAll('[ms-code-filter-list]');

  const updateFiltering = function() {
    filterLists.forEach(list => {
      const filterAttribute = list.getAttribute('ms-code-filter-list');
      filterListItems(list, filterAttribute);
    });
  };

  const pollPage = function() {
    updateFiltering();
    setTimeout(pollPage, 1000); // Poll every 1 second
  };

  pollPage();
});
</script>
v0.1.1

Option standard

Cela fonctionne dans la plupart des cas.


<!-- 💙 MEMBERSCRIPT #24 v0.1 💙 FILTER ITEMS WITHIN LIST BASED ON ELEMENT -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const filterListItems = function(list, filterAttribute) {
    const items = list.querySelectorAll(`[ms-code-filter-item="${filterAttribute}"]`);

    items.forEach(item => {
      const target = item.querySelector(`[ms-code-filter-target="${filterAttribute}"]`);

      if (!target || window.getComputedStyle(target).display === 'none') {
        item.style.display = 'none';
      } else {
        item.style.display = '';
      }
    });
  };

  const filterLists = document.querySelectorAll('[ms-code-filter-list]');

  const updateFiltering = function() {
    filterLists.forEach(list => {
      const filterAttribute = list.getAttribute('ms-code-filter-list');
      filterListItems(list, filterAttribute);
    });
  };

  const observeListChanges = function() {
    const observer = new MutationObserver(updateFiltering);
    filterLists.forEach(list => observer.observe(list, { childList: true, subtree: true }));
  };

  updateFiltering();
  observeListChanges();
});
</script>

Option d'interrogation

Si la norme ne fonctionne pas, essayez ceci.


<!-- 💙 MEMBERSCRIPT #24 v0.1.1 💙 FILTER ITEMS WITHIN LIST BASED ON ELEMENT (POLLING) -->
<script>
window.addEventListener("DOMContentLoaded", function() {
  const filterListItems = function(list, filterAttribute) {
    const items = list.querySelectorAll(`[ms-code-filter-item="${filterAttribute}"]`);

    items.forEach(item => {
      const target = item.querySelector(`[ms-code-filter-target="${filterAttribute}"]`);

      if (!target || window.getComputedStyle(target).display === 'none') {
        item.style.display = 'none';
      } else {
        item.style.display = '';
      }
    });
  };

  const filterLists = document.querySelectorAll('[ms-code-filter-list]');

  const updateFiltering = function() {
    filterLists.forEach(list => {
      const filterAttribute = list.getAttribute('ms-code-filter-list');
      filterListItems(list, filterAttribute);
    });
  };

  const pollPage = function() {
    updateFiltering();
    setTimeout(pollPage, 1000); // Poll every 1 second
  };

  pollPage();
});
</script>
Voir le Memberscript
UX

#23 - Écrans squelettes / chargeurs de contenu

Ajoutez facilement ces états de chargement standard à votre site en quelques secondes.

Mode lumineux

A utiliser sur fond blanc


<!-- 💙 MEMBERSCRIPT #23 v0.1 💙 SKELETON SCREENS/CONTENT LOADERS -->
<script>
window.addEventListener("DOMContentLoaded", (event) => {
  const skeletonElements = document.querySelectorAll('[ms-code-skeleton]');
  
  skeletonElements.forEach(element => {
    // Create a skeleton div
    const skeletonDiv = document.createElement('div');
    skeletonDiv.classList.add('skeleton-loader');

    // Add the skeleton div to the current element
    element.style.position = 'relative';
    element.appendChild(skeletonDiv);

    // Get delay from the attribute
    let delay = element.getAttribute('ms-code-skeleton');

    // If attribute value is not a number, set default delay as 2000ms
    if (isNaN(delay)) {
      delay = 2000;
    }

    setTimeout(() => {
      // Remove the skeleton loader div after delay
      const skeletonDiv = element.querySelector('.skeleton-loader');
      element.removeChild(skeletonDiv);
    }, delay);
  });
});
</script>
<style>
.skeleton-loader {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: inherit; /* Inherit the border-radius of the parent element */
  background: linear-gradient(to right, #f6f7f8 25%, #e0e0e0 50%, #f6f7f8 75%);
  background-size: 200% 100%;  /* Increase the size of the background image */
  z-index: 1; /* Make sure the skeleton loader is on top of the content */
  animation: skeleton 1s infinite linear;
}

@keyframes skeleton {
  0% { background-position: -100% 0; }
  100% { background-position: 100% 0; }
}

[ms-code-skeleton] {
  background-clip: padding-box;
}
</style>

Mode sombre

A utiliser sur fond noir


<!-- 💙 MEMBERSCRIPT #23 v0.1 💙 SKELETON SCREENS/CONTENT LOADERS -->
<script>
window.addEventListener("DOMContentLoaded", (event) => {
  const skeletonElements = document.querySelectorAll('[ms-code-skeleton]');
  
  skeletonElements.forEach(element => {
    // Create a skeleton div
    const skeletonDiv = document.createElement('div');
    skeletonDiv.classList.add('skeleton-loader');

    // Add the skeleton div to the current element
    element.style.position = 'relative';
    element.appendChild(skeletonDiv);

    // Get delay from the attribute
    let delay = element.getAttribute('ms-code-skeleton');

    // If attribute value is not a number, set default delay as 2000ms
    if (isNaN(delay)) {
      delay = 2000;
    }

    setTimeout(() => {
      // Remove the skeleton loader div after delay
      const skeletonDiv = element.querySelector('.skeleton-loader');
      element.removeChild(skeletonDiv);
    }, delay);
  });
});
</script>
<style>
.skeleton-loader {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: inherit;
  background: linear-gradient(to right, #222222 25%, #333333 50%, #222222 75%); /* Updated background colors */
  background-size: 200% 100%;
  z-index: 1;
  animation: skeleton 1s infinite linear;
}

@keyframes skeleton {
  0% { background-position: -100% 0; }
  100% { background-position: 100% 0; }
}

[ms-code-skeleton] {
  background-clip: padding-box;
}
</style>
v0.1

Mode lumineux

A utiliser sur fond blanc


<!-- 💙 MEMBERSCRIPT #23 v0.1 💙 SKELETON SCREENS/CONTENT LOADERS -->
<script>
window.addEventListener("DOMContentLoaded", (event) => {
  const skeletonElements = document.querySelectorAll('[ms-code-skeleton]');
  
  skeletonElements.forEach(element => {
    // Create a skeleton div
    const skeletonDiv = document.createElement('div');
    skeletonDiv.classList.add('skeleton-loader');

    // Add the skeleton div to the current element
    element.style.position = 'relative';
    element.appendChild(skeletonDiv);

    // Get delay from the attribute
    let delay = element.getAttribute('ms-code-skeleton');

    // If attribute value is not a number, set default delay as 2000ms
    if (isNaN(delay)) {
      delay = 2000;
    }

    setTimeout(() => {
      // Remove the skeleton loader div after delay
      const skeletonDiv = element.querySelector('.skeleton-loader');
      element.removeChild(skeletonDiv);
    }, delay);
  });
});
</script>
<style>
.skeleton-loader {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: inherit; /* Inherit the border-radius of the parent element */
  background: linear-gradient(to right, #f6f7f8 25%, #e0e0e0 50%, #f6f7f8 75%);
  background-size: 200% 100%;  /* Increase the size of the background image */
  z-index: 1; /* Make sure the skeleton loader is on top of the content */
  animation: skeleton 1s infinite linear;
}

@keyframes skeleton {
  0% { background-position: -100% 0; }
  100% { background-position: 100% 0; }
}

[ms-code-skeleton] {
  background-clip: padding-box;
}
</style>

Mode sombre

A utiliser sur fond noir


<!-- 💙 MEMBERSCRIPT #23 v0.1 💙 SKELETON SCREENS/CONTENT LOADERS -->
<script>
window.addEventListener("DOMContentLoaded", (event) => {
  const skeletonElements = document.querySelectorAll('[ms-code-skeleton]');
  
  skeletonElements.forEach(element => {
    // Create a skeleton div
    const skeletonDiv = document.createElement('div');
    skeletonDiv.classList.add('skeleton-loader');

    // Add the skeleton div to the current element
    element.style.position = 'relative';
    element.appendChild(skeletonDiv);

    // Get delay from the attribute
    let delay = element.getAttribute('ms-code-skeleton');

    // If attribute value is not a number, set default delay as 2000ms
    if (isNaN(delay)) {
      delay = 2000;
    }

    setTimeout(() => {
      // Remove the skeleton loader div after delay
      const skeletonDiv = element.querySelector('.skeleton-loader');
      element.removeChild(skeletonDiv);
    }, delay);
  });
});
</script>
<style>
.skeleton-loader {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: inherit;
  background: linear-gradient(to right, #222222 25%, #333333 50%, #222222 75%); /* Updated background colors */
  background-size: 200% 100%;
  z-index: 1;
  animation: skeleton 1s infinite linear;
}

@keyframes skeleton {
  0% { background-position: -100% 0; }
  100% { background-position: 100% 0; }
}

[ms-code-skeleton] {
  background-clip: padding-box;
}
</style>
Voir le Memberscript
Visibilité conditionnelle
UX

#22 - Désactiver le bouton Soumettre jusqu'à ce que les champs requis soient remplis

Faites griser votre bouton d'envoi jusqu'à ce que toutes les valeurs requises soient renseignées.


<!-- 💙 MEMBERSCRIPT #22 v0.1 💙 DISABLE SUBMIT BUTTON UNTIL REQUIRED FIELDS ARE COMPLETE -->
<script>
window.onload = function() {
  const forms = document.querySelectorAll('form[ms-code-submit-form]');

  forms.forEach(form => {
    const submitButton = form.querySelector('input[type="submit"]');
    const requiredFields = form.querySelectorAll('input[required]');

    form.addEventListener('input', function() {
      const allFilled = Array.from(requiredFields).every(field => field.value.trim() !== '');

      if (allFilled) {
        submitButton.classList.add('submit-enabled');
      } else {
        submitButton.classList.remove('submit-enabled');
      }
    });
  });
};
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #22 v0.1 💙 DISABLE SUBMIT BUTTON UNTIL REQUIRED FIELDS ARE COMPLETE -->
<script>
window.onload = function() {
  const forms = document.querySelectorAll('form[ms-code-submit-form]');

  forms.forEach(form => {
    const submitButton = form.querySelector('input[type="submit"]');
    const requiredFields = form.querySelectorAll('input[required]');

    form.addEventListener('input', function() {
      const allFilled = Array.from(requiredFields).every(field => field.value.trim() !== '');

      if (allFilled) {
        submitButton.classList.add('submit-enabled');
      } else {
        submitButton.classList.remove('submit-enabled');
      }
    });
  });
};
</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.