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

#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
Visibilité conditionnelle
UX

#21 - Notifications personnalisées sur Toast

Affichez des boîtes à toasts personnalisées en cliquant sur un élément !


<!-- 💙 MEMBERSCRIPT #21 v0.1 💙 CUSTOM TOAST BOXES -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const toastTriggers = document.querySelectorAll("[ms-code-toast-trigger]");

  toastTriggers.forEach(trigger => {
    trigger.addEventListener("click", function() {
      const triggerId = trigger.getAttribute("ms-code-toast-trigger");
      const toastBox = document.querySelector(`[ms-code-toast-box="${triggerId}"]`);

      if (toastBox) {
        const fadeInDuration = 200;
        const fadeOutDuration = 200;
        const staticDuration = 2000;
        const totalDuration = fadeInDuration + staticDuration + fadeOutDuration;

        toastBox.style.opacity = "0";
        toastBox.style.display = "block";

        let currentTime = 0;

        const fade = function() {
          currentTime += 10;
          const opacity = currentTime < fadeInDuration
            ? currentTime / fadeInDuration
            : currentTime < fadeInDuration + staticDuration
              ? 1
              : 1 - (currentTime - fadeInDuration - staticDuration) / fadeOutDuration;

          toastBox.style.opacity = opacity;

          if (currentTime < totalDuration) {
            setTimeout(fade, 10);
          } else {
            toastBox.style.display = "none";
          }
        };

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

<!-- 💙 MEMBERSCRIPT #21 v0.1 💙 CUSTOM TOAST BOXES -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  const toastTriggers = document.querySelectorAll("[ms-code-toast-trigger]");

  toastTriggers.forEach(trigger => {
    trigger.addEventListener("click", function() {
      const triggerId = trigger.getAttribute("ms-code-toast-trigger");
      const toastBox = document.querySelector(`[ms-code-toast-box="${triggerId}"]`);

      if (toastBox) {
        const fadeInDuration = 200;
        const fadeOutDuration = 200;
        const staticDuration = 2000;
        const totalDuration = fadeInDuration + staticDuration + fadeOutDuration;

        toastBox.style.opacity = "0";
        toastBox.style.display = "block";

        let currentTime = 0;

        const fade = function() {
          currentTime += 10;
          const opacity = currentTime < fadeInDuration
            ? currentTime / fadeInDuration
            : currentTime < fadeInDuration + staticDuration
              ? 1
              : 1 - (currentTime - fadeInDuration - staticDuration) / fadeOutDuration;

          toastBox.style.opacity = opacity;

          if (currentTime < totalDuration) {
            setTimeout(fade, 10);
          } else {
            toastBox.style.display = "none";
          }
        };

        fade();
      }
    });
  });
});
</script>
Voir le Memberscript
Champs personnalisés

#19 - Ajouter l'URL d'un champ personnalisé à l'IFrame SRC

Créez une fonctionnalité d'intégration spécifique aux membres avec cette solution de champ personnalisé iframe !


<!-- 💙 MEMBERSCRIPT #19 v0.1 💙 ADD CUSTOM FIELD AS AN IFRAME SRC -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  // Parse member data from local storage
  const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');

  // Check if the user is logged in
  if(memberData && memberData.id) {
    // Get custom fields
    const customFields = memberData.customFields;

    // Select all elements with 'ms-code-field-link' attribute
    const elements = document.querySelectorAll('[ms-code-field-link]');
    
    // Iterate over all selected elements
    elements.forEach(element => {
      // Get custom field key from 'ms-code-field-link' attribute
      const fieldKey = element.getAttribute('ms-code-field-link');
      
      // If key exists in custom fields, set element src to the corresponding value
      if(customFields.hasOwnProperty(fieldKey)) {
        element.setAttribute('src', customFields[fieldKey]);
      }
    });
  }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #19 v0.1 💙 ADD CUSTOM FIELD AS AN IFRAME SRC -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  // Parse member data from local storage
  const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');

  // Check if the user is logged in
  if(memberData && memberData.id) {
    // Get custom fields
    const customFields = memberData.customFields;

    // Select all elements with 'ms-code-field-link' attribute
    const elements = document.querySelectorAll('[ms-code-field-link]');
    
    // Iterate over all selected elements
    elements.forEach(element => {
      // Get custom field key from 'ms-code-field-link' attribute
      const fieldKey = element.getAttribute('ms-code-field-link');
      
      // If key exists in custom fields, set element src to the corresponding value
      if(customFields.hasOwnProperty(fieldKey)) {
        element.setAttribute('src', customFields[fieldKey]);
      }
    });
  }
});
</script>
Voir le Memberscript
Marketing

#18 - Tronquer facilement le texte

Ajoutez un attribut et un simple script pour tronquer le texte de manière programmée !


<!-- 💙 MEMBERSCRIPT #18 v0.2 💙 - EASILY TRUNCATE TEXT -->
<script>
const elements = document.querySelectorAll('[ms-code-truncate]');

elements.forEach((element) => {
  const charLimit = parseInt(element.getAttribute('ms-code-truncate'));

  // Create a helper function that will recursively traverse the DOM tree
  const traverseNodes = (node, count) => {
    for (let child of node.childNodes) {
      // If the node is a text node, truncate if necessary
      if (child.nodeType === Node.TEXT_NODE) {
        if (count + child.textContent.length > charLimit) {
          child.textContent = child.textContent.slice(0, charLimit - count) + '...';
          return count + child.textContent.length;
        }
        count += child.textContent.length;
      }
      // If the node is an element, recurse through its children
      else if (child.nodeType === Node.ELEMENT_NODE) {
        count = traverseNodes(child, count);
      }
    }
    return count;
  }

  // Create a deep clone of the element to work on. This is so that we don't modify the original element
  // until we have completely finished processing.
  const clone = element.cloneNode(true);

  // Traverse and truncate the cloned node
  traverseNodes(clone, 0);

  // Replace the original element with our modified clone
  element.parentNode.replaceChild(clone, element);
});
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #18 v0.2 💙 - EASILY TRUNCATE TEXT -->
<script>
const elements = document.querySelectorAll('[ms-code-truncate]');

elements.forEach((element) => {
  const charLimit = parseInt(element.getAttribute('ms-code-truncate'));

  // Create a helper function that will recursively traverse the DOM tree
  const traverseNodes = (node, count) => {
    for (let child of node.childNodes) {
      // If the node is a text node, truncate if necessary
      if (child.nodeType === Node.TEXT_NODE) {
        if (count + child.textContent.length > charLimit) {
          child.textContent = child.textContent.slice(0, charLimit - count) + '...';
          return count + child.textContent.length;
        }
        count += child.textContent.length;
      }
      // If the node is an element, recurse through its children
      else if (child.nodeType === Node.ELEMENT_NODE) {
        count = traverseNodes(child, count);
      }
    }
    return count;
  }

  // Create a deep clone of the element to work on. This is so that we don't modify the original element
  // until we have completely finished processing.
  const clone = element.cloneNode(true);

  // Traverse and truncate the cloned node
  traverseNodes(clone, 0);

  // Replace the original element with our modified clone
  element.parentNode.replaceChild(clone, element);
});
</script>
Voir le Memberscript
Champs personnalisés

#17 - Création d'un lien à partir d'un champ personnalisé

Utilisez des champs personnalisés pour remplir les cibles de liens avec un seul attribut !


<!-- 💙 MEMBERSCRIPT #17 v0.3 💙 ADD CUSTOM FIELD AS A LINK -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');
    if (!memberData?.id) return;

    document.querySelectorAll('[ms-code-field-link]').forEach(element => {
      const fieldKey = element.getAttribute('ms-code-field-link');
      const fieldValue = memberData.customFields?.[fieldKey]?.trim();

      if (!fieldValue) {
        element.style.display = 'none';
        return;
      }

      try {
        // Add protocol if missing and validate URL
        const url = !/^https?:\/\//i.test(fieldValue) ? 'https://' + fieldValue : fieldValue;
        new URL(url); // Will throw if invalid URL

        element.href = url;
        element.rel = 'noopener noreferrer';
        element.target = '_blank';
      } catch {
        element.style.display = 'none';
      }
    });
  });
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #17 v0.3 💙 ADD CUSTOM FIELD AS A LINK -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');
    if (!memberData?.id) return;

    document.querySelectorAll('[ms-code-field-link]').forEach(element => {
      const fieldKey = element.getAttribute('ms-code-field-link');
      const fieldValue = memberData.customFields?.[fieldKey]?.trim();

      if (!fieldValue) {
        element.style.display = 'none';
        return;
      }

      try {
        // Add protocol if missing and validate URL
        const url = !/^https?:\/\//i.test(fieldValue) ? 'https://' + fieldValue : fieldValue;
        new URL(url); // Will throw if invalid URL

        element.href = url;
        element.rel = 'noopener noreferrer';
        element.target = '_blank';
      } catch {
        element.style.display = 'none';
      }
    });
  });
</script>
Voir le Memberscript
JSON

#16 - Fin de la session après X minutes d'inactivité

Ce script redirige les utilisateurs inactifs vers une page "Session expirée" après une certaine période d'inactivité. Un compte à rebours est affiché sur la page et se réinitialise en fonction de l'activité de la page.

Add this code before the closing </body> tag any page which needs a countdown timer.


<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
let logoutTimer;
let countdownInterval;
let initialTime;

function startLogoutTimer() {
  // Get the logout time from the HTML element
  const timeElement = document.querySelector('[ms-code-logout-timer]');
  const timeParts = timeElement.textContent.split(':');
  const minutes = parseInt(timeParts[0], 10);
  const seconds = parseInt(timeParts[1], 10);
  const LOGOUT_TIME = (minutes * 60 + seconds) * 1000; // Convert to milliseconds

  // Store the initial time value
  if (!initialTime) {
    initialTime = LOGOUT_TIME;
  }

  // Clear the previous timer, if any
  clearTimeout(logoutTimer);
  clearInterval(countdownInterval);

  let startTime = Date.now();

  // Start a new timer to redirect the user after the specified time
  logoutTimer = setTimeout(() => {
    window.location.href = "/expired";
    startLogoutTimer(); // Start the logout timer again
  }, LOGOUT_TIME); 

  // Start a countdown timer
  countdownInterval = setInterval(() => {
    let elapsed = Date.now() - startTime;
    let remaining = LOGOUT_TIME - elapsed;
    updateCountdownDisplay(remaining);
  }, 1000); // update every second
}

function updateCountdownDisplay(remainingTimeInMs) {
  // convert ms to MM:SS format
  let minutes = Math.floor(remainingTimeInMs / 1000 / 60);
  let seconds = Math.floor((remainingTimeInMs / 1000) % 60);
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;

  const timeElement = document.querySelector('[ms-code-logout-timer]');
  timeElement.textContent = `${minutes}:${seconds}`;
}

// Call this function whenever the user interacts with the page
function resetLogoutTimer() {
  const timeElement = document.querySelector('[ms-code-logout-timer]');
  timeElement.textContent = formatTime(initialTime); // Reset to the initial time
  startLogoutTimer();
}

function formatTime(timeInMs) {
  let minutes = Math.floor(timeInMs / 1000 / 60);
  let seconds = Math.floor((timeInMs / 1000) % 60);
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;
  return `${minutes}:${seconds}`;
}

// Call this function when the user logs in
function cancelLogoutTimer() {
  clearTimeout(logoutTimer);
  clearInterval(countdownInterval);
}

// Start the timer when the page loads
startLogoutTimer();

// Add event listeners to reset timer on any page activity
document.addEventListener('mousemove', resetLogoutTimer);
document.addEventListener('keypress', resetLogoutTimer);
document.addEventListener('touchstart', resetLogoutTimer);

</script>

Add this code to your /expired page before the closing </body> tag.


<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
window.addEventListener('load', () => {
  window.$memberstackDom.getCurrentMember().then(async ({ data: member }) => {   
    if (member) {
      try {
        await window.$memberstackDom.logout();
        console.log('Logged out successfully');
        
        setTimeout(() => {
          location.reload();
        }, 1000); // Refresh the page 1 second after logout

      } catch (error) {
        console.error(error);
      }
    }
  });
});
</script>
v0.2

Add this code before the closing </body> tag any page which needs a countdown timer.


<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
let logoutTimer;
let countdownInterval;
let initialTime;

function startLogoutTimer() {
  // Get the logout time from the HTML element
  const timeElement = document.querySelector('[ms-code-logout-timer]');
  const timeParts = timeElement.textContent.split(':');
  const minutes = parseInt(timeParts[0], 10);
  const seconds = parseInt(timeParts[1], 10);
  const LOGOUT_TIME = (minutes * 60 + seconds) * 1000; // Convert to milliseconds

  // Store the initial time value
  if (!initialTime) {
    initialTime = LOGOUT_TIME;
  }

  // Clear the previous timer, if any
  clearTimeout(logoutTimer);
  clearInterval(countdownInterval);

  let startTime = Date.now();

  // Start a new timer to redirect the user after the specified time
  logoutTimer = setTimeout(() => {
    window.location.href = "/expired";
    startLogoutTimer(); // Start the logout timer again
  }, LOGOUT_TIME); 

  // Start a countdown timer
  countdownInterval = setInterval(() => {
    let elapsed = Date.now() - startTime;
    let remaining = LOGOUT_TIME - elapsed;
    updateCountdownDisplay(remaining);
  }, 1000); // update every second
}

function updateCountdownDisplay(remainingTimeInMs) {
  // convert ms to MM:SS format
  let minutes = Math.floor(remainingTimeInMs / 1000 / 60);
  let seconds = Math.floor((remainingTimeInMs / 1000) % 60);
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;

  const timeElement = document.querySelector('[ms-code-logout-timer]');
  timeElement.textContent = `${minutes}:${seconds}`;
}

// Call this function whenever the user interacts with the page
function resetLogoutTimer() {
  const timeElement = document.querySelector('[ms-code-logout-timer]');
  timeElement.textContent = formatTime(initialTime); // Reset to the initial time
  startLogoutTimer();
}

function formatTime(timeInMs) {
  let minutes = Math.floor(timeInMs / 1000 / 60);
  let seconds = Math.floor((timeInMs / 1000) % 60);
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;
  return `${minutes}:${seconds}`;
}

// Call this function when the user logs in
function cancelLogoutTimer() {
  clearTimeout(logoutTimer);
  clearInterval(countdownInterval);
}

// Start the timer when the page loads
startLogoutTimer();

// Add event listeners to reset timer on any page activity
document.addEventListener('mousemove', resetLogoutTimer);
document.addEventListener('keypress', resetLogoutTimer);
document.addEventListener('touchstart', resetLogoutTimer);

</script>

Add this code to your /expired page before the closing </body> tag.


<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
window.addEventListener('load', () => {
  window.$memberstackDom.getCurrentMember().then(async ({ data: member }) => {   
    if (member) {
      try {
        await window.$memberstackDom.logout();
        console.log('Logged out successfully');
        
        setTimeout(() => {
          location.reload();
        }, 1000); // Refresh the page 1 second after logout

      } catch (error) {
        console.error(error);
      }
    }
  });
});
</script>
Voir le Memberscript
UX

#15 - Rafraîchir la page après une durée définie au clic

Actualiser la page au bout d'un certain temps lorsqu'un élément est cliqué.


<!-- 💙 MEMBERSCRIPT #15 v0.1 💙 TRIGGER REFRESH ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  document.addEventListener("click", function(event) {
    const clickedElement = event.target.closest('[ms-code-refresh]');

    if (clickedElement) {
      const refreshDelay = parseInt(clickedElement.getAttribute('ms-code-refresh'));

      if (!isNaN(refreshDelay)) {
        setTimeout(function() {
          location.reload();
        }, refreshDelay);
      }
    }
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #15 v0.1 💙 TRIGGER REFRESH ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  document.addEventListener("click", function(event) {
    const clickedElement = event.target.closest('[ms-code-refresh]');

    if (clickedElement) {
      const refreshDelay = parseInt(clickedElement.getAttribute('ms-code-refresh'));

      if (!isNaN(refreshDelay)) {
        setTimeout(function() {
          location.reload();
        }, refreshDelay);
      }
    }
  });
});
</script>
Voir le Memberscript
UX

#14 - Créer un état de chargement au clic

Simuler un état de chargement personnalisé lorsqu'un élément est cliqué.


<!-- 💙 MEMBERSCRIPT #14 v0.1 💙 CREATE LOADING STATE ON CLICK -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous" ></script>
<script>
$(document).ready(function() {

    // Bind click event to elements with ms-code-loading-trigger attribute
    $(document).on('click', '[ms-code-loading-trigger]', function() {
        // Get the value of ms-code-loading-trigger attribute
        var triggerValue = $(this).attr("ms-code-loading-trigger");

        // Find the matching loading element
        var loadingElement = $("[ms-code-loading-element='" + triggerValue + "']");
        
        // Find the matching subject element
        var subjectElement = $("[ms-code-loading-subject='" + triggerValue + "']");

        // Show the loading element (with flex display), append it to the subject element
        loadingElement.css('display', 'flex').appendTo(subjectElement);

        // After 5 seconds (5000 milliseconds), hide the loading element
        setTimeout(function() {
            loadingElement.hide();
        }, 5000);
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #14 v0.1 💙 CREATE LOADING STATE ON CLICK -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous" ></script>
<script>
$(document).ready(function() {

    // Bind click event to elements with ms-code-loading-trigger attribute
    $(document).on('click', '[ms-code-loading-trigger]', function() {
        // Get the value of ms-code-loading-trigger attribute
        var triggerValue = $(this).attr("ms-code-loading-trigger");

        // Find the matching loading element
        var loadingElement = $("[ms-code-loading-element='" + triggerValue + "']");
        
        // Find the matching subject element
        var subjectElement = $("[ms-code-loading-subject='" + triggerValue + "']");

        // Show the loading element (with flex display), append it to the subject element
        loadingElement.css('display', 'flex').appendTo(subjectElement);

        // After 5 seconds (5000 milliseconds), hide the loading element
        setTimeout(function() {
            loadingElement.hide();
        }, 5000);
    });
});
</script>
Voir le Memberscript
JSON
Visibilité conditionnelle

#13 - Filtrer les groupes d'éléments JSON

Affichez des listes filtrées à vos membres sur la base d'une propriété JSON !


<!-- 💙 MEMBERSCRIPT #13 v0.2 💙 FILTER ITEM GROUPS FROM JSON BASED ON ATTRIBUTE VALUE -->
<script>
(function() {
  const memberstack = window.$memberstackDom;
  let member;

  const fetchMemberJSON = async function() {
    member = await memberstack.getMemberJSON();
    return member;
  }

  const filterItems = async function(printList) {
    const filterAttr = printList.getAttribute('ms-code-list-filter');
    if (!filterAttr) return;

    const filters = filterAttr.split(',').map(filter => filter.trim());
    const jsonGroup = printList.getAttribute('ms-code-print-list');
    const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];

    const itemContainer = document.createElement('div');
    const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
    const itemTemplate = placeholder.outerHTML;

    items.forEach(item => {
      const newItem = document.createElement('div');
      newItem.innerHTML = itemTemplate;
      const itemElements = newItem.querySelectorAll('[ms-code-item-text]');

      let skipItem = false;
      filters.forEach(filter => {
        const exclude = filter.startsWith('!');
        const filterKey = exclude ? filter.substring(1) : filter;
        if ((exclude && item.hasOwnProperty(filterKey)) || (!exclude && !item.hasOwnProperty(filterKey))) {
          skipItem = true;
        }
      });

      if (skipItem) return; // Skip this item

      itemElements.forEach(itemElement => {
        const jsonKey = itemElement.getAttribute('ms-code-item-text');
        const value = item && item[jsonKey] ? item[jsonKey] : '';
        itemElement.textContent = value;
      });

      const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
      newItem.firstChild.setAttribute('ms-code-item-key', itemKey);

      itemContainer.appendChild(newItem.firstChild);
    });

    printList.innerHTML = itemContainer.innerHTML;
  };

  // Fetch member JSON
  let intervalId = setInterval(async () => {
    member = await fetchMemberJSON();
    if (member && member.data) {
      clearInterval(intervalId);
      const printLists = document.querySelectorAll('[ms-code-print-list]');
      printLists.forEach(filterItems);
    }
  }, 500);

  // Add click event listener to elements with ms-code-update="json"
  const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
  updateButtons.forEach(button => {
    button.addEventListener("click", async function() {
      // Fetch member JSON on each click
      let intervalIdClick = setInterval(async () => {
        member = await fetchMemberJSON();
        if (member && member.data) {
          clearInterval(intervalIdClick);
          const printLists = document.querySelectorAll('[ms-code-print-list]');
          printLists.forEach(filterItems);
        }
      }, 500);
    });
  });
})();
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #13 v0.2 💙 FILTER ITEM GROUPS FROM JSON BASED ON ATTRIBUTE VALUE -->
<script>
(function() {
  const memberstack = window.$memberstackDom;
  let member;

  const fetchMemberJSON = async function() {
    member = await memberstack.getMemberJSON();
    return member;
  }

  const filterItems = async function(printList) {
    const filterAttr = printList.getAttribute('ms-code-list-filter');
    if (!filterAttr) return;

    const filters = filterAttr.split(',').map(filter => filter.trim());
    const jsonGroup = printList.getAttribute('ms-code-print-list');
    const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];

    const itemContainer = document.createElement('div');
    const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
    const itemTemplate = placeholder.outerHTML;

    items.forEach(item => {
      const newItem = document.createElement('div');
      newItem.innerHTML = itemTemplate;
      const itemElements = newItem.querySelectorAll('[ms-code-item-text]');

      let skipItem = false;
      filters.forEach(filter => {
        const exclude = filter.startsWith('!');
        const filterKey = exclude ? filter.substring(1) : filter;
        if ((exclude && item.hasOwnProperty(filterKey)) || (!exclude && !item.hasOwnProperty(filterKey))) {
          skipItem = true;
        }
      });

      if (skipItem) return; // Skip this item

      itemElements.forEach(itemElement => {
        const jsonKey = itemElement.getAttribute('ms-code-item-text');
        const value = item && item[jsonKey] ? item[jsonKey] : '';
        itemElement.textContent = value;
      });

      const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
      newItem.firstChild.setAttribute('ms-code-item-key', itemKey);

      itemContainer.appendChild(newItem.firstChild);
    });

    printList.innerHTML = itemContainer.innerHTML;
  };

  // Fetch member JSON
  let intervalId = setInterval(async () => {
    member = await fetchMemberJSON();
    if (member && member.data) {
      clearInterval(intervalId);
      const printLists = document.querySelectorAll('[ms-code-print-list]');
      printLists.forEach(filterItems);
    }
  }, 500);

  // Add click event listener to elements with ms-code-update="json"
  const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
  updateButtons.forEach(button => {
    button.addEventListener("click", async function() {
      // Fetch member JSON on each click
      let intervalIdClick = setInterval(async () => {
        member = await fetchMemberJSON();
        if (member && member.data) {
          clearInterval(intervalIdClick);
          const printLists = document.querySelectorAll('[ms-code-print-list]');
          printLists.forEach(filterItems);
        }
      }, 500);
    });
  });
})();
</script>
Voir le Memberscript
JSON

#12 - Ajouter des éléments à des groupes JSON sur simple clic

Ajoutez un élément/une propriété aux éléments JSON précédemment créés en un seul clic !


<!-- 💙 MEMBERSCRIPT #12 v0.1 💙 ADD ITEM TO JSON GROUP ON CLICK -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;

    // Add click event listener to the document
    document.addEventListener("click", async function(event) {
      const target = event.target;

      // Check if the clicked element has ms-code-update-json-item attribute
      const updateJsonItem = target.closest('[ms-code-update-json-item]');
      if (updateJsonItem) {
        const key = updateJsonItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
        const jsonGroup = updateJsonItem.closest('[ms-code-print-list]').getAttribute('ms-code-print-list');

        // Retrieve the current member JSON data
        const member = await memberstack.getMemberJSON();

        if (member.data && member.data[jsonGroup] && member.data[jsonGroup][key]) {
          // Get the value to be added to the item in the member JSON
          const updateValue = updateJsonItem.getAttribute('ms-code-update-json-item');
          // Update the member JSON with the new value
          member.data[jsonGroup][key][updateValue] = true;

          // Update the member JSON using the Memberstack SDK
          await memberstack.updateMemberJSON({
            json: member.data
          });

          // Optional: Display a success message or perform any other desired action
          console.log('Member JSON updated successfully');
        } else {
          console.error(`Could not find item with key: ${key}`);
        }
      }

      // Check if the clicked element has ms-code-update attribute
      const updateButton = target.closest('[ms-code-update="json"]');
      if (updateButton) {
        // Add a delay
        await new Promise(resolve => setTimeout(resolve, 500));

        // Retrieve the current member JSON data
        const member = await memberstack.getMemberJSON();

        // Save the member JSON as a local storage item
        localStorage.setItem("memberJSON", JSON.stringify(member));

        // Optional: Display a success message or perform any other desired action
        console.log('Member JSON saved to local storage');
      }
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #12 v0.1 💙 ADD ITEM TO JSON GROUP ON CLICK -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;

    // Add click event listener to the document
    document.addEventListener("click", async function(event) {
      const target = event.target;

      // Check if the clicked element has ms-code-update-json-item attribute
      const updateJsonItem = target.closest('[ms-code-update-json-item]');
      if (updateJsonItem) {
        const key = updateJsonItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
        const jsonGroup = updateJsonItem.closest('[ms-code-print-list]').getAttribute('ms-code-print-list');

        // Retrieve the current member JSON data
        const member = await memberstack.getMemberJSON();

        if (member.data && member.data[jsonGroup] && member.data[jsonGroup][key]) {
          // Get the value to be added to the item in the member JSON
          const updateValue = updateJsonItem.getAttribute('ms-code-update-json-item');
          // Update the member JSON with the new value
          member.data[jsonGroup][key][updateValue] = true;

          // Update the member JSON using the Memberstack SDK
          await memberstack.updateMemberJSON({
            json: member.data
          });

          // Optional: Display a success message or perform any other desired action
          console.log('Member JSON updated successfully');
        } else {
          console.error(`Could not find item with key: ${key}`);
        }
      }

      // Check if the clicked element has ms-code-update attribute
      const updateButton = target.closest('[ms-code-update="json"]');
      if (updateButton) {
        // Add a delay
        await new Promise(resolve => setTimeout(resolve, 500));

        // Retrieve the current member JSON data
        const member = await memberstack.getMemberJSON();

        // Save the member JSON as a local storage item
        localStorage.setItem("memberJSON", JSON.stringify(member));

        // Optional: Display a success message or perform any other desired action
        console.log('Member JSON saved to local storage');
      }
    });
  });
</script>
Voir le Memberscript
Visibilité conditionnelle

#11 - Ouvrir automatiquement la fenêtre modale de connexion

Montrer à tous les visiteurs déconnectés une fenêtre modale de connexion immédiatement après la visite de la page. Les Memberstack personnalisés et par défaut fonctionnent.


<!-- 💙 MEMBERSCRIPT #11 v0.1 💙 SHOW LOGIN MODAL IF MEMBER IS NOT LOGGED IN -->

<!-- KEEP THIS FOR DEFAULT MEMBERSTACK MODAL -->
<script>
function handleRedirect(redirect) {
  if (redirect && (window.location.pathname !== redirect)) return window.location.href = redirect;
  window.location.reload()
}

$memberstackDom.getCurrentMember().then(async ({
  data
}) => {
  if (!data) {
    const {
      data
    } = await $memberstackDom.openModal("login");
    handleRedirect(data.redirect)
  }
})
</script>

<!-- KEEP THIS FOR YOUR OWN CUSTOM MODAL -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
  if (!data) {
    const loginModal = document.querySelector('[ms-code-modal="login"]');
    if (loginModal) {
      loginModal.style.display = "flex";
    }
  }
});
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #11 v0.1 💙 SHOW LOGIN MODAL IF MEMBER IS NOT LOGGED IN -->

<!-- KEEP THIS FOR DEFAULT MEMBERSTACK MODAL -->
<script>
function handleRedirect(redirect) {
  if (redirect && (window.location.pathname !== redirect)) return window.location.href = redirect;
  window.location.reload()
}

$memberstackDom.getCurrentMember().then(async ({
  data
}) => {
  if (!data) {
    const {
      data
    } = await $memberstackDom.openModal("login");
    handleRedirect(data.redirect)
  }
})
</script>

<!-- KEEP THIS FOR YOUR OWN CUSTOM MODAL -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
  if (!data) {
    const loginModal = document.querySelector('[ms-code-modal="login"]');
    if (loginModal) {
      loginModal.style.display = "flex";
    }
  }
});
</script>
Voir le Memberscript
Visibilité conditionnelle

#10 - Afficher/supprimer un élément en fonction d'un champ personnalisé

Vérifier si un membre a rempli un champ personnalisé. Si c'est le cas, l'élément cible est affiché.


<!-- 💙 MEMBERSCRIPT #10 v0.2 💙 HIDE ELEMENTS IF CUSTOM FIELD IS BLANK -->
<script>
document.addEventListener('DOMContentLoaded', function() {
  // Get the `_ms-mem` object from the local storage
  const msMem = JSON.parse(localStorage.getItem('_ms-mem'));

  // Get all the elements that have the `ms-code-customfield` attribute
  const elements = document.querySelectorAll('[ms-code-customfield]');

  // Iterate over each element
  elements.forEach(element => {
    // Get the value of the `ms-code-customfield` attribute
    const customField = element.getAttribute('ms-code-customfield');

    // If customField starts with '!', we invert the logic
    if (customField.startsWith('!')) {
      const actualCustomField = customField.slice(1); // remove the '!' from the start

      // If the custom field is empty, remove the element from the DOM
      if (msMem.customFields && msMem.customFields[actualCustomField]) {
        element.parentNode.removeChild(element);
      }
    } else {
      // Check if the user has the corresponding custom field in Memberstack
      if (!msMem.customFields || !msMem.customFields[customField]) {
        // If the custom field is empty, remove the element from the DOM
        element.parentNode.removeChild(element);
      }
    }
  });
});
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #10 v0.2 💙 HIDE ELEMENTS IF CUSTOM FIELD IS BLANK -->
<script>
document.addEventListener('DOMContentLoaded', function() {
  // Get the `_ms-mem` object from the local storage
  const msMem = JSON.parse(localStorage.getItem('_ms-mem'));

  // Get all the elements that have the `ms-code-customfield` attribute
  const elements = document.querySelectorAll('[ms-code-customfield]');

  // Iterate over each element
  elements.forEach(element => {
    // Get the value of the `ms-code-customfield` attribute
    const customField = element.getAttribute('ms-code-customfield');

    // If customField starts with '!', we invert the logic
    if (customField.startsWith('!')) {
      const actualCustomField = customField.slice(1); // remove the '!' from the start

      // If the custom field is empty, remove the element from the DOM
      if (msMem.customFields && msMem.customFields[actualCustomField]) {
        element.parentNode.removeChild(element);
      }
    } else {
      // Check if the user has the corresponding custom field in Memberstack
      if (!msMem.customFields || !msMem.customFields[customField]) {
        // If the custom field is empty, remove the element from the DOM
        element.parentNode.removeChild(element);
      }
    }
  });
});
</script>
Voir le Memberscript
Marketing

#9 - Compte à rebours réel, basé sur l'utilisateur

Définir dynamiquement un compte à rebours par utilisateur et masquer les éléments lorsque le temps est écoulé.


<!-- 💙 MEMBERSCRIPT #9 v0.1 💙 COUNTDOWN BY USER -->
<script>
// Step 1: Get the current date and time
const currentDate = new Date();

// Step 2: Calculate the new date and time based on the attribute values
const addTime = (date, unit, value) => {
  const newDate = new Date(date);
  switch (unit) {
    case 'hour':
      newDate.setHours(newDate.getHours() + value);
      break;
    case 'minute':
      newDate.setMinutes(newDate.getMinutes() + value);
      break;
    case 'second':
      newDate.setSeconds(newDate.getSeconds() + value);
      break;
    case 'millisecond':
      newDate.setMilliseconds(newDate.getMilliseconds() + value);
      break;
  }
  return newDate;
};

// Retrieve attribute values and calculate the new date and time
const hourAttr = document.querySelector('[ms-code-time-hour]');
const minuteAttr = document.querySelector('[ms-code-time-minute]');
const secondAttr = document.querySelector('[ms-code-time-second]');
const millisecondAttr = document.querySelector('[ms-code-time-millisecond]');

const countdownDateTime = localStorage.getItem('countdownDateTime');
let newDate;

if (countdownDateTime) {
  newDate = new Date(countdownDateTime);
} else {
  newDate = currentDate;
  if (hourAttr.hasAttribute('ms-code-time-hour')) {
    const hours = parseInt(hourAttr.getAttribute('ms-code-time-hour'));
    newDate = addTime(newDate, 'hour', isNaN(hours) ? 0 : hours);
  }
  if (minuteAttr.hasAttribute('ms-code-time-minute')) {
    const minutes = parseInt(minuteAttr.getAttribute('ms-code-time-minute'));
    newDate = addTime(newDate, 'minute', isNaN(minutes) ? 0 : minutes);
  }
  if (secondAttr.hasAttribute('ms-code-time-second')) {
    const seconds = parseInt(secondAttr.getAttribute('ms-code-time-second'));
    newDate = addTime(newDate, 'second', isNaN(seconds) ? 0 : seconds);
  }
  if (millisecondAttr.hasAttribute('ms-code-time-millisecond')) {
    const milliseconds = parseInt(millisecondAttr.getAttribute('ms-code-time-millisecond'));
    newDate = addTime(newDate, 'millisecond', isNaN(milliseconds) ? 0 : milliseconds);
  }

  localStorage.setItem('countdownDateTime', newDate);
}

// Step 4: Update the text of the elements to show the continuous countdown
const countdownElements = [hourAttr, minuteAttr, secondAttr, millisecondAttr];

const updateCountdown = () => {
  const currentTime = new Date();
  const timeDifference = newDate - currentTime;

  if (timeDifference > 0) {
    const timeParts = [
      Math.floor(timeDifference / (1000 * 60 * 60)) % 24, // Hours
      Math.floor(timeDifference / (1000 * 60)) % 60, // Minutes
      Math.floor(timeDifference / 1000) % 60, // Seconds
      Math.floor(timeDifference) % 1000, // Milliseconds
    ];

    // Update the text of the elements with the countdown values
    countdownElements.forEach((element, index) => {
      const timeValue = timeParts[index];
      if (index === 3) {
        element.innerText = timeValue.toString().padStart(2, '0').slice(0, 2); // Display only two digits for milliseconds
      } else {
        element.innerText = timeValue < 10 ? `0${timeValue}` : timeValue;
      }
    });

    // Update the countdown every 10 milliseconds
    setTimeout(updateCountdown, 10);
  } else {
    // Countdown has reached zero or has passed
    countdownElements.forEach((element) => {
      element.innerText = '00';
    });

    // Remove elements with ms-code-countdown="hide-on-end" attribute
    const hideOnEndElements = document.querySelectorAll('[ms-code-countdown="hide-on-end"]');
    hideOnEndElements.forEach((element) => {
      element.remove();
    });

    // Optionally, you can perform additional actions or display a message when the countdown ends
  }
};

// Start the countdown
updateCountdown();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #9 v0.1 💙 COUNTDOWN BY USER -->
<script>
// Step 1: Get the current date and time
const currentDate = new Date();

// Step 2: Calculate the new date and time based on the attribute values
const addTime = (date, unit, value) => {
  const newDate = new Date(date);
  switch (unit) {
    case 'hour':
      newDate.setHours(newDate.getHours() + value);
      break;
    case 'minute':
      newDate.setMinutes(newDate.getMinutes() + value);
      break;
    case 'second':
      newDate.setSeconds(newDate.getSeconds() + value);
      break;
    case 'millisecond':
      newDate.setMilliseconds(newDate.getMilliseconds() + value);
      break;
  }
  return newDate;
};

// Retrieve attribute values and calculate the new date and time
const hourAttr = document.querySelector('[ms-code-time-hour]');
const minuteAttr = document.querySelector('[ms-code-time-minute]');
const secondAttr = document.querySelector('[ms-code-time-second]');
const millisecondAttr = document.querySelector('[ms-code-time-millisecond]');

const countdownDateTime = localStorage.getItem('countdownDateTime');
let newDate;

if (countdownDateTime) {
  newDate = new Date(countdownDateTime);
} else {
  newDate = currentDate;
  if (hourAttr.hasAttribute('ms-code-time-hour')) {
    const hours = parseInt(hourAttr.getAttribute('ms-code-time-hour'));
    newDate = addTime(newDate, 'hour', isNaN(hours) ? 0 : hours);
  }
  if (minuteAttr.hasAttribute('ms-code-time-minute')) {
    const minutes = parseInt(minuteAttr.getAttribute('ms-code-time-minute'));
    newDate = addTime(newDate, 'minute', isNaN(minutes) ? 0 : minutes);
  }
  if (secondAttr.hasAttribute('ms-code-time-second')) {
    const seconds = parseInt(secondAttr.getAttribute('ms-code-time-second'));
    newDate = addTime(newDate, 'second', isNaN(seconds) ? 0 : seconds);
  }
  if (millisecondAttr.hasAttribute('ms-code-time-millisecond')) {
    const milliseconds = parseInt(millisecondAttr.getAttribute('ms-code-time-millisecond'));
    newDate = addTime(newDate, 'millisecond', isNaN(milliseconds) ? 0 : milliseconds);
  }

  localStorage.setItem('countdownDateTime', newDate);
}

// Step 4: Update the text of the elements to show the continuous countdown
const countdownElements = [hourAttr, minuteAttr, secondAttr, millisecondAttr];

const updateCountdown = () => {
  const currentTime = new Date();
  const timeDifference = newDate - currentTime;

  if (timeDifference > 0) {
    const timeParts = [
      Math.floor(timeDifference / (1000 * 60 * 60)) % 24, // Hours
      Math.floor(timeDifference / (1000 * 60)) % 60, // Minutes
      Math.floor(timeDifference / 1000) % 60, // Seconds
      Math.floor(timeDifference) % 1000, // Milliseconds
    ];

    // Update the text of the elements with the countdown values
    countdownElements.forEach((element, index) => {
      const timeValue = timeParts[index];
      if (index === 3) {
        element.innerText = timeValue.toString().padStart(2, '0').slice(0, 2); // Display only two digits for milliseconds
      } else {
        element.innerText = timeValue < 10 ? `0${timeValue}` : timeValue;
      }
    });

    // Update the countdown every 10 milliseconds
    setTimeout(updateCountdown, 10);
  } else {
    // Countdown has reached zero or has passed
    countdownElements.forEach((element) => {
      element.innerText = '00';
    });

    // Remove elements with ms-code-countdown="hide-on-end" attribute
    const hideOnEndElements = document.querySelectorAll('[ms-code-countdown="hide-on-end"]');
    hideOnEndElements.forEach((element) => {
      element.remove();
    });

    // Optionally, you can perform additional actions or display a message when the countdown ends
  }
};

// Start the countdown
updateCountdown();
</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.