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é.
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.

#N° 98 - Limitation de l'âge
Les utilisateurs doivent confirmer leur âge avant de continuer.
<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const form = document.querySelector('form[ms-code-age-gate]');
const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
const backButton = document.querySelector('a[ms-code-age-gate="back"]');
const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');
if (localStorage.getItem('ageVerified') === 'true') {
wrapper.remove();
return;
}
backButton.addEventListener('click', (e) => {
e.preventDefault();
window.history.back();
});
const inputs = [dayInput, monthInput, yearInput];
inputs.forEach((input, index) => {
input.addEventListener('keyup', (e) => {
const maxChars = input === yearInput ? 4 : 2;
let value = e.target.value;
if (input === dayInput && value.length === maxChars) {
value = value > 31 ? '31' : value.padStart(2, '0');
} else if (input === monthInput && value.length === maxChars) {
value = value > 12 ? '12' : value.padStart(2, '0');
}
e.target.value = value;
if (value.length === maxChars) {
const nextInput = inputs[index + 1];
if (nextInput) {
nextInput.focus();
}
}
});
});
form.addEventListener('submit', (e) => {
e.preventDefault();
const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
const currentDate = new Date();
const ageDifference = new Date(currentDate - enteredDate);
const age = Math.abs(ageDifference.getUTCFullYear() - 1970);
const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);
if (age >= ageLimit) {
console.log('Age verified.');
errorDiv.style.display = 'none';
localStorage.setItem('ageVerified', 'true');
wrapper.remove();
} else {
console.log('Age verification failed, user is under the age limit.');
errorDiv.style.display = 'block';
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const form = document.querySelector('form[ms-code-age-gate]');
const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
const backButton = document.querySelector('a[ms-code-age-gate="back"]');
const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');
if (localStorage.getItem('ageVerified') === 'true') {
wrapper.remove();
return;
}
backButton.addEventListener('click', (e) => {
e.preventDefault();
window.history.back();
});
const inputs = [dayInput, monthInput, yearInput];
inputs.forEach((input, index) => {
input.addEventListener('keyup', (e) => {
const maxChars = input === yearInput ? 4 : 2;
let value = e.target.value;
if (input === dayInput && value.length === maxChars) {
value = value > 31 ? '31' : value.padStart(2, '0');
} else if (input === monthInput && value.length === maxChars) {
value = value > 12 ? '12' : value.padStart(2, '0');
}
e.target.value = value;
if (value.length === maxChars) {
const nextInput = inputs[index + 1];
if (nextInput) {
nextInput.focus();
}
}
});
});
form.addEventListener('submit', (e) => {
e.preventDefault();
const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
const currentDate = new Date();
const ageDifference = new Date(currentDate - enteredDate);
const age = Math.abs(ageDifference.getUTCFullYear() - 1970);
const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);
if (age >= ageLimit) {
console.log('Age verified.');
errorDiv.style.display = 'none';
localStorage.setItem('ageVerified', 'true');
wrapper.remove();
} else {
console.log('Age verification failed, user is under the age limit.');
errorDiv.style.display = 'block';
}
});
});
</script>

#97 - Uploader des fichiers vers le S3 Bucket
Autoriser les téléchargements vers un godet S3 à partir d'un formulaire Webflow.
<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
document.addEventListener('DOMContentLoaded', function() {
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
input.addEventListener('change', function() {
if (this.files.length > 0) {
const file = this.files[0];
const uuid = generateUUID();
const extension = file.name.split('.').pop();
const newFileName = `${uuid}.${extension}`;
const wrapper = this.closest('div[ms-code-s3-wrapper]');
const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');
s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);
const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));
fetch(apiGatewayUrl, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type }
})
.then(response => {
if (response.status !== 200) {
throw new Error(`Upload failed with status: ${response.status}`);
}
console.log('File uploaded successfully:', newFileName);
})
.catch(error => {
console.error('Upload error:', error);
alert('Upload failed.');
});
}
});
});
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function(event) {
const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
const allUrlsSet = s3Inputs.every(input => input.value);
if (!allUrlsSet) {
event.preventDefault();
alert('Please wait for all files to finish uploading before submitting.');
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
document.addEventListener('DOMContentLoaded', function() {
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
input.addEventListener('change', function() {
if (this.files.length > 0) {
const file = this.files[0];
const uuid = generateUUID();
const extension = file.name.split('.').pop();
const newFileName = `${uuid}.${extension}`;
const wrapper = this.closest('div[ms-code-s3-wrapper]');
const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');
s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);
const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));
fetch(apiGatewayUrl, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type }
})
.then(response => {
if (response.status !== 200) {
throw new Error(`Upload failed with status: ${response.status}`);
}
console.log('File uploaded successfully:', newFileName);
})
.catch(error => {
console.error('Upload error:', error);
alert('Upload failed.');
});
}
});
});
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function(event) {
const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
const allUrlsSet = s3Inputs.every(input => input.value);
if (!allUrlsSet) {
event.preventDefault();
alert('Please wait for all files to finish uploading before submitting.');
}
});
});
});
</script>

#96 - Enregistrer un décompte
Créez et mettez à jour un décompte qui s'enregistre dans un champ personnalisé !
<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
const addButtons = document.querySelectorAll("[ms-code-add-tally]");
addButtons.forEach(button => {
button.addEventListener("click", async () => {
const tallyKey = button.getAttribute("ms-code-add-tally");
const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
if(tallyText){
let currentCount = parseInt(tallyText.textContent, 10);
currentCount += 1;
tallyText.textContent = currentCount;
// Store the new tally count to Memberstack
let newFields = {};
newFields[tallyKey] = currentCount;
await memberstack.updateMember({customFields: newFields});
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
const addButtons = document.querySelectorAll("[ms-code-add-tally]");
addButtons.forEach(button => {
button.addEventListener("click", async () => {
const tallyKey = button.getAttribute("ms-code-add-tally");
const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
if(tallyText){
let currentCount = parseInt(tallyText.textContent, 10);
currentCount += 1;
tallyText.textContent = currentCount;
// Store the new tally count to Memberstack
let newFields = {};
newFields[tallyKey] = currentCount;
await memberstack.updateMember({customFields: newFields});
}
});
});
});
</script>

#95 - Confetti On Click
Faites voler des confettis amusants en cliquant !
<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const confettiElems = document.querySelectorAll("[ms-code-confetti]");
confettiElems.forEach(item => {
item.addEventListener("click", () => {
const effect = item.getAttribute("ms-code-confetti");
switch (effect) {
case "falling":
const makeFall = () => {
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: 0 },
colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
});
}
setInterval(makeFall, 2000);
break;
case "single":
confetti({
particleCount: 1,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: Math.random() }
});
break;
case "sides":
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: 0.5 }
});
break;
case "explosions":
confetti({
particleCount: 100,
startVelocity: 50,
spread: 360
});
break;
case "bottom":
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: 0.5, y: 1 }
});
break;
default:
console.log("Unknown confetti effect");
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const confettiElems = document.querySelectorAll("[ms-code-confetti]");
confettiElems.forEach(item => {
item.addEventListener("click", () => {
const effect = item.getAttribute("ms-code-confetti");
switch (effect) {
case "falling":
const makeFall = () => {
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: 0 },
colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
});
}
setInterval(makeFall, 2000);
break;
case "single":
confetti({
particleCount: 1,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: Math.random() }
});
break;
case "sides":
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: Math.random(), y: 0.5 }
});
break;
case "explosions":
confetti({
particleCount: 100,
startVelocity: 50,
spread: 360
});
break;
case "bottom":
confetti({
particleCount: 100,
startVelocity: 30,
spread: 360,
origin: { x: 0.5, y: 1 }
});
break;
default:
console.log("Unknown confetti effect");
}
});
});
});
</script>

#94 - Définir l'attribut href
Définir dynamiquement un lien à l'aide du CMS Webflow (ou autre)
<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
var elements = document.querySelectorAll('[ms-code-href]');
elements.forEach(function(element) {
var url = element.getAttribute('ms-code-href');
element.setAttribute('href', url);
});
};
</script>
<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
var elements = document.querySelectorAll('[ms-code-href]');
elements.forEach(function(element) {
var url = element.getAttribute('ms-code-href');
element.setAttribute('href', url);
});
};
</script>

#93 - Forcer les URL valides dans les formulaires de saisie
Convertit automatiquement tout ce qui est saisi en URL valide.
<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');
// Add event listener to each form field
formFields.forEach((field) => {
field.addEventListener('input', convertToLink);
});
// Function to convert input to a link
function convertToLink(event) {
const input = event.target;
// Get user input
const userInput = input.value.trim();
// Check if input starts with http:// or https://
if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
input.value = userInput; // No conversion needed for valid links
} else {
input.value = `http://${userInput}`; // Prepend http:// for simplicity
}
}
</script>
<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');
// Add event listener to each form field
formFields.forEach((field) => {
field.addEventListener('input', convertToLink);
});
// Function to convert input to a link
function convertToLink(event) {
const input = event.target;
// Get user input
const userInput = input.value.trim();
// Check if input starts with http:// or https://
if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
input.value = userInput; // No conversion needed for valid links
} else {
input.value = `http://${userInput}`; // Prepend http:// for simplicity
}
}
</script>

#92 - Changer de page en cliquant
Modifier l'URL de la page en cours lorsque l'on clique sur un élément.
<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
let target = event.target;
// Traverse up the DOM tree to find an element with the ms-code-navigate attribute
while (target && !target.getAttribute('ms-code-navigate')) {
target = target.parentElement;
}
// If we found an element with the ms-code-navigate attribute
if (target) {
const navigateUrl = target.getAttribute('ms-code-navigate');
if (navigateUrl) {
event.preventDefault();
// Always open in a new tab
window.open(navigateUrl, '_blank');
}
}
});
</script>
<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
let target = event.target;
// Traverse up the DOM tree to find an element with the ms-code-navigate attribute
while (target && !target.getAttribute('ms-code-navigate')) {
target = target.parentElement;
}
// If we found an element with the ms-code-navigate attribute
if (target) {
const navigateUrl = target.getAttribute('ms-code-navigate');
if (navigateUrl) {
event.preventDefault();
// Always open in a new tab
window.open(navigateUrl, '_blank');
}
}
});
</script>

#91 - Masquer la fenêtre popup pour une durée déterminée
Masquer une fenêtre contextuelle pendant une durée X lorsqu'un bouton est cliqué.
<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Look for elements with 'ms-code-hide-popup' attribute
var items = $('[ms-code-hide-popup]');
var button;
var timeElement;
// Determine which element is the button and which is the timer
items.each(function(index, item) {
var value = $(item).attr('ms-code-hide-popup');
if (value === "button") {
button = $(item);
} else {
timeElement = $(item);
}
});
// Calculate the target date
var calculateTargetDate = function(timeStr) {
var splitTime = timeStr.split(':');
var now = new Date();
now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
return now;
};
// Check if element should be removed from DOM
var checkTimeAndRemoveElement = function() {
var targetDate = localStorage.getItem('targetDate');
if (targetDate && new Date() < new Date(targetDate)) {
timeElement.remove();
} else {
localStorage.removeItem('targetDate');
}
};
// Action on button click
button.on('click', function() {
var time = timeElement.attr('ms-code-hide-popup');
localStorage.setItem('targetDate', calculateTargetDate(time));
checkTimeAndRemoveElement();
});
// Initial check
checkTimeAndRemove AndRemoveElement();
});
</script>
<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Look for elements with 'ms-code-hide-popup' attribute
var items = $('[ms-code-hide-popup]');
var button;
var timeElement;
// Determine which element is the button and which is the timer
items.each(function(index, item) {
var value = $(item).attr('ms-code-hide-popup');
if (value === "button") {
button = $(item);
} else {
timeElement = $(item);
}
});
// Calculate the target date
var calculateTargetDate = function(timeStr) {
var splitTime = timeStr.split(':');
var now = new Date();
now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
return now;
};
// Check if element should be removed from DOM
var checkTimeAndRemoveElement = function() {
var targetDate = localStorage.getItem('targetDate');
if (targetDate && new Date() < new Date(targetDate)) {
timeElement.remove();
} else {
localStorage.removeItem('targetDate');
}
};
// Action on button click
button.on('click', function() {
var time = timeElement.attr('ms-code-hide-popup');
localStorage.setItem('targetDate', calculateTargetDate(time));
checkTimeAndRemoveElement();
});
// Initial check
checkTimeAndRemove AndRemoveElement();
});
</script>

#90 - Afficher les éléments lors d'un changement d'entrée
Afficher 1 ou plusieurs éléments lorsque l'utilisateur modifie la valeur d'entrée.
<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Initially hide all elements
$('[ms-code-show-item]').css('display', 'none');
setTimeout(function() {
$('[ms-code-show-field]').change(function() {
var field = $(this).attr('ms-code-show-field');
$('[ms-code-show-item=' + field + ']').css('display', 'block');
});
}, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>
<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Initially hide all elements
$('[ms-code-show-item]').css('display', 'none');
setTimeout(function() {
$('[ms-code-show-field]').change(function() {
var field = $(this).attr('ms-code-show-field');
$('[ms-code-show-item=' + field + ']').css('display', 'block');
});
}, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>

#89 - Menus contextuels personnalisés
Afficher un menu contextuel personnalisé et intégré à Webflow lorsque l'on clique avec le bouton droit de la souris sur votre élément.
<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");
// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
element.addEventListener('contextmenu', event => {
event.preventDefault(); // Prevents showing the default context menu
hideAllMenus(); // Make sure other menus are hidden
// fetch the related menu, make it visible
const menuItemId = element.getAttribute("ms-code-context-item");
const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);
if (menu) {
menu.classList.remove('hidden');
menu.classList.add('visible');
}
});
});
// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
menu.addEventListener('click', event => {
event.stopPropagation();
});
});
// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);
// Helper function to hide all custom context menus
function hideAllMenus() {
menus.forEach(menu => {
menu.classList.remove('visible');
menu.classList.add('hidden');
});
}
</script>
<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");
// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
element.addEventListener('contextmenu', event => {
event.preventDefault(); // Prevents showing the default context menu
hideAllMenus(); // Make sure other menus are hidden
// fetch the related menu, make it visible
const menuItemId = element.getAttribute("ms-code-context-item");
const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);
if (menu) {
menu.classList.remove('hidden');
menu.classList.add('visible');
}
});
});
// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
menu.addEventListener('click', event => {
event.stopPropagation();
});
});
// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);
// Helper function to hide all custom context menus
function hideAllMenus() {
menus.forEach(menu => {
menu.classList.remove('visible');
menu.classList.add('hidden');
});
}
</script>

#88 - Afficher l'état actuel du CMS, liens vers les dossiers
Affichez l'état "actuel" du Webflow sur vos pages imbriquées et les éléments du CMS.
<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
var currentUrl = window.location.href;
var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute
elements.forEach(function (element) {
var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
element.classList.add('w--current'); // apply the class
}
});
};
</script>
<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
var currentUrl = window.location.href;
var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute
elements.forEach(function (element) {
var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
element.classList.add('w--current'); // apply the class
}
});
};
</script>

#87 - Supprimer un plan après le compte à rebours
Créez des contenus sécurisés sensibles au facteur temps !
<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));
// Check if date has passed
const checkDate = async () => {
const now = new Date();
if (now > countdown) {
// Remove member's free plan
await memberstack.removePlan({
planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
});
console.log("Plan removed");
// Reload the page
location.reload();
}
}
// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>
<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));
// Check if date has passed
const checkDate = async () => {
const now = new Date();
if (now > countdown) {
// Remove member's free plan
await memberstack.removePlan({
planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
});
console.log("Plan removed");
// Reload the page
location.reload();
}
}
// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>

#86 - Text-To-Speech simple et gratuit
Ajoutez un bouton qui permet aux visiteurs d'écouter votre article.
<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
let utterance = new SpeechSynthesisUtterance();
speakButton.addEventListener('click', () => {
if(speechSynthesis.speaking || speechSynthesis.paused) {
speechSynthesis.cancel(); // stops current speech
} else {
utterance.text = textDiv.innerText;
speechSynthesis.speak(utterance); // starts speaking
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
let utterance = new SpeechSynthesisUtterance();
speakButton.addEventListener('click', () => {
if(speechSynthesis.speaking || speechSynthesis.paused) {
speechSynthesis.cancel(); // stops current speech
} else {
utterance.text = textDiv.innerText;
speechSynthesis.speak(utterance); // starts speaking
}
});
});
</script>

#85 - Entrées du formulaire "Ajouter une ligne
Permet aux membres d'ajouter et de supprimer des lignes à partir d'un formulaire.
<!-- 💙 MEMBERSCRIPT #85 v0.1 💙 ADD A ROW FORM INPUTS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
// Hide all rows except the original row
$('[ms-code-row-input="new"]').hide();
// Add row button click event
$('[ms-code-row-input="add-row"]').click(function(e) {
e.preventDefault();
var clonedRow = $('[ms-code-row-input="new"]').first().clone();
clonedRow.find('input').val('');
clonedRow.show().appendTo('[ms-code-row-input="row-container"]');
updateHolderValue();
});
// Delete row button click event
$(document).on('click', '[ms-code-row-input="delete"]', function(e) {
e.preventDefault();
$(this).closest('[ms-code-row-input="new"]').remove();
updateHolderValue();
});
// Event for all inputs
$(document).on('input', '[ms-code-row-input="original"], [ms-code-row-input="new-input"], [ms-code-row-input="holder"]', function() {
if ($(this).is('[ms-code-row-input="holder"]')) {
updateRowsFromHolder();
} else {
updateHolderValue();
}
});
// Function to update the holder input value
function updateHolderValue() {
var values = [];
$('[ms-code-row-input="original"], [ms-code-row-input="new-input"]').each(function() {
var value = $(this).val().trim();
if (value) {
values.push(value);
}
});
$('[ms-code-row-input="holder"]').val(values.join(','));
}
// Function to update rows from the holder field
function updateRowsFromHolder() {
var holderValue = $('[ms-code-row-input="holder"]').val();
var values = holderValue.split(',');
$('[ms-code-row-input="new"]').not(':first').remove();
// For each holder value, create a new row
values.forEach(function(val, idx) {
if (idx === 0) {
$('[ms-code-row-input="original"]').val(val);
} else {
var newRow = $('[ms-code-row-input="new"]').first().clone().appendTo('[ms-code-row-input="row-container"]');
newRow.find('input').val(val);
newRow.show();
}
});
}
// Initial update of the holder input value
updateHolderValue();
// Adding MutationObserver to call updateRowsFromHolder on changes to the holder field
var targetNode = $('[ms-code-row-input="holder"]')[0];
var config = { attributes: true, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList')
{
updateRowsFromHolder();
}
}
};
var observer = new MutationObserver(callback);
observer.observe(targetNode, config);
});
</script>
<!-- 💙 MEMBERSCRIPT #85 v0.1 💙 ADD A ROW FORM INPUTS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
// Hide all rows except the original row
$('[ms-code-row-input="new"]').hide();
// Add row button click event
$('[ms-code-row-input="add-row"]').click(function(e) {
e.preventDefault();
var clonedRow = $('[ms-code-row-input="new"]').first().clone();
clonedRow.find('input').val('');
clonedRow.show().appendTo('[ms-code-row-input="row-container"]');
updateHolderValue();
});
// Delete row button click event
$(document).on('click', '[ms-code-row-input="delete"]', function(e) {
e.preventDefault();
$(this).closest('[ms-code-row-input="new"]').remove();
updateHolderValue();
});
// Event for all inputs
$(document).on('input', '[ms-code-row-input="original"], [ms-code-row-input="new-input"], [ms-code-row-input="holder"]', function() {
if ($(this).is('[ms-code-row-input="holder"]')) {
updateRowsFromHolder();
} else {
updateHolderValue();
}
});
// Function to update the holder input value
function updateHolderValue() {
var values = [];
$('[ms-code-row-input="original"], [ms-code-row-input="new-input"]').each(function() {
var value = $(this).val().trim();
if (value) {
values.push(value);
}
});
$('[ms-code-row-input="holder"]').val(values.join(','));
}
// Function to update rows from the holder field
function updateRowsFromHolder() {
var holderValue = $('[ms-code-row-input="holder"]').val();
var values = holderValue.split(',');
$('[ms-code-row-input="new"]').not(':first').remove();
// For each holder value, create a new row
values.forEach(function(val, idx) {
if (idx === 0) {
$('[ms-code-row-input="original"]').val(val);
} else {
var newRow = $('[ms-code-row-input="new"]').first().clone().appendTo('[ms-code-row-input="row-container"]');
newRow.find('input').val(val);
newRow.show();
}
});
}
// Initial update of the holder input value
updateHolderValue();
// Adding MutationObserver to call updateRowsFromHolder on changes to the holder field
var targetNode = $('[ms-code-row-input="holder"]')[0];
var config = { attributes: true, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList')
{
updateRowsFromHolder();
}
}
};
var observer = new MutationObserver(callback);
observer.observe(targetNode, config);
});
</script>

#84 - Effacer les entrées en charge
Ajoutez ce script à n'importe quelle page pour effacer la valeur d'un champ personnalisé au chargement de la page.
<!-- 💙 MEMBERSCRIPT #84 v0.1 💙 CLEAR INPUT VALUES ONLOAD -->
<script>
document.addEventListener('DOMContentLoaded', async function() {
const memberstack = window.$memberstackDom;
const fieldsToClear = ["phone", "last-name"]; // Specify the fields to clear
// Clear inputs and Memberstack fields on page load
memberstack.getCurrentMember().then(async ({ data: member }) => {
if (member) {
const customFieldsToUpdate = {};
fieldsToClear.forEach(fieldName => {
customFieldsToUpdate[fieldName] = '';
});
try {
await memberstack.updateMember({
customFields: customFieldsToUpdate
});
console.log("Fields cleared on page load.");
} catch (error) {
console.error('Error clearing fields on page load:', error);
}
}
// Clear input values on page load for specified fields
fieldsToClear.forEach(fieldName => {
const inputField = document.querySelector(`[data-ms-member="${fieldName}"]`);
if (inputField) {
inputField.value = '';
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #84 v0.1 💙 CLEAR INPUT VALUES ONLOAD -->
<script>
document.addEventListener('DOMContentLoaded', async function() {
const memberstack = window.$memberstackDom;
const fieldsToClear = ["phone", "last-name"]; // Specify the fields to clear
// Clear inputs and Memberstack fields on page load
memberstack.getCurrentMember().then(async ({ data: member }) => {
if (member) {
const customFieldsToUpdate = {};
fieldsToClear.forEach(fieldName => {
customFieldsToUpdate[fieldName] = '';
});
try {
await memberstack.updateMember({
customFields: customFieldsToUpdate
});
console.log("Fields cleared on page load.");
} catch (error) {
console.error('Error clearing fields on page load:', error);
}
}
// Clear input values on page load for specified fields
fieldsToClear.forEach(fieldName => {
const inputField = document.querySelector(`[data-ms-member="${fieldName}"]`);
if (inputField) {
inputField.value = '';
}
});
});
});
</script>

#83 - Préférences de cookies inter-appareils
Permettre aux membres d'enregistrer leurs préférences en matière de cookies dans leur compte.
<!-- 💙 MEMBERSCRIPT #83 v0.1 💙 CROSS-DEVICE COOKIE PREFERENCES -->
<script>
// Function to retrieve a cookie value by name
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
}
async function updateMemberConsentPreferences(fsCcCookieValue) {
try {
const memberstack = window.$memberstackDom;
const userData = await memberstack.getCurrentMember();
if (userData && userData.data.customFields) {
if (!userData.data.customFields['cookie-consent']) {
const decodedFsCcCookieValue = decodeURIComponent(fsCcCookieValue);
await memberstack.updateMember({
customFields: {
'cookie-consent': decodedFsCcCookieValue
}
});
} else {
document.cookie = `fs-cc=${encodeURIComponent(userData.data.customFields['cookie-consent'])}`;
}
}
} catch (error) {}
}
async function initialize() {
const fsCcCookieValue = getCookie('fs-cc');
if (fsCcCookieValue) {
await updateMemberConsentPreferences(fsCcCookieValue);
const checkboxes = document.querySelectorAll('[fs-cc-checkbox]');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', async () => {
const memberstack = window.$memberstackDom;
const userData = await memberstack.getCurrentMember();
if (userData && userData.data.customFields) {
const customFieldKey = 'cookie-consent';
const checkboxName = checkbox.getAttribute('fs-cc-checkbox');
if (userData.data.customFields[customFieldKey]) {
const consentData = JSON.parse(userData.data.customFields[customFieldKey]);
consentData.consents[checkboxName] = checkbox.checked;
const updatedCustomField = JSON.stringify(consentData);
await memberstack.updateMember({
customFields: {
[customFieldKey]: updatedCustomField
}
});
document.cookie = `fs-cc=${encodeURIComponent(updatedCustomField)}`;
}
}
});
});
}
}
// Initialize the script
initialize();
</script>
<!-- 💙 MEMBERSCRIPT #83 v0.1 💙 CROSS-DEVICE COOKIE PREFERENCES -->
<script>
// Function to retrieve a cookie value by name
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
}
async function updateMemberConsentPreferences(fsCcCookieValue) {
try {
const memberstack = window.$memberstackDom;
const userData = await memberstack.getCurrentMember();
if (userData && userData.data.customFields) {
if (!userData.data.customFields['cookie-consent']) {
const decodedFsCcCookieValue = decodeURIComponent(fsCcCookieValue);
await memberstack.updateMember({
customFields: {
'cookie-consent': decodedFsCcCookieValue
}
});
} else {
document.cookie = `fs-cc=${encodeURIComponent(userData.data.customFields['cookie-consent'])}`;
}
}
} catch (error) {}
}
async function initialize() {
const fsCcCookieValue = getCookie('fs-cc');
if (fsCcCookieValue) {
await updateMemberConsentPreferences(fsCcCookieValue);
const checkboxes = document.querySelectorAll('[fs-cc-checkbox]');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', async () => {
const memberstack = window.$memberstackDom;
const userData = await memberstack.getCurrentMember();
if (userData && userData.data.customFields) {
const customFieldKey = 'cookie-consent';
const checkboxName = checkbox.getAttribute('fs-cc-checkbox');
if (userData.data.customFields[customFieldKey]) {
const consentData = JSON.parse(userData.data.customFields[customFieldKey]);
consentData.consents[checkboxName] = checkbox.checked;
const updatedCustomField = JSON.stringify(consentData);
await memberstack.updateMember({
customFields: {
[customFieldKey]: updatedCustomField
}
});
document.cookie = `fs-cc=${encodeURIComponent(updatedCustomField)}`;
}
}
});
});
}
}
// Initialize the script
initialize();
</script>

#82 - Clés de licence
Sécurisez vos contenus téléchargeables à l'aide de clés de licence.
<!-- 💙 MEMBERSCRIPT #82 v0.1 💙 LICENSE KEYS -->
<script>
const memberstack = window.$memberstackDom;
// Initialize MutationObserver
const observer = new MutationObserver(async (mutations) => {
const downloadBtn = document.getElementById("download");
if (downloadBtn) {
// Element exists, so add event listener
downloadBtn.addEventListener("click", async () => {
await memberstack.removePlan({
planId: "pln_activate-license-key-952c0d8u"
});
console.log("Plan removed");
});
// Stop observing since we found the element
observer.disconnect();
}
});
// Observe the whole document
observer.observe(document.body, { childList: true, subtree: true });
</script>
<!-- 💙 MEMBERSCRIPT #82 v0.1 💙 LICENSE KEYS -->
<script>
const memberstack = window.$memberstackDom;
// Initialize MutationObserver
const observer = new MutationObserver(async (mutations) => {
const downloadBtn = document.getElementById("download");
if (downloadBtn) {
// Element exists, so add event listener
downloadBtn.addEventListener("click", async () => {
await memberstack.removePlan({
planId: "pln_activate-license-key-952c0d8u"
});
console.log("Plan removed");
});
// Stop observing since we found the element
observer.disconnect();
}
});
// Observe the whole document
observer.observe(document.body, { childList: true, subtree: true });
</script>

#81 - Valeurs personnalisées des cases à cocher
Transmettre une valeur unique selon que la case est cochée ou non.
<!-- 💙 MEMBERSCRIPT #81 v0.1 💙 CUSTOM CHECKBOX VALUES -->
<script>
document.addEventListener('submit', function(e) {
var checkboxes = document.querySelectorAll('[ms-code-custom-checkbox]');
checkboxes.forEach(function(checkbox) {
var values = checkbox.getAttribute('ms-code-custom-checkbox').split(',');
var valueToSubmit = checkbox.checked ? values[0] : values[1];
var hiddenInput = document.createElement('input');
// Copy all attributes except type and ms-code-custom-checkbox
for (var i = 0; i < checkbox.attributes.length; i++) {
var attr = checkbox.attributes[i];
if (attr.name !== 'type' && attr.name !== 'ms-code-custom-checkbox') {
hiddenInput.setAttribute(attr.name, attr.value);
}
}
hiddenInput.type = 'hidden';
hiddenInput.value = valueToSubmit;
checkbox.form.appendChild(hiddenInput);
checkbox.remove(); // Remove the original checkbox so it doesn't interfere with submission
});
});
</script>
<!-- 💙 MEMBERSCRIPT #81 v0.1 💙 CUSTOM CHECKBOX VALUES -->
<script>
document.addEventListener('submit', function(e) {
var checkboxes = document.querySelectorAll('[ms-code-custom-checkbox]');
checkboxes.forEach(function(checkbox) {
var values = checkbox.getAttribute('ms-code-custom-checkbox').split(',');
var valueToSubmit = checkbox.checked ? values[0] : values[1];
var hiddenInput = document.createElement('input');
// Copy all attributes except type and ms-code-custom-checkbox
for (var i = 0; i < checkbox.attributes.length; i++) {
var attr = checkbox.attributes[i];
if (attr.name !== 'type' && attr.name !== 'ms-code-custom-checkbox') {
hiddenInput.setAttribute(attr.name, attr.value);
}
}
hiddenInput.type = 'hidden';
hiddenInput.value = valueToSubmit;
checkbox.form.appendChild(hiddenInput);
checkbox.remove(); // Remove the original checkbox so it doesn't interfere with submission
});
});
</script>

#80 - Notification d'annulation du plan
Déclencher une notification Slack lorsqu'un membre annule son plan.

#79 - Déclencher le clic au survol
Déclencher un événement de clic onHover.
<!-- 💙 MEMBERSCRIPT #79 v0.1 💙 HOVER BASED TABS -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const hoverTabElements = document.querySelectorAll('[ms-code-onhover="click"]');
hoverTabElements.forEach(hoverTabElement => {
hoverTabElement.addEventListener('mouseenter', function() {
hoverTabElement.click(); // Click on the element when hovering
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #79 v0.1 💙 HOVER BASED TABS -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const hoverTabElements = document.querySelectorAll('[ms-code-onhover="click"]');
hoverTabElements.forEach(hoverTabElement => {
hoverTabElement.addEventListener('mouseenter', function() {
hoverTabElement.click(); // Click on the element when hovering
});
});
});
</script>
Scripts des membres
Instantly add custom features to your Webflow site.
Just paste a script, set attributes, and go live.
Join the Memberstack 2.0 Slack for tips, answers, and community scripts. Please note that these are not official features and support cannot be guaranteed.

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
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>

#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>
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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>

#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>
<!-- 💙 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>
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 SlackDé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.
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.