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.

#17 - Création d'un lien à partir d'un champ personnalisé
Utilisez des champs personnalisés pour remplir les cibles de liens avec un seul attribut !
<!-- 💙 MEMBERSCRIPT #17 v0.3 💙 ADD CUSTOM FIELD AS A LINK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');
if (!memberData?.id) return;
document.querySelectorAll('[ms-code-field-link]').forEach(element => {
const fieldKey = element.getAttribute('ms-code-field-link');
const fieldValue = memberData.customFields?.[fieldKey]?.trim();
if (!fieldValue) {
element.style.display = 'none';
return;
}
try {
// Add protocol if missing and validate URL
const url = !/^https?:\/\//i.test(fieldValue) ? 'https://' + fieldValue : fieldValue;
new URL(url); // Will throw if invalid URL
element.href = url;
element.rel = 'noopener noreferrer';
element.target = '_blank';
} catch {
element.style.display = 'none';
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #17 v0.3 💙 ADD CUSTOM FIELD AS A LINK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberData = JSON.parse(localStorage.getItem('_ms-mem') || '{}');
if (!memberData?.id) return;
document.querySelectorAll('[ms-code-field-link]').forEach(element => {
const fieldKey = element.getAttribute('ms-code-field-link');
const fieldValue = memberData.customFields?.[fieldKey]?.trim();
if (!fieldValue) {
element.style.display = 'none';
return;
}
try {
// Add protocol if missing and validate URL
const url = !/^https?:\/\//i.test(fieldValue) ? 'https://' + fieldValue : fieldValue;
new URL(url); // Will throw if invalid URL
element.href = url;
element.rel = 'noopener noreferrer';
element.target = '_blank';
} catch {
element.style.display = 'none';
}
});
});
</script>

#16 - Fin de la session après X minutes d'inactivité
Ce script redirige les utilisateurs inactifs vers une page "Session expirée" après une certaine période d'inactivité. Un compte à rebours est affiché sur la page et se réinitialise en fonction de l'activité de la page.
Add this code before the closing </body> tag any page which needs a countdown timer.
<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
let logoutTimer;
let countdownInterval;
let initialTime;
function startLogoutTimer() {
// Get the logout time from the HTML element
const timeElement = document.querySelector('[ms-code-logout-timer]');
const timeParts = timeElement.textContent.split(':');
const minutes = parseInt(timeParts[0], 10);
const seconds = parseInt(timeParts[1], 10);
const LOGOUT_TIME = (minutes * 60 + seconds) * 1000; // Convert to milliseconds
// Store the initial time value
if (!initialTime) {
initialTime = LOGOUT_TIME;
}
// Clear the previous timer, if any
clearTimeout(logoutTimer);
clearInterval(countdownInterval);
let startTime = Date.now();
// Start a new timer to redirect the user after the specified time
logoutTimer = setTimeout(() => {
window.location.href = "/expired";
startLogoutTimer(); // Start the logout timer again
}, LOGOUT_TIME);
// Start a countdown timer
countdownInterval = setInterval(() => {
let elapsed = Date.now() - startTime;
let remaining = LOGOUT_TIME - elapsed;
updateCountdownDisplay(remaining);
}, 1000); // update every second
}
function updateCountdownDisplay(remainingTimeInMs) {
// convert ms to MM:SS format
let minutes = Math.floor(remainingTimeInMs / 1000 / 60);
let seconds = Math.floor((remainingTimeInMs / 1000) % 60);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
const timeElement = document.querySelector('[ms-code-logout-timer]');
timeElement.textContent = `${minutes}:${seconds}`;
}
// Call this function whenever the user interacts with the page
function resetLogoutTimer() {
const timeElement = document.querySelector('[ms-code-logout-timer]');
timeElement.textContent = formatTime(initialTime); // Reset to the initial time
startLogoutTimer();
}
function formatTime(timeInMs) {
let minutes = Math.floor(timeInMs / 1000 / 60);
let seconds = Math.floor((timeInMs / 1000) % 60);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
return `${minutes}:${seconds}`;
}
// Call this function when the user logs in
function cancelLogoutTimer() {
clearTimeout(logoutTimer);
clearInterval(countdownInterval);
}
// Start the timer when the page loads
startLogoutTimer();
// Add event listeners to reset timer on any page activity
document.addEventListener('mousemove', resetLogoutTimer);
document.addEventListener('keypress', resetLogoutTimer);
document.addEventListener('touchstart', resetLogoutTimer);
</script>
Add this code to your /expired page before the closing </body> tag.
<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
window.addEventListener('load', () => {
window.$memberstackDom.getCurrentMember().then(async ({ data: member }) => {
if (member) {
try {
await window.$memberstackDom.logout();
console.log('Logged out successfully');
setTimeout(() => {
location.reload();
}, 1000); // Refresh the page 1 second after logout
} catch (error) {
console.error(error);
}
}
});
});
</script>
Add this code before the closing </body> tag any page which needs a countdown timer.
<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
let logoutTimer;
let countdownInterval;
let initialTime;
function startLogoutTimer() {
// Get the logout time from the HTML element
const timeElement = document.querySelector('[ms-code-logout-timer]');
const timeParts = timeElement.textContent.split(':');
const minutes = parseInt(timeParts[0], 10);
const seconds = parseInt(timeParts[1], 10);
const LOGOUT_TIME = (minutes * 60 + seconds) * 1000; // Convert to milliseconds
// Store the initial time value
if (!initialTime) {
initialTime = LOGOUT_TIME;
}
// Clear the previous timer, if any
clearTimeout(logoutTimer);
clearInterval(countdownInterval);
let startTime = Date.now();
// Start a new timer to redirect the user after the specified time
logoutTimer = setTimeout(() => {
window.location.href = "/expired";
startLogoutTimer(); // Start the logout timer again
}, LOGOUT_TIME);
// Start a countdown timer
countdownInterval = setInterval(() => {
let elapsed = Date.now() - startTime;
let remaining = LOGOUT_TIME - elapsed;
updateCountdownDisplay(remaining);
}, 1000); // update every second
}
function updateCountdownDisplay(remainingTimeInMs) {
// convert ms to MM:SS format
let minutes = Math.floor(remainingTimeInMs / 1000 / 60);
let seconds = Math.floor((remainingTimeInMs / 1000) % 60);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
const timeElement = document.querySelector('[ms-code-logout-timer]');
timeElement.textContent = `${minutes}:${seconds}`;
}
// Call this function whenever the user interacts with the page
function resetLogoutTimer() {
const timeElement = document.querySelector('[ms-code-logout-timer]');
timeElement.textContent = formatTime(initialTime); // Reset to the initial time
startLogoutTimer();
}
function formatTime(timeInMs) {
let minutes = Math.floor(timeInMs / 1000 / 60);
let seconds = Math.floor((timeInMs / 1000) % 60);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
return `${minutes}:${seconds}`;
}
// Call this function when the user logs in
function cancelLogoutTimer() {
clearTimeout(logoutTimer);
clearInterval(countdownInterval);
}
// Start the timer when the page loads
startLogoutTimer();
// Add event listeners to reset timer on any page activity
document.addEventListener('mousemove', resetLogoutTimer);
document.addEventListener('keypress', resetLogoutTimer);
document.addEventListener('touchstart', resetLogoutTimer);
</script>
Add this code to your /expired page before the closing </body> tag.
<!-- 💙 MEMBERSCRIPT #16 v0.2 💙 LOGOUT AFTER X MINUTES OF INACTIVITY -->
<script>
window.addEventListener('load', () => {
window.$memberstackDom.getCurrentMember().then(async ({ data: member }) => {
if (member) {
try {
await window.$memberstackDom.logout();
console.log('Logged out successfully');
setTimeout(() => {
location.reload();
}, 1000); // Refresh the page 1 second after logout
} catch (error) {
console.error(error);
}
}
});
});
</script>

#15 - Rafraîchir la page après une durée définie au clic
Actualiser la page au bout d'un certain temps lorsqu'un élément est cliqué.
<!-- 💙 MEMBERSCRIPT #15 v0.1 💙 TRIGGER REFRESH ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener("click", function(event) {
const clickedElement = event.target.closest('[ms-code-refresh]');
if (clickedElement) {
const refreshDelay = parseInt(clickedElement.getAttribute('ms-code-refresh'));
if (!isNaN(refreshDelay)) {
setTimeout(function() {
location.reload();
}, refreshDelay);
}
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #15 v0.1 💙 TRIGGER REFRESH ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener("click", function(event) {
const clickedElement = event.target.closest('[ms-code-refresh]');
if (clickedElement) {
const refreshDelay = parseInt(clickedElement.getAttribute('ms-code-refresh'));
if (!isNaN(refreshDelay)) {
setTimeout(function() {
location.reload();
}, refreshDelay);
}
}
});
});
</script>

#14 - Créer un état de chargement au clic
Simuler un état de chargement personnalisé lorsqu'un élément est cliqué.
<!-- 💙 MEMBERSCRIPT #14 v0.1 💙 CREATE LOADING STATE ON CLICK -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous" ></script>
<script>
$(document).ready(function() {
// Bind click event to elements with ms-code-loading-trigger attribute
$(document).on('click', '[ms-code-loading-trigger]', function() {
// Get the value of ms-code-loading-trigger attribute
var triggerValue = $(this).attr("ms-code-loading-trigger");
// Find the matching loading element
var loadingElement = $("[ms-code-loading-element='" + triggerValue + "']");
// Find the matching subject element
var subjectElement = $("[ms-code-loading-subject='" + triggerValue + "']");
// Show the loading element (with flex display), append it to the subject element
loadingElement.css('display', 'flex').appendTo(subjectElement);
// After 5 seconds (5000 milliseconds), hide the loading element
setTimeout(function() {
loadingElement.hide();
}, 5000);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #14 v0.1 💙 CREATE LOADING STATE ON CLICK -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous" ></script>
<script>
$(document).ready(function() {
// Bind click event to elements with ms-code-loading-trigger attribute
$(document).on('click', '[ms-code-loading-trigger]', function() {
// Get the value of ms-code-loading-trigger attribute
var triggerValue = $(this).attr("ms-code-loading-trigger");
// Find the matching loading element
var loadingElement = $("[ms-code-loading-element='" + triggerValue + "']");
// Find the matching subject element
var subjectElement = $("[ms-code-loading-subject='" + triggerValue + "']");
// Show the loading element (with flex display), append it to the subject element
loadingElement.css('display', 'flex').appendTo(subjectElement);
// After 5 seconds (5000 milliseconds), hide the loading element
setTimeout(function() {
loadingElement.hide();
}, 5000);
});
});
</script>

#13 - Filtrer les groupes d'éléments JSON
Affichez des listes filtrées à vos membres sur la base d'une propriété JSON !
<!-- 💙 MEMBERSCRIPT #13 v0.2 💙 FILTER ITEM GROUPS FROM JSON BASED ON ATTRIBUTE VALUE -->
<script>
(function() {
const memberstack = window.$memberstackDom;
let member;
const fetchMemberJSON = async function() {
member = await memberstack.getMemberJSON();
return member;
}
const filterItems = async function(printList) {
const filterAttr = printList.getAttribute('ms-code-list-filter');
if (!filterAttr) return;
const filters = filterAttr.split(',').map(filter => filter.trim());
const jsonGroup = printList.getAttribute('ms-code-print-list');
const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];
const itemContainer = document.createElement('div');
const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
const itemTemplate = placeholder.outerHTML;
items.forEach(item => {
const newItem = document.createElement('div');
newItem.innerHTML = itemTemplate;
const itemElements = newItem.querySelectorAll('[ms-code-item-text]');
let skipItem = false;
filters.forEach(filter => {
const exclude = filter.startsWith('!');
const filterKey = exclude ? filter.substring(1) : filter;
if ((exclude && item.hasOwnProperty(filterKey)) || (!exclude && !item.hasOwnProperty(filterKey))) {
skipItem = true;
}
});
if (skipItem) return; // Skip this item
itemElements.forEach(itemElement => {
const jsonKey = itemElement.getAttribute('ms-code-item-text');
const value = item && item[jsonKey] ? item[jsonKey] : '';
itemElement.textContent = value;
});
const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
newItem.firstChild.setAttribute('ms-code-item-key', itemKey);
itemContainer.appendChild(newItem.firstChild);
});
printList.innerHTML = itemContainer.innerHTML;
};
// Fetch member JSON
let intervalId = setInterval(async () => {
member = await fetchMemberJSON();
if (member && member.data) {
clearInterval(intervalId);
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(filterItems);
}
}, 500);
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
// Fetch member JSON on each click
let intervalIdClick = setInterval(async () => {
member = await fetchMemberJSON();
if (member && member.data) {
clearInterval(intervalIdClick);
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(filterItems);
}
}, 500);
});
});
})();
</script>
<!-- 💙 MEMBERSCRIPT #13 v0.2 💙 FILTER ITEM GROUPS FROM JSON BASED ON ATTRIBUTE VALUE -->
<script>
(function() {
const memberstack = window.$memberstackDom;
let member;
const fetchMemberJSON = async function() {
member = await memberstack.getMemberJSON();
return member;
}
const filterItems = async function(printList) {
const filterAttr = printList.getAttribute('ms-code-list-filter');
if (!filterAttr) return;
const filters = filterAttr.split(',').map(filter => filter.trim());
const jsonGroup = printList.getAttribute('ms-code-print-list');
const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];
const itemContainer = document.createElement('div');
const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
const itemTemplate = placeholder.outerHTML;
items.forEach(item => {
const newItem = document.createElement('div');
newItem.innerHTML = itemTemplate;
const itemElements = newItem.querySelectorAll('[ms-code-item-text]');
let skipItem = false;
filters.forEach(filter => {
const exclude = filter.startsWith('!');
const filterKey = exclude ? filter.substring(1) : filter;
if ((exclude && item.hasOwnProperty(filterKey)) || (!exclude && !item.hasOwnProperty(filterKey))) {
skipItem = true;
}
});
if (skipItem) return; // Skip this item
itemElements.forEach(itemElement => {
const jsonKey = itemElement.getAttribute('ms-code-item-text');
const value = item && item[jsonKey] ? item[jsonKey] : '';
itemElement.textContent = value;
});
const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
newItem.firstChild.setAttribute('ms-code-item-key', itemKey);
itemContainer.appendChild(newItem.firstChild);
});
printList.innerHTML = itemContainer.innerHTML;
};
// Fetch member JSON
let intervalId = setInterval(async () => {
member = await fetchMemberJSON();
if (member && member.data) {
clearInterval(intervalId);
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(filterItems);
}
}, 500);
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
// Fetch member JSON on each click
let intervalIdClick = setInterval(async () => {
member = await fetchMemberJSON();
if (member && member.data) {
clearInterval(intervalIdClick);
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(filterItems);
}
}, 500);
});
});
})();
</script>

#12 - Ajouter des éléments à des groupes JSON sur simple clic
Ajoutez un élément/une propriété aux éléments JSON précédemment créés en un seul clic !
<!-- 💙 MEMBERSCRIPT #12 v0.1 💙 ADD ITEM TO JSON GROUP ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Add click event listener to the document
document.addEventListener("click", async function(event) {
const target = event.target;
// Check if the clicked element has ms-code-update-json-item attribute
const updateJsonItem = target.closest('[ms-code-update-json-item]');
if (updateJsonItem) {
const key = updateJsonItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
const jsonGroup = updateJsonItem.closest('[ms-code-print-list]').getAttribute('ms-code-print-list');
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
if (member.data && member.data[jsonGroup] && member.data[jsonGroup][key]) {
// Get the value to be added to the item in the member JSON
const updateValue = updateJsonItem.getAttribute('ms-code-update-json-item');
// Update the member JSON with the new value
member.data[jsonGroup][key][updateValue] = true;
// Update the member JSON using the Memberstack SDK
await memberstack.updateMemberJSON({
json: member.data
});
// Optional: Display a success message or perform any other desired action
console.log('Member JSON updated successfully');
} else {
console.error(`Could not find item with key: ${key}`);
}
}
// Check if the clicked element has ms-code-update attribute
const updateButton = target.closest('[ms-code-update="json"]');
if (updateButton) {
// Add a delay
await new Promise(resolve => setTimeout(resolve, 500));
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
// Optional: Display a success message or perform any other desired action
console.log('Member JSON saved to local storage');
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #12 v0.1 💙 ADD ITEM TO JSON GROUP ON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Add click event listener to the document
document.addEventListener("click", async function(event) {
const target = event.target;
// Check if the clicked element has ms-code-update-json-item attribute
const updateJsonItem = target.closest('[ms-code-update-json-item]');
if (updateJsonItem) {
const key = updateJsonItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
const jsonGroup = updateJsonItem.closest('[ms-code-print-list]').getAttribute('ms-code-print-list');
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
if (member.data && member.data[jsonGroup] && member.data[jsonGroup][key]) {
// Get the value to be added to the item in the member JSON
const updateValue = updateJsonItem.getAttribute('ms-code-update-json-item');
// Update the member JSON with the new value
member.data[jsonGroup][key][updateValue] = true;
// Update the member JSON using the Memberstack SDK
await memberstack.updateMemberJSON({
json: member.data
});
// Optional: Display a success message or perform any other desired action
console.log('Member JSON updated successfully');
} else {
console.error(`Could not find item with key: ${key}`);
}
}
// Check if the clicked element has ms-code-update attribute
const updateButton = target.closest('[ms-code-update="json"]');
if (updateButton) {
// Add a delay
await new Promise(resolve => setTimeout(resolve, 500));
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
// Optional: Display a success message or perform any other desired action
console.log('Member JSON saved to local storage');
}
});
});
</script>

#11 - Ouvrir automatiquement la fenêtre modale de connexion
Montrer à tous les visiteurs déconnectés une fenêtre modale de connexion immédiatement après la visite de la page. Les Memberstack personnalisés et par défaut fonctionnent.
<!-- 💙 MEMBERSCRIPT #11 v0.1 💙 SHOW LOGIN MODAL IF MEMBER IS NOT LOGGED IN -->
<!-- KEEP THIS FOR DEFAULT MEMBERSTACK MODAL -->
<script>
function handleRedirect(redirect) {
if (redirect && (window.location.pathname !== redirect)) return window.location.href = redirect;
window.location.reload()
}
$memberstackDom.getCurrentMember().then(async ({
data
}) => {
if (!data) {
const {
data
} = await $memberstackDom.openModal("login");
handleRedirect(data.redirect)
}
})
</script>
<!-- KEEP THIS FOR YOUR OWN CUSTOM MODAL -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
if (!data) {
const loginModal = document.querySelector('[ms-code-modal="login"]');
if (loginModal) {
loginModal.style.display = "flex";
}
}
});
</script>
<!-- 💙 MEMBERSCRIPT #11 v0.1 💙 SHOW LOGIN MODAL IF MEMBER IS NOT LOGGED IN -->
<!-- KEEP THIS FOR DEFAULT MEMBERSTACK MODAL -->
<script>
function handleRedirect(redirect) {
if (redirect && (window.location.pathname !== redirect)) return window.location.href = redirect;
window.location.reload()
}
$memberstackDom.getCurrentMember().then(async ({
data
}) => {
if (!data) {
const {
data
} = await $memberstackDom.openModal("login");
handleRedirect(data.redirect)
}
})
</script>
<!-- KEEP THIS FOR YOUR OWN CUSTOM MODAL -->
<script>
$memberstackDom.getCurrentMember().then(({ data }) => {
if (!data) {
const loginModal = document.querySelector('[ms-code-modal="login"]');
if (loginModal) {
loginModal.style.display = "flex";
}
}
});
</script>

#10 - Afficher/supprimer un élément en fonction d'un champ personnalisé
Vérifier si un membre a rempli un champ personnalisé. Si c'est le cas, l'élément cible est affiché.
<!-- 💙 MEMBERSCRIPT #10 v0.2 💙 HIDE ELEMENTS IF CUSTOM FIELD IS BLANK -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get the `_ms-mem` object from the local storage
const msMem = JSON.parse(localStorage.getItem('_ms-mem'));
// Get all the elements that have the `ms-code-customfield` attribute
const elements = document.querySelectorAll('[ms-code-customfield]');
// Iterate over each element
elements.forEach(element => {
// Get the value of the `ms-code-customfield` attribute
const customField = element.getAttribute('ms-code-customfield');
// If customField starts with '!', we invert the logic
if (customField.startsWith('!')) {
const actualCustomField = customField.slice(1); // remove the '!' from the start
// If the custom field is empty, remove the element from the DOM
if (msMem.customFields && msMem.customFields[actualCustomField]) {
element.parentNode.removeChild(element);
}
} else {
// Check if the user has the corresponding custom field in Memberstack
if (!msMem.customFields || !msMem.customFields[customField]) {
// If the custom field is empty, remove the element from the DOM
element.parentNode.removeChild(element);
}
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #10 v0.2 💙 HIDE ELEMENTS IF CUSTOM FIELD IS BLANK -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get the `_ms-mem` object from the local storage
const msMem = JSON.parse(localStorage.getItem('_ms-mem'));
// Get all the elements that have the `ms-code-customfield` attribute
const elements = document.querySelectorAll('[ms-code-customfield]');
// Iterate over each element
elements.forEach(element => {
// Get the value of the `ms-code-customfield` attribute
const customField = element.getAttribute('ms-code-customfield');
// If customField starts with '!', we invert the logic
if (customField.startsWith('!')) {
const actualCustomField = customField.slice(1); // remove the '!' from the start
// If the custom field is empty, remove the element from the DOM
if (msMem.customFields && msMem.customFields[actualCustomField]) {
element.parentNode.removeChild(element);
}
} else {
// Check if the user has the corresponding custom field in Memberstack
if (!msMem.customFields || !msMem.customFields[customField]) {
// If the custom field is empty, remove the element from the DOM
element.parentNode.removeChild(element);
}
}
});
});
</script>

#9 - Compte à rebours réel, basé sur l'utilisateur
Définir dynamiquement un compte à rebours par utilisateur et masquer les éléments lorsque le temps est écoulé.
<!-- 💙 MEMBERSCRIPT #9 v0.1 💙 COUNTDOWN BY USER -->
<script>
// Step 1: Get the current date and time
const currentDate = new Date();
// Step 2: Calculate the new date and time based on the attribute values
const addTime = (date, unit, value) => {
const newDate = new Date(date);
switch (unit) {
case 'hour':
newDate.setHours(newDate.getHours() + value);
break;
case 'minute':
newDate.setMinutes(newDate.getMinutes() + value);
break;
case 'second':
newDate.setSeconds(newDate.getSeconds() + value);
break;
case 'millisecond':
newDate.setMilliseconds(newDate.getMilliseconds() + value);
break;
}
return newDate;
};
// Retrieve attribute values and calculate the new date and time
const hourAttr = document.querySelector('[ms-code-time-hour]');
const minuteAttr = document.querySelector('[ms-code-time-minute]');
const secondAttr = document.querySelector('[ms-code-time-second]');
const millisecondAttr = document.querySelector('[ms-code-time-millisecond]');
const countdownDateTime = localStorage.getItem('countdownDateTime');
let newDate;
if (countdownDateTime) {
newDate = new Date(countdownDateTime);
} else {
newDate = currentDate;
if (hourAttr.hasAttribute('ms-code-time-hour')) {
const hours = parseInt(hourAttr.getAttribute('ms-code-time-hour'));
newDate = addTime(newDate, 'hour', isNaN(hours) ? 0 : hours);
}
if (minuteAttr.hasAttribute('ms-code-time-minute')) {
const minutes = parseInt(minuteAttr.getAttribute('ms-code-time-minute'));
newDate = addTime(newDate, 'minute', isNaN(minutes) ? 0 : minutes);
}
if (secondAttr.hasAttribute('ms-code-time-second')) {
const seconds = parseInt(secondAttr.getAttribute('ms-code-time-second'));
newDate = addTime(newDate, 'second', isNaN(seconds) ? 0 : seconds);
}
if (millisecondAttr.hasAttribute('ms-code-time-millisecond')) {
const milliseconds = parseInt(millisecondAttr.getAttribute('ms-code-time-millisecond'));
newDate = addTime(newDate, 'millisecond', isNaN(milliseconds) ? 0 : milliseconds);
}
localStorage.setItem('countdownDateTime', newDate);
}
// Step 4: Update the text of the elements to show the continuous countdown
const countdownElements = [hourAttr, minuteAttr, secondAttr, millisecondAttr];
const updateCountdown = () => {
const currentTime = new Date();
const timeDifference = newDate - currentTime;
if (timeDifference > 0) {
const timeParts = [
Math.floor(timeDifference / (1000 * 60 * 60)) % 24, // Hours
Math.floor(timeDifference / (1000 * 60)) % 60, // Minutes
Math.floor(timeDifference / 1000) % 60, // Seconds
Math.floor(timeDifference) % 1000, // Milliseconds
];
// Update the text of the elements with the countdown values
countdownElements.forEach((element, index) => {
const timeValue = timeParts[index];
if (index === 3) {
element.innerText = timeValue.toString().padStart(2, '0').slice(0, 2); // Display only two digits for milliseconds
} else {
element.innerText = timeValue < 10 ? `0${timeValue}` : timeValue;
}
});
// Update the countdown every 10 milliseconds
setTimeout(updateCountdown, 10);
} else {
// Countdown has reached zero or has passed
countdownElements.forEach((element) => {
element.innerText = '00';
});
// Remove elements with ms-code-countdown="hide-on-end" attribute
const hideOnEndElements = document.querySelectorAll('[ms-code-countdown="hide-on-end"]');
hideOnEndElements.forEach((element) => {
element.remove();
});
// Optionally, you can perform additional actions or display a message when the countdown ends
}
};
// Start the countdown
updateCountdown();
</script>
<!-- 💙 MEMBERSCRIPT #9 v0.1 💙 COUNTDOWN BY USER -->
<script>
// Step 1: Get the current date and time
const currentDate = new Date();
// Step 2: Calculate the new date and time based on the attribute values
const addTime = (date, unit, value) => {
const newDate = new Date(date);
switch (unit) {
case 'hour':
newDate.setHours(newDate.getHours() + value);
break;
case 'minute':
newDate.setMinutes(newDate.getMinutes() + value);
break;
case 'second':
newDate.setSeconds(newDate.getSeconds() + value);
break;
case 'millisecond':
newDate.setMilliseconds(newDate.getMilliseconds() + value);
break;
}
return newDate;
};
// Retrieve attribute values and calculate the new date and time
const hourAttr = document.querySelector('[ms-code-time-hour]');
const minuteAttr = document.querySelector('[ms-code-time-minute]');
const secondAttr = document.querySelector('[ms-code-time-second]');
const millisecondAttr = document.querySelector('[ms-code-time-millisecond]');
const countdownDateTime = localStorage.getItem('countdownDateTime');
let newDate;
if (countdownDateTime) {
newDate = new Date(countdownDateTime);
} else {
newDate = currentDate;
if (hourAttr.hasAttribute('ms-code-time-hour')) {
const hours = parseInt(hourAttr.getAttribute('ms-code-time-hour'));
newDate = addTime(newDate, 'hour', isNaN(hours) ? 0 : hours);
}
if (minuteAttr.hasAttribute('ms-code-time-minute')) {
const minutes = parseInt(minuteAttr.getAttribute('ms-code-time-minute'));
newDate = addTime(newDate, 'minute', isNaN(minutes) ? 0 : minutes);
}
if (secondAttr.hasAttribute('ms-code-time-second')) {
const seconds = parseInt(secondAttr.getAttribute('ms-code-time-second'));
newDate = addTime(newDate, 'second', isNaN(seconds) ? 0 : seconds);
}
if (millisecondAttr.hasAttribute('ms-code-time-millisecond')) {
const milliseconds = parseInt(millisecondAttr.getAttribute('ms-code-time-millisecond'));
newDate = addTime(newDate, 'millisecond', isNaN(milliseconds) ? 0 : milliseconds);
}
localStorage.setItem('countdownDateTime', newDate);
}
// Step 4: Update the text of the elements to show the continuous countdown
const countdownElements = [hourAttr, minuteAttr, secondAttr, millisecondAttr];
const updateCountdown = () => {
const currentTime = new Date();
const timeDifference = newDate - currentTime;
if (timeDifference > 0) {
const timeParts = [
Math.floor(timeDifference / (1000 * 60 * 60)) % 24, // Hours
Math.floor(timeDifference / (1000 * 60)) % 60, // Minutes
Math.floor(timeDifference / 1000) % 60, // Seconds
Math.floor(timeDifference) % 1000, // Milliseconds
];
// Update the text of the elements with the countdown values
countdownElements.forEach((element, index) => {
const timeValue = timeParts[index];
if (index === 3) {
element.innerText = timeValue.toString().padStart(2, '0').slice(0, 2); // Display only two digits for milliseconds
} else {
element.innerText = timeValue < 10 ? `0${timeValue}` : timeValue;
}
});
// Update the countdown every 10 milliseconds
setTimeout(updateCountdown, 10);
} else {
// Countdown has reached zero or has passed
countdownElements.forEach((element) => {
element.innerText = '00';
});
// Remove elements with ms-code-countdown="hide-on-end" attribute
const hideOnEndElements = document.querySelectorAll('[ms-code-countdown="hide-on-end"]');
hideOnEndElements.forEach((element) => {
element.remove();
});
// Optionally, you can perform additional actions or display a message when the countdown ends
}
};
// Start the countdown
updateCountdown();
</script>

#8 - Copier dans le presse-papiers
Permettre aux visiteurs de copier des éléments tels que des codes de réduction dans leur presse-papiers en un seul clic.
<!-- 💙 MEMBERSCRIPT #8 v0.1 💙 SIMPLE COPY ELEMENT TO CLIPBOARD -->
<script>
// Step 1: Click the element with the attribute ms-code-copy="trigger"
const triggerElement = document.querySelector('[ms-code-copy="trigger"]');
triggerElement.addEventListener('click', () => {
// Step 2: Copy text from the element with the attribute ms-code-copy="subject" to the clipboard
const subjectElement = document.querySelector('[ms-code-copy="subject"]');
const subjectText = subjectElement.innerText;
// Create a temporary textarea element to facilitate the copying process
const tempTextArea = document.createElement('textarea');
tempTextArea.value = subjectText;
document.body.appendChild(tempTextArea);
// Select the text within the textarea and copy it to the clipboard
tempTextArea.select();
document.execCommand('copy');
// Remove the temporary textarea
document.body.removeChild(tempTextArea);
});
</script>
<!-- 💙 MEMBERSCRIPT #8 v0.1 💙 SIMPLE COPY ELEMENT TO CLIPBOARD -->
<script>
// Step 1: Click the element with the attribute ms-code-copy="trigger"
const triggerElement = document.querySelector('[ms-code-copy="trigger"]');
triggerElement.addEventListener('click', () => {
// Step 2: Copy text from the element with the attribute ms-code-copy="subject" to the clipboard
const subjectElement = document.querySelector('[ms-code-copy="subject"]');
const subjectText = subjectElement.innerText;
// Create a temporary textarea element to facilitate the copying process
const tempTextArea = document.createElement('textarea');
tempTextArea.value = subjectText;
document.body.appendChild(tempTextArea);
// Select the text within the textarea and copy it to the clipboard
tempTextArea.select();
document.execCommand('copy');
// Remove the temporary textarea
document.body.removeChild(tempTextArea);
});
</script>

#7 - Éléments de chargement à retardement
Retarder l'apparition des éléments pendant une durée déterminée afin de donner à la page le temps de se mettre à jour avec les données correctes concernant les membres.
<!-- 💙 MEMBERSCRIPT #7 v0.1 💙 DELAY LOADING ELEMENTS -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const elementsToDelay = document.querySelectorAll('[ms-code-delay]');
elementsToDelay.forEach(element => {
element.style.visibility = "hidden"; // Hide the element initially
});
setTimeout(function() {
elementsToDelay.forEach(element => {
element.style.visibility = "visible"; // Make the element visible after the delay
element.style.opacity = "0"; // Set the initial opacity to 0
element.style.animation = "fadeIn 0.5s"; // Apply the fadeIn animation
element.addEventListener("animationend", function() {
element.style.opacity = "1"; // Set opacity to 1 at the end of the animation
});
});
}, 1000);
});
</script>
<style>
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
<!-- 💙 MEMBERSCRIPT #7 v0.1 💙 DELAY LOADING ELEMENTS -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const elementsToDelay = document.querySelectorAll('[ms-code-delay]');
elementsToDelay.forEach(element => {
element.style.visibility = "hidden"; // Hide the element initially
});
setTimeout(function() {
elementsToDelay.forEach(element => {
element.style.visibility = "visible"; // Make the element visible after the delay
element.style.opacity = "0"; // Set the initial opacity to 0
element.style.animation = "fadeIn 0.5s"; // Apply the fadeIn animation
element.addEventListener("animationend", function() {
element.style.opacity = "1"; // Set opacity to 1 at the end of the animation
});
});
}, 1000);
});
</script>
<style>
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>

#6 - Créer des groupes d'éléments à partir du JSON des membres
Afficher des groupes JSON aux membres connectés en utilisant un élément placeholder construit dans le Webflow.
<!-- 💙 MEMBERSCRIPT #6 v0.1 💙 CREATE ITEM GROUPS FROM JSON -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
// Function to display nested/sub items
const displayNestedItems = async function(printList) {
const jsonGroup = printList.getAttribute('ms-code-print-list');
const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
if (!placeholder) return;
const itemTemplate = placeholder.outerHTML;
const itemContainer = document.createElement('div'); // Create a new container for the items
const member = await memberstack.getMemberJSON();
const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];
if (items.length === 0) return; // If no items, exit the function
items.forEach(item => {
if (Object.keys(item).length === 0) return;
const newItem = document.createElement('div');
newItem.innerHTML = itemTemplate;
const itemElements = newItem.querySelectorAll('[ms-code-item-text]');
itemElements.forEach(itemElement => {
const jsonKey = itemElement.getAttribute('ms-code-item-text');
const value = item && item[jsonKey] ? item[jsonKey] : '';
itemElement.textContent = value;
});
// Add item key attribute
const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
newItem.firstChild.setAttribute('ms-code-item-key', itemKey);
itemContainer.appendChild(newItem.firstChild);
});
// Replace the existing list with the new items
printList.innerHTML = itemContainer.innerHTML;
};
// Call displayNestedItems function on initial page load for each instance
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(printList => {
displayNestedItems(printList);
});
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
// Add a delay of 500ms
await new Promise(resolve => setTimeout(resolve, 500));
printLists.forEach(printList => {
displayNestedItems(printList);
});
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #6 v0.1 💙 CREATE ITEM GROUPS FROM JSON -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
// Function to display nested/sub items
const displayNestedItems = async function(printList) {
const jsonGroup = printList.getAttribute('ms-code-print-list');
const placeholder = printList.querySelector(`[ms-code-print-item="${jsonGroup}"]`);
if (!placeholder) return;
const itemTemplate = placeholder.outerHTML;
const itemContainer = document.createElement('div'); // Create a new container for the items
const member = await memberstack.getMemberJSON();
const items = member.data && member.data[jsonGroup] ? Object.values(member.data[jsonGroup]) : [];
if (items.length === 0) return; // If no items, exit the function
items.forEach(item => {
if (Object.keys(item).length === 0) return;
const newItem = document.createElement('div');
newItem.innerHTML = itemTemplate;
const itemElements = newItem.querySelectorAll('[ms-code-item-text]');
itemElements.forEach(itemElement => {
const jsonKey = itemElement.getAttribute('ms-code-item-text');
const value = item && item[jsonKey] ? item[jsonKey] : '';
itemElement.textContent = value;
});
// Add item key attribute
const itemKey = Object.keys(member.data[jsonGroup]).find(k => member.data[jsonGroup][k] === item);
newItem.firstChild.setAttribute('ms-code-item-key', itemKey);
itemContainer.appendChild(newItem.firstChild);
});
// Replace the existing list with the new items
printList.innerHTML = itemContainer.innerHTML;
};
// Call displayNestedItems function on initial page load for each instance
const printLists = document.querySelectorAll('[ms-code-print-list]');
printLists.forEach(printList => {
displayNestedItems(printList);
});
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
// Add a delay of 500ms
await new Promise(resolve => setTimeout(resolve, 500));
printLists.forEach(printList => {
displayNestedItems(printList);
});
});
});
});
</script>

#5 - Remplir du texte à partir d'un simple élément JSON
Utilisez Member JSON pour mettre à jour le texte de n'importe quel élément de votre page.
<!-- 💙 MEMBERSCRIPT #5 v0.1 💙 FILL TEXT BASED ON SIMPLE ITEM IN JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Function to fill text elements with attribute ms-code-fill-text
const fillTextElements = async function() {
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Fill text elements with attribute ms-code-fill-text
const textElements = document.querySelectorAll('[ms-code-fill-text]');
textElements.forEach(element => {
const jsonKey = element.getAttribute('ms-code-fill-text');
const value = member.data && member.data[jsonKey] ? member.data[jsonKey] : '';
element.textContent = value;
});
};
// Function to handle script #4 event
const handleScript4Event = async function() {
// Add a delay of 500ms
await new Promise(resolve => setTimeout(resolve, 500));
await fillTextElements();
};
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", handleScript4Event);
});
// Call fillTextElements function on initial page load
fillTextElements();
});
</script>
<!-- 💙 MEMBERSCRIPT #5 v0.1 💙 FILL TEXT BASED ON SIMPLE ITEM IN JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Function to fill text elements with attribute ms-code-fill-text
const fillTextElements = async function() {
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Fill text elements with attribute ms-code-fill-text
const textElements = document.querySelectorAll('[ms-code-fill-text]');
textElements.forEach(element => {
const jsonKey = element.getAttribute('ms-code-fill-text');
const value = member.data && member.data[jsonKey] ? member.data[jsonKey] : '';
element.textContent = value;
});
};
// Function to handle script #4 event
const handleScript4Event = async function() {
// Add a delay of 500ms
await new Promise(resolve => setTimeout(resolve, 500));
await fillTextElements();
};
// Add click event listener to elements with ms-code-update="json"
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", handleScript4Event);
});
// Call fillTextElements function on initial page load
fillTextElements();
});
</script>

#4 - Mettre à jour le JSON des membres dans le stockage local au moment du clic
Ajoutez cet attribut à tout bouton/élément qui doit déclencher une mise à jour JSON après un court délai.
<!-- 💙 MEMBERSCRIPT #4 v0.1 💙 UPDATE MEMBER JSON IN LOCAL STORAGE ON BUTTON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
const memberstack = window.$memberstackDom;
// Add a delay
await new Promise(resolve => setTimeout(resolve, 500));
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #4 v0.1 💙 UPDATE MEMBER JSON IN LOCAL STORAGE ON BUTTON CLICK -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const updateButtons = document.querySelectorAll('[ms-code-update="json"]');
updateButtons.forEach(button => {
button.addEventListener("click", async function() {
const memberstack = window.$memberstackDom;
// Add a delay
await new Promise(resolve => setTimeout(resolve, 500));
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
});
});
});
</script>

#3 - Enregistrer le JSON des membres dans le stockage local lors du chargement de la page
Créer un objet localstorage contenant le JSON du membre connecté au chargement de la page
<!-- 💙 MEMBERSCRIPT #3 v0.1 💙 SAVE JSON TO LOCALSTORAGE ON PAGE LOAD -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
});
</script>
<!-- 💙 MEMBERSCRIPT #3 v0.1 💙 SAVE JSON TO LOCALSTORAGE ON PAGE LOAD -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// Save the member JSON as a local storage item
localStorage.setItem("memberJSON", JSON.stringify(member));
});
</script>

#2 - Ajouter des groupes d'éléments au JSON des membres
Permet aux membres d'ajouter des éléments/groupes imbriqués à leur JSON de membre.
<!-- 💙 MEMBERSCRIPT #2 v0.1 💙 ADD ITEM GROUPS TO MEMBER JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const msCodeForm2 = document.querySelector('[ms-code="form2"]');
const jsonList = msCodeForm2.getAttribute("ms-code-json-list");
msCodeForm2.addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the default form submission
// Retrieve the current member JSON data
const memberstack = window.$memberstackDom;
const member = await memberstack.getMemberJSON();
// Create a new member.data object if it doesn't already exist
if (!member.data) {
member.data = {};
}
// Check if the friends group already exists
if (!member.data[jsonList]) {
// Create a new friends group object
member.data[jsonList] = {};
}
// Retrieve the input values for creating the subgroup
const inputs = msCodeForm2.querySelectorAll('[ms-code-json-name]');
const subgroup = {};
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
subgroup[jsonName] = newItem;
});
// Generate a unique key for the new subgroup prefixed with the main group name
const timestamp = Date.now();
const subgroupKey = `${jsonList}-${timestamp}`;
// Create the new subgroup within the friends group
member.data[jsonList][subgroupKey] = subgroup;
// Update the member JSON with the new data
await memberstack.updateMemberJSON({
json: member.data
});
// Log a message to the console
console.log(`New subgroup with key ${subgroupKey} has been created within ${jsonList} group.`);
// Reset the input values
inputs.forEach(input => {
input.value = "";
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #2 v0.1 💙 ADD ITEM GROUPS TO MEMBER JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const msCodeForm2 = document.querySelector('[ms-code="form2"]');
const jsonList = msCodeForm2.getAttribute("ms-code-json-list");
msCodeForm2.addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the default form submission
// Retrieve the current member JSON data
const memberstack = window.$memberstackDom;
const member = await memberstack.getMemberJSON();
// Create a new member.data object if it doesn't already exist
if (!member.data) {
member.data = {};
}
// Check if the friends group already exists
if (!member.data[jsonList]) {
// Create a new friends group object
member.data[jsonList] = {};
}
// Retrieve the input values for creating the subgroup
const inputs = msCodeForm2.querySelectorAll('[ms-code-json-name]');
const subgroup = {};
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
subgroup[jsonName] = newItem;
});
// Generate a unique key for the new subgroup prefixed with the main group name
const timestamp = Date.now();
const subgroupKey = `${jsonList}-${timestamp}`;
// Create the new subgroup within the friends group
member.data[jsonList][subgroupKey] = subgroup;
// Update the member JSON with the new data
await memberstack.updateMemberJSON({
json: member.data
});
// Log a message to the console
console.log(`New subgroup with key ${subgroupKey} has been created within ${jsonList} group.`);
// Reset the input values
inputs.forEach(input => {
input.value = "";
});
});
});
</script>

#1 - Ajouter des éléments au JSON des membres
Permettre aux membres d'enregistrer des éléments simples dans leur JSON sans écrire de code.
<!-- 💙 MEMBERSCRIPT #1 v0.1 💙 ADD INDIVIDUAL ITEMS TO MEMBER JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const forms = document.querySelectorAll('[ms-code="form1"]');
forms.forEach(form => {
const jsonType = form.getAttribute("ms-code-json-type");
const jsonList = form.getAttribute("ms-code-json-list");
form.addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the default form submission
// Retrieve the current member JSON data
const memberstack = window.$memberstackDom;
const member = await memberstack.getMemberJSON();
// Create a new member.data object if it doesn't already exist
if (!member.data) {
member.data = {};
}
if (jsonType === "group") {
// Check if the group already exists
if (!member.data[jsonList]) {
// Create a new group object
member.data[jsonList] = {};
}
// Iterate over the inputs with ms-code-json-name attribute
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonList][jsonName] = newItem;
});
// Log a message to the console for group type
console.log(`Item(s) have been added to member JSON with group key ${jsonList}.`);
} else if (jsonType === "array") {
// Check if the array already exists
if (!member.data[jsonList]) {
// Create a new array
member.data[jsonList] = [];
}
// Retrieve the input values for the array type
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonList].push(newItem);
});
// Log a message to the console for array type
console.log(`Item(s) have been added to member JSON with array key ${jsonList}.`);
} else {
// Retrieve the input value and key for the basic item type
const input = form.querySelector('[ms-code-json-name]');
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonName] = newItem;
// Log a message to the console for basic item type
console.log(`Item ${newItem} has been added to member JSON with key ${jsonName}.`);
}
// Update the member JSON with the new data
await memberstack.updateMemberJSON({
json: member.data
});
// Reset the input values
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
input.value = "";
});
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #1 v0.1 💙 ADD INDIVIDUAL ITEMS TO MEMBER JSON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const forms = document.querySelectorAll('[ms-code="form1"]');
forms.forEach(form => {
const jsonType = form.getAttribute("ms-code-json-type");
const jsonList = form.getAttribute("ms-code-json-list");
form.addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the default form submission
// Retrieve the current member JSON data
const memberstack = window.$memberstackDom;
const member = await memberstack.getMemberJSON();
// Create a new member.data object if it doesn't already exist
if (!member.data) {
member.data = {};
}
if (jsonType === "group") {
// Check if the group already exists
if (!member.data[jsonList]) {
// Create a new group object
member.data[jsonList] = {};
}
// Iterate over the inputs with ms-code-json-name attribute
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonList][jsonName] = newItem;
});
// Log a message to the console for group type
console.log(`Item(s) have been added to member JSON with group key ${jsonList}.`);
} else if (jsonType === "array") {
// Check if the array already exists
if (!member.data[jsonList]) {
// Create a new array
member.data[jsonList] = [];
}
// Retrieve the input values for the array type
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonList].push(newItem);
});
// Log a message to the console for array type
console.log(`Item(s) have been added to member JSON with array key ${jsonList}.`);
} else {
// Retrieve the input value and key for the basic item type
const input = form.querySelector('[ms-code-json-name]');
const jsonName = input.getAttribute('ms-code-json-name');
const newItem = input.value;
member.data[jsonName] = newItem;
// Log a message to the console for basic item type
console.log(`Item ${newItem} has been added to member JSON with key ${jsonName}.`);
}
// Update the member JSON with the new data
await memberstack.updateMemberJSON({
json: member.data
});
// Reset the input values
const inputs = form.querySelectorAll('[ms-code-json-name]');
inputs.forEach(input => {
input.value = "";
});
});
});
});
</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.

#58 - Entrées du curseur de gamme de prix
Créez une entrée de fourchette de prix avec des entrées individuelles pour le minimum et le maximum.
<!-- 💙 MEMBERSCRIPT #58 v0.1 💙 RANGE SLIDER INPUT -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js"></script>
<style>
.irs {
font-family: inherit;
}
</style>
<script>
$(document).ready(function() {
var rangeSlider = $('[ms-code-input="range-slider"]');
var priceFromInput = $('[ms-code-input="price-from"]');
var priceToInput = $('[ms-code-input="price-to"]');
// Set the default range values
var defaultFrom = 20000;
var defaultTo = 50000;
rangeSlider.ionRangeSlider({
skin: "round", // You can also try "flat", "big", "modern", "sharp", or "square". Default styles can be overridden with CSS.
type: "double",
grid: true,
min: 0,
max: 100000,
from: defaultFrom,
to: defaultTo,
prefix: "$",
onStart: function(data) {
priceFromInput.val(data.from);
priceToInput.val(data.to);
},
onChange: function(data) {
priceFromInput.val(data.from);
priceToInput.val(data.to);
}
});
// Get the initial range values and update the inputs
var initialRange = rangeSlider.data("ionRangeSlider");
var initialData = initialRange.result;
priceFromInput.val(initialData.from);
priceToInput.val(initialData.to);
// Update the range slider and inputs when the inputs lose focus
priceFromInput.on("blur", function() {
var value = $(this).val();
var toValue = priceToInput.val();
// Perform validation
if (
value < initialRange.options.min ||
value > initialRange.options.max ||
value >= toValue ||
value > initialData.to // Check if fromValue is higher than the current toValue
) {
value = defaultFrom;
}
rangeSlider.data("ionRangeSlider").update({
from: value
});
priceFromInput.val(value);
});
priceToInput.on("blur", function() {
var value = $(this).val();
var fromValue = priceFromInput.val();
// Perform validation
if (
value < initialRange.options.min ||
value > initialRange.options.max ||
value <= fromValue ||
value < initialData.from // Check if toValue is lower than the current fromValue
) {
value = defaultTo;
}
rangeSlider.data("ionRangeSlider").update({
to: value
});
priceToInput.val(value);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #58 v0.1 💙 RANGE SLIDER INPUT -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js"></script>
<style>
.irs {
font-family: inherit;
}
</style>
<script>
$(document).ready(function() {
var rangeSlider = $('[ms-code-input="range-slider"]');
var priceFromInput = $('[ms-code-input="price-from"]');
var priceToInput = $('[ms-code-input="price-to"]');
// Set the default range values
var defaultFrom = 20000;
var defaultTo = 50000;
rangeSlider.ionRangeSlider({
skin: "round", // You can also try "flat", "big", "modern", "sharp", or "square". Default styles can be overridden with CSS.
type: "double",
grid: true,
min: 0,
max: 100000,
from: defaultFrom,
to: defaultTo,
prefix: "$",
onStart: function(data) {
priceFromInput.val(data.from);
priceToInput.val(data.to);
},
onChange: function(data) {
priceFromInput.val(data.from);
priceToInput.val(data.to);
}
});
// Get the initial range values and update the inputs
var initialRange = rangeSlider.data("ionRangeSlider");
var initialData = initialRange.result;
priceFromInput.val(initialData.from);
priceToInput.val(initialData.to);
// Update the range slider and inputs when the inputs lose focus
priceFromInput.on("blur", function() {
var value = $(this).val();
var toValue = priceToInput.val();
// Perform validation
if (
value < initialRange.options.min ||
value > initialRange.options.max ||
value >= toValue ||
value > initialData.to // Check if fromValue is higher than the current toValue
) {
value = defaultFrom;
}
rangeSlider.data("ionRangeSlider").update({
from: value
});
priceFromInput.val(value);
});
priceToInput.on("blur", function() {
var value = $(this).val();
var fromValue = priceFromInput.val();
// Perform validation
if (
value < initialRange.options.min ||
value > initialRange.options.max ||
value <= fromValue ||
value < initialData.from // Check if toValue is lower than the current fromValue
) {
value = defaultTo;
}
rangeSlider.data("ionRangeSlider").update({
to: value
});
priceToInput.val(value);
});
});
</script>

#57 - Saisie de l'heure
Ajoutez un sélecteur de temps à votre formulaire et pré-remplissez l'heure dans un champ.
<!-- 💙 MEMBERSCRIPT #57 v0.1 💙 TIME PICKER -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<link rel="stylesheet" href="https://uicdn.toast.com/tui.time-picker/latest/tui-time-picker.css">
<script src="https://uicdn.toast.com/tui.time-picker/latest/tui-time-picker.js"> </script>
<script>
$(document).ready(function() {
var tpSpinbox = new tui.TimePicker(document.querySelector('[ms-code-timepicker="box"]'), {
inputType: 'spinbox',
showMeridiem: true // If you don't use AM/PM remove this line
});
// Setup an event handler for when the time is selected
tpSpinbox.on('change', function() {
// Get the selected time
var hour = tpSpinbox.getHour();
var minute = tpSpinbox.getMinute();
var selectedTime = hour + ':' + (minute < 10 ? '0' : '') + minute;
// Update the value of the input element
document.querySelector('[ms-code-timepicker="input"]').value = selectedTime;
});
});
</script>
<!-- 💙 MEMBERSCRIPT #57 v0.1 💙 TIME PICKER -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<link rel="stylesheet" href="https://uicdn.toast.com/tui.time-picker/latest/tui-time-picker.css">
<script src="https://uicdn.toast.com/tui.time-picker/latest/tui-time-picker.js"> </script>
<script>
$(document).ready(function() {
var tpSpinbox = new tui.TimePicker(document.querySelector('[ms-code-timepicker="box"]'), {
inputType: 'spinbox',
showMeridiem: true // If you don't use AM/PM remove this line
});
// Setup an event handler for when the time is selected
tpSpinbox.on('change', function() {
// Get the selected time
var hour = tpSpinbox.getHour();
var minute = tpSpinbox.getMinute();
var selectedTime = hour + ':' + (minute < 10 ? '0' : '') + minute;
// Update the value of the input element
document.querySelector('[ms-code-timepicker="input"]').value = selectedTime;
});
});
</script>

#56 - Paires d'options d'entrée
Combinez les valeurs de plusieurs entrées dans un seul champ.
<!-- 💙 MEMBERSCRIPT #56 v0.1 💙 INPUT OPTION PAIRS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script>
$(document).ready(function() {
var groups = {};
// Get all inputs with the attribute ms-code-combine-inputs
var inputs = $('input[ms-code-combine-inputs], select[ms-code-combine-inputs]');
// For each input
inputs.each(function() {
// Split the attribute value at the dash
var parts = $(this).attr('ms-code-combine-inputs').split('-');
// If the group doesn't exist yet, create it
if (!groups[parts[0]]) {
groups[parts[0]] = {
targets: [],
values: [],
};
}
// If it's a target, add it to the targets
if (parts[1] == 'target') {
groups[parts[0]].targets.push($(this));
} else {
// It's an input, add it to the values and attach a listener
groups[parts[0]].values.push($(this));
$(this).on('input change', function() {
// On input or change, combine all values with a space in between
// and set the targets' value
var combinedValue = '';
$.each(groups[parts[0]].values, function(index, value) {
combinedValue += $(this).val();
if (index < groups[parts[0]].values.length - 1) {
combinedValue += ' '; // Add a space between values
}
});
$.each(groups[parts[0]].targets, function() {
$(this).val(combinedValue);
});
});
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #56 v0.1 💙 INPUT OPTION PAIRS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script>
$(document).ready(function() {
var groups = {};
// Get all inputs with the attribute ms-code-combine-inputs
var inputs = $('input[ms-code-combine-inputs], select[ms-code-combine-inputs]');
// For each input
inputs.each(function() {
// Split the attribute value at the dash
var parts = $(this).attr('ms-code-combine-inputs').split('-');
// If the group doesn't exist yet, create it
if (!groups[parts[0]]) {
groups[parts[0]] = {
targets: [],
values: [],
};
}
// If it's a target, add it to the targets
if (parts[1] == 'target') {
groups[parts[0]].targets.push($(this));
} else {
// It's an input, add it to the values and attach a listener
groups[parts[0]].values.push($(this));
$(this).on('input change', function() {
// On input or change, combine all values with a space in between
// and set the targets' value
var combinedValue = '';
$.each(groups[parts[0]].values, function(index, value) {
combinedValue += $(this).val();
if (index < groups[parts[0]].values.length - 1) {
combinedValue += ' '; // Add a space between values
}
});
$.each(groups[parts[0]].targets, function() {
$(this).val(combinedValue);
});
});
}
});
});
</script>

#55 - Modifier le style des cases à cocher
Modifier les styles d'un autre élément lorsqu'une case est cochée.
<!-- 💙 MEMBERSCRIPT #55 v0.1 💙 UPDATE CHECKBOX PARENT STYLES -->
<script>
// Wait for the DOM content to load
document.addEventListener('DOMContentLoaded', function() {
// Get all the checkbox elements
var checkboxes = document.querySelectorAll('[ms-code-checkbox="check"]');
// Iterate over each checkbox element
checkboxes.forEach(function(checkbox) {
// Get the boolean wrap element associated with the current checkbox
var booleanWrap = checkbox.closest('[ms-code-checkbox="wrap"]');
// Function to update the boolean wrap class based on checkbox state
function updateBooleanWrapClass() {
if (checkbox.checked) {
booleanWrap.classList.add('checked');
} else {
booleanWrap.classList.remove('checked');
}
}
// Check the initial value of the checkbox
updateBooleanWrapClass();
// Add an event listener to the checkbox to handle changes
checkbox.addEventListener('change', function() {
updateBooleanWrapClass();
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #55 v0.1 💙 UPDATE CHECKBOX PARENT STYLES -->
<script>
// Wait for the DOM content to load
document.addEventListener('DOMContentLoaded', function() {
// Get all the checkbox elements
var checkboxes = document.querySelectorAll('[ms-code-checkbox="check"]');
// Iterate over each checkbox element
checkboxes.forEach(function(checkbox) {
// Get the boolean wrap element associated with the current checkbox
var booleanWrap = checkbox.closest('[ms-code-checkbox="wrap"]');
// Function to update the boolean wrap class based on checkbox state
function updateBooleanWrapClass() {
if (checkbox.checked) {
booleanWrap.classList.add('checked');
} else {
booleanWrap.classList.remove('checked');
}
}
// Check the initial value of the checkbox
updateBooleanWrapClass();
// Add an event listener to the checkbox to handle changes
checkbox.addEventListener('change', function() {
updateBooleanWrapClass();
});
});
});
</script>

#54 - Logique des champs de formulaire à cases à cocher
Bloquer d'autres champs/éléments si une case n'est pas cochée.
<!-- 💙 MEMBERSCRIPT #54 v0.1 💙 CHECKBOX FIELD FORM LOGIC -->
<style>
.disabled {
pointer-events: none;
opacity: 0.5;
}
</style>
<script>
// Wait for the DOM content to load
document.addEventListener('DOMContentLoaded', function() {
// Get all the trigger checkboxes
var triggerCheckboxes = document.querySelectorAll('[ms-code-field-logic-trigger]');
// Iterate over each trigger checkbox
triggerCheckboxes.forEach(function(checkbox) {
// Get the value of the trigger checkbox's attribute
var triggerValue = checkbox.getAttribute('ms-code-field-logic-trigger');
// Function to update the target elements' class based on checkbox state
function updateTargetElementsClass() {
// Find the associated target elements based on the attribute value
var targetElements = document.querySelectorAll('[ms-code-field-logic-target="' + triggerValue + '"]');
// Check the new value of the trigger checkbox
if (!checkbox.checked) {
// Add the "disabled" class to each target element
targetElements.forEach(function(targetElement) {
targetElement.classList.add('disabled');
});
} else {
// Remove the "disabled" class from each target element
targetElements.forEach(function(targetElement) {
targetElement.classList.remove('disabled');
});
}
}
// Check the initial value of the trigger checkbox
updateTargetElementsClass();
// Add an event listener to the trigger checkbox to handle changes
checkbox.addEventListener('change', function() {
updateTargetElementsClass();
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #54 v0.1 💙 CHECKBOX FIELD FORM LOGIC -->
<style>
.disabled {
pointer-events: none;
opacity: 0.5;
}
</style>
<script>
// Wait for the DOM content to load
document.addEventListener('DOMContentLoaded', function() {
// Get all the trigger checkboxes
var triggerCheckboxes = document.querySelectorAll('[ms-code-field-logic-trigger]');
// Iterate over each trigger checkbox
triggerCheckboxes.forEach(function(checkbox) {
// Get the value of the trigger checkbox's attribute
var triggerValue = checkbox.getAttribute('ms-code-field-logic-trigger');
// Function to update the target elements' class based on checkbox state
function updateTargetElementsClass() {
// Find the associated target elements based on the attribute value
var targetElements = document.querySelectorAll('[ms-code-field-logic-target="' + triggerValue + '"]');
// Check the new value of the trigger checkbox
if (!checkbox.checked) {
// Add the "disabled" class to each target element
targetElements.forEach(function(targetElement) {
targetElement.classList.add('disabled');
});
} else {
// Remove the "disabled" class from each target element
targetElements.forEach(function(targetElement) {
targetElement.classList.remove('disabled');
});
}
}
// Check the initial value of the trigger checkbox
updateTargetElementsClass();
// Add an event listener to the trigger checkbox to handle changes
checkbox.addEventListener('change', function() {
updateTargetElementsClass();
});
});
});
</script>

#53 - Mettre à jour des éléments JSON avec un formulaire
Permettez à vos membres de modifier les détails de leurs éléments JSON.
<!-- 💙 MEMBERSCRIPT #53 v0.1 💙 UPDATE JSON ITEMS WITH A FORM -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Add click event listener to the document
document.addEventListener("click", async function(event) {
const target = event.target;
// Check if the clicked element has ms-code-edit-item attribute
const editItem = target.closest('[ms-code-edit-item="prompt"]');
if (editItem) {
// Get the item key from the closest ancestor element with ms-code-item-key attribute
const key = editItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// SET THE TARGET - EDIT ME
let targetObject = member.data.projects; // Update this line with the desired target location
if (member.data && targetObject && targetObject[key]) {
// Get the form element with the ms-code-edit-item="form" attribute
const form = document.querySelector('form[ms-code-edit-item="form"]');
if (form) {
// Loop through the form fields
for (const field of form.elements) {
const jsonName = field.getAttribute('ms-code-json-name');
if (jsonName && targetObject[key].hasOwnProperty(jsonName)) {
// Pre-fill the form field with the corresponding value from the JSON item
field.value = targetObject[key][jsonName];
}
}
// Get the modal element with the ms-code-edit-item="modal" attribute
const modal = document.querySelector('[ms-code-edit-item="modal"]');
if (modal) {
// Set the display property of the modal to flex
modal.style.display = 'flex';
}
// Add submit event listener to the form
form.addEventListener("submit", async function(event) {
event.preventDefault(); // Prevent the form from submitting normally
// Create an object to hold the updated values
const updatedValues = {};
// Loop through the form fields
for (const field of form.elements) {
const jsonName = field.getAttribute('ms-code-json-name');
if (jsonName) {
// Update the corresponding value in the updatedValues object
updatedValues[jsonName] = field.value;
}
}
// Update the target object with the new values
targetObject[key] = { ...targetObject[key], ...updatedValues };
// Update the member JSON using the Memberstack SDK
await memberstack.updateMemberJSON({
json: member.data
});
// Optional: Display a success message or perform any other desired action
console.log('Member JSON updated successfully');
});
} else {
console.error('Form element not found');
}
} else {
console.error(`Could not find item with key: ${key}`);
}
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #53 v0.1 💙 UPDATE JSON ITEMS WITH A FORM -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
// Add click event listener to the document
document.addEventListener("click", async function(event) {
const target = event.target;
// Check if the clicked element has ms-code-edit-item attribute
const editItem = target.closest('[ms-code-edit-item="prompt"]');
if (editItem) {
// Get the item key from the closest ancestor element with ms-code-item-key attribute
const key = editItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');
// Retrieve the current member JSON data
const member = await memberstack.getMemberJSON();
// SET THE TARGET - EDIT ME
let targetObject = member.data.projects; // Update this line with the desired target location
if (member.data && targetObject && targetObject[key]) {
// Get the form element with the ms-code-edit-item="form" attribute
const form = document.querySelector('form[ms-code-edit-item="form"]');
if (form) {
// Loop through the form fields
for (const field of form.elements) {
const jsonName = field.getAttribute('ms-code-json-name');
if (jsonName && targetObject[key].hasOwnProperty(jsonName)) {
// Pre-fill the form field with the corresponding value from the JSON item
field.value = targetObject[key][jsonName];
}
}
// Get the modal element with the ms-code-edit-item="modal" attribute
const modal = document.querySelector('[ms-code-edit-item="modal"]');
if (modal) {
// Set the display property of the modal to flex
modal.style.display = 'flex';
}
// Add submit event listener to the form
form.addEventListener("submit", async function(event) {
event.preventDefault(); // Prevent the form from submitting normally
// Create an object to hold the updated values
const updatedValues = {};
// Loop through the form fields
for (const field of form.elements) {
const jsonName = field.getAttribute('ms-code-json-name');
if (jsonName) {
// Update the corresponding value in the updatedValues object
updatedValues[jsonName] = field.value;
}
}
// Update the target object with the new values
targetObject[key] = { ...targetObject[key], ...updatedValues };
// Update the member JSON using the Memberstack SDK
await memberstack.updateMemberJSON({
json: member.data
});
// Optional: Display a success message or perform any other desired action
console.log('Member JSON updated successfully');
});
} else {
console.error('Form element not found');
}
} else {
console.error(`Could not find item with key: ${key}`);
}
}
});
});
</script>

#52 - Redirection de page retardée
Rediriger les membres vers une nouvelle page avec un délai facultatif.
<!-- 💙 MEMBERSCRIPT #52 v0.1 💙 DELAYED REDIRECT TO NEW PAGE -->
<script>
const redirectToNewPage = function() {
setTimeout(function() {
window.location.href = "/your-page";
}, 1000); // Delay in milliseconds
};
redirectToNewPage();
</script>
<!-- 💙 MEMBERSCRIPT #52 v0.1 💙 DELAYED REDIRECT TO NEW PAGE -->
<script>
const redirectToNewPage = function() {
setTimeout(function() {
window.location.href = "/your-page";
}, 1000); // Delay in milliseconds
};
redirectToNewPage();
</script>

#51 - Afficher les métadonnées des membres
Affichez les métadonnées des membres de manière dynamique sur votre site web.
<!-- 💙 MEMBERSCRIPT #51 v0.2 💙 DISPLAY MEMBER METADATA -->
<script>
function replaceTextWithMetadata(metadata) {
var els = Array.from(document.querySelectorAll('[ms-code-member-meta]'));
els.forEach((el) => {
const key = el.getAttribute('ms-code-member-meta');
const value = metadata[key];
if (value !== undefined) {
el.innerHTML = value;
el.value = value;
el.src = value;
}
});
}
const memberstack = window.$memberstackDom;
memberstack.getCurrentMember()
.then(({ data: member }) => {
if (member && member.metaData) {
replaceTextWithMetadata(member.metaData);
}
})
.catch((error) => {
console.error('Error retrieving member data:', error);
});
</script>
<!-- 💙 MEMBERSCRIPT #51 v0.2 💙 DISPLAY MEMBER METADATA -->
<script>
function replaceTextWithMetadata(metadata) {
var els = Array.from(document.querySelectorAll('[ms-code-member-meta]'));
els.forEach((el) => {
const key = el.getAttribute('ms-code-member-meta');
const value = metadata[key];
if (value !== undefined) {
el.innerHTML = value;
el.value = value;
el.src = value;
}
});
}
const memberstack = window.$memberstackDom;
memberstack.getCurrentMember()
.then(({ data: member }) => {
if (member && member.metaData) {
replaceTextWithMetadata(member.metaData);
}
})
.catch((error) => {
console.error('Error retrieving member data:', error);
});
</script>

#50 - Mode sombre inter-appareils
Option de mode sombre persistant qui continue à fonctionner sur les différents appareils de vos membres.
Code de la tête
Put this in the <head> section of your site.
<!-- 💙 MEMBERSCRIPT #50 HEAD CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const themePreference = localStorage.getItem('themePreference');
if (themePreference === 'dark') {
document.body.classList.add('dark');
}
});
</script>
Code du corps
Put this in the </body> section of your site.
<!-- 💙 MEMBERSCRIPT #50 BODY CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const darkModeToggle = document.querySelector('[ms-code-dark-mode="toggle"]');
const bodyElement = document.querySelector('.body');
// Function to check if theme preference is saved in member JSON
function checkThemePreference() {
const memberstack = window.$memberstackDom;
memberstack.getMemberJSON()
.then(function(memberData) {
const themePreference = memberData.data?.themePreference;
if (themePreference === 'dark') {
enableDarkMode();
} else {
disableDarkMode();
}
})
.catch(function(error) {
console.error('Error retrieving member data:', error);
});
}
// Function to enable dark mode
function enableDarkMode() {
darkModeToggle.classList.add('dark');
bodyElement.classList.add('dark');
updateThemePreference('dark');
}
// Function to disable dark mode
function disableDarkMode() {
darkModeToggle.classList.remove('dark');
bodyElement.classList.remove('dark');
updateThemePreference('light');
}
// Function to update theme preference in member JSON
function updateThemePreference(themePreference) {
const memberstack = window.$memberstackDom;
memberstack.getMemberJSON()
.then(function(memberData) {
memberData.data = memberData.data || {};
memberData.data.themePreference = themePreference;
memberstack.updateMemberJSON({ json: memberData.data })
.then(function() {
localStorage.setItem('themePreference', themePreference);
})
.catch(function(error) {
console.error('Error updating member data:', error);
});
})
.catch(function(error) {
console.error('Error retrieving member data:', error);
});
}
// Event listener for dark mode toggle
darkModeToggle.addEventListener('click', function() {
if (darkModeToggle.classList.contains('dark')) {
disableDarkMode();
} else {
enableDarkMode();
}
});
// Apply transition duration and timing to all elements
const transitionDuration = '0.0s';
const transitionTiming = 'ease';
const elementsToTransition = [darkModeToggle, bodyElement];
elementsToTransition.forEach(function(element) {
element.style.transitionDuration = transitionDuration;
element.style.transitionTimingFunction = transitionTiming;
});
// Check theme preference on page load
const savedThemePreference = localStorage.getItem('themePreference');
if (savedThemePreference === 'dark') {
enableDarkMode();
} else {
disableDarkMode();
}
checkThemePreference();
});
</script>
Code de la tête
Put this in the <head> section of your site.
<!-- 💙 MEMBERSCRIPT #50 HEAD CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const themePreference = localStorage.getItem('themePreference');
if (themePreference === 'dark') {
document.body.classList.add('dark');
}
});
</script>
Code du corps
Put this in the </body> section of your site.
<!-- 💙 MEMBERSCRIPT #50 BODY CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const darkModeToggle = document.querySelector('[ms-code-dark-mode="toggle"]');
const bodyElement = document.querySelector('.body');
// Function to check if theme preference is saved in member JSON
function checkThemePreference() {
const memberstack = window.$memberstackDom;
memberstack.getMemberJSON()
.then(function(memberData) {
const themePreference = memberData.data?.themePreference;
if (themePreference === 'dark') {
enableDarkMode();
} else {
disableDarkMode();
}
})
.catch(function(error) {
console.error('Error retrieving member data:', error);
});
}
// Function to enable dark mode
function enableDarkMode() {
darkModeToggle.classList.add('dark');
bodyElement.classList.add('dark');
updateThemePreference('dark');
}
// Function to disable dark mode
function disableDarkMode() {
darkModeToggle.classList.remove('dark');
bodyElement.classList.remove('dark');
updateThemePreference('light');
}
// Function to update theme preference in member JSON
function updateThemePreference(themePreference) {
const memberstack = window.$memberstackDom;
memberstack.getMemberJSON()
.then(function(memberData) {
memberData.data = memberData.data || {};
memberData.data.themePreference = themePreference;
memberstack.updateMemberJSON({ json: memberData.data })
.then(function() {
localStorage.setItem('themePreference', themePreference);
})
.catch(function(error) {
console.error('Error updating member data:', error);
});
})
.catch(function(error) {
console.error('Error retrieving member data:', error);
});
}
// Event listener for dark mode toggle
darkModeToggle.addEventListener('click', function() {
if (darkModeToggle.classList.contains('dark')) {
disableDarkMode();
} else {
enableDarkMode();
}
});
// Apply transition duration and timing to all elements
const transitionDuration = '0.0s';
const transitionTiming = 'ease';
const elementsToTransition = [darkModeToggle, bodyElement];
elementsToTransition.forEach(function(element) {
element.style.transitionDuration = transitionDuration;
element.style.transitionTimingFunction = transitionTiming;
});
// Check theme preference on page load
const savedThemePreference = localStorage.getItem('themePreference');
if (savedThemePreference === 'dark') {
enableDarkMode();
} else {
disableDarkMode();
}
checkThemePreference();
});
</script>

#49 - Désactiver la première option d'une entrée de sélection
Empêchez les utilisateurs de sélectionner l'option de remplacement dans vos entrées de sélection.
<!-- 💙 MEMBERSCRIPT #49 v0.1 💙 DISABLE FIRST OPTION IN SELECT INPUT -->
<script>
let selects = document.querySelectorAll("select[ms-code=hide-first-option]");
selects.forEach((select) => {
let options = select.getElementsByTagName("option");
options[0].hidden = true;
});
</script>
<!-- 💙 MEMBERSCRIPT #49 v0.1 💙 DISABLE FIRST OPTION IN SELECT INPUT -->
<script>
let selects = document.querySelectorAll("select[ms-code=hide-first-option]");
selects.forEach((select) => {
let options = select.getElementsByTagName("option");
options[0].hidden = true;
});
</script>

#48 - Autocomplétion des entrées d'adresses
Pré-remplir toutes les entrées d'adresse à l'aide de l'API Google Places !
Code de la tête
Place this in your page <head>
<!-- 💙 MEMBERSCRIPT #48 HEAD CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR-API-KEY&libraries=places&callback=initAutocomplete" async defer> </script>
<style>
.pac-logo::after {
display: none;
}
.pac-container {
border-radius: 5px;
border: 1px solid #ccc;
}
.pac-item {
padding: 0 10px;
}
</style>
Code du corps
Place this in your page </body>
<!-- 💙 MEMBERSCRIPT #48 BODY CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script>
let autocomplete;
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(
document.querySelector('input[ms-code-input="address"]'),
{
componentRestrictions: { country: ['US'] },
fields: ['address_components'],
types: ['address']
}
);
autocomplete.addListener('place_changed', function() {
const place = autocomplete.getPlace();
if (place) {
const addressInput = document.querySelector('input[ms-code-input="address"]');
const cityInput = document.querySelector('input[ms-code-input="city"]');
const regionInput = document.querySelector('input[ms-code-input="region"]');
const countryInput = document.querySelector('input[ms-code-input="country"]');
const postalCodeInput = document.querySelector('input[ms-code-input="postal-code"]');
addressInput.value = extractAddress(place);
cityInput.value = extractCity(place);
regionInput.value = extractRegion(place);
countryInput.value = extractCountry(place);
postalCodeInput.value = extractPostalCode(place);
}
});
}
function extractAddress(place) {
let address = '';
const streetNumber = extractComponent(place, 'street_number');
const route = extractComponent(place, 'route');
if (streetNumber) {
address += streetNumber + ' ';
}
if (route) {
address += route;
}
return address.trim();
}
function extractComponent(place, componentType) {
for (const component of place.address_components) {
if (component.types.includes(componentType)) {
return component.long_name;
}
}
return '';
}
function extractCity(place) {
for (const component of place.address_components) {
if (component.types.includes('locality')) {
return component.long_name;
}
}
return '';
}
function extractRegion(place) {
for (const component of place.address_components) {
if (component.types.includes('administrative_area_level_1')) {
return component.long_name;
}
}
return '';
}
function extractCountry(place) {
for (const component of place.address_components) {
if (component.types.includes('country')) {
return component.long_name;
}
}
return '';
}
function extractPostalCode(place) {
for (const component of place.address_components) {
if (component.types.includes('postal_code')) {
return component.long_name;
}
}
return '';
}
</script>
Code de la tête
Place this in your page <head>
<!-- 💙 MEMBERSCRIPT #48 HEAD CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR-API-KEY&libraries=places&callback=initAutocomplete" async defer> </script>
<style>
.pac-logo::after {
display: none;
}
.pac-container {
border-radius: 5px;
border: 1px solid #ccc;
}
.pac-item {
padding: 0 10px;
}
</style>
Code du corps
Place this in your page </body>
<!-- 💙 MEMBERSCRIPT #48 BODY CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script>
let autocomplete;
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(
document.querySelector('input[ms-code-input="address"]'),
{
componentRestrictions: { country: ['US'] },
fields: ['address_components'],
types: ['address']
}
);
autocomplete.addListener('place_changed', function() {
const place = autocomplete.getPlace();
if (place) {
const addressInput = document.querySelector('input[ms-code-input="address"]');
const cityInput = document.querySelector('input[ms-code-input="city"]');
const regionInput = document.querySelector('input[ms-code-input="region"]');
const countryInput = document.querySelector('input[ms-code-input="country"]');
const postalCodeInput = document.querySelector('input[ms-code-input="postal-code"]');
addressInput.value = extractAddress(place);
cityInput.value = extractCity(place);
regionInput.value = extractRegion(place);
countryInput.value = extractCountry(place);
postalCodeInput.value = extractPostalCode(place);
}
});
}
function extractAddress(place) {
let address = '';
const streetNumber = extractComponent(place, 'street_number');
const route = extractComponent(place, 'route');
if (streetNumber) {
address += streetNumber + ' ';
}
if (route) {
address += route;
}
return address.trim();
}
function extractComponent(place, componentType) {
for (const component of place.address_components) {
if (component.types.includes(componentType)) {
return component.long_name;
}
}
return '';
}
function extractCity(place) {
for (const component of place.address_components) {
if (component.types.includes('locality')) {
return component.long_name;
}
}
return '';
}
function extractRegion(place) {
for (const component of place.address_components) {
if (component.types.includes('administrative_area_level_1')) {
return component.long_name;
}
}
return '';
}
function extractCountry(place) {
for (const component of place.address_components) {
if (component.types.includes('country')) {
return component.long_name;
}
}
return '';
}
function extractPostalCode(place) {
for (const component of place.address_components) {
if (component.types.includes('postal_code')) {
return component.long_name;
}
}
return '';
}
</script>

#47 - Afficher la date du membre JSON
Indiquez aux membres une date - par exemple, la date d'expiration de leur plan !
<!-- 💙 MEMBERSCRIPT #47 v0.1 💙 DISPLAY ONE TIME DATE -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
const formatDate = function(date) {
const options = { month: 'long', day: 'numeric', year: 'numeric' };
return new Date(date).toLocaleDateString('en-US', options);
// Replace 'en-US' with one of these depending on your locale: en-US, en-GB, en-CA, en-AU, fr-FR, de-DE, es-ES, it-IT, ja-JP, ko-KR, pt-BR, ru-RU, zn-CH, ar-SA
};
const updateTextSpans = async function() {
const member = await memberstack.getMemberJSON();
if (!member.data || !member.data['one-time-date']) {
// Member data or one-time date not available, do nothing
return;
}
const oneTimeDate = formatDate(member.data['one-time-date']);
const textSpans = document.querySelectorAll('[ms-code-display-text="one-time-date"]');
textSpans.forEach(span => {
span.textContent = oneTimeDate;
});
};
updateTextSpans();
});
</script>
<!-- 💙 MEMBERSCRIPT #47 v0.1 💙 DISPLAY ONE TIME DATE -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
const memberstack = window.$memberstackDom;
const formatDate = function(date) {
const options = { month: 'long', day: 'numeric', year: 'numeric' };
return new Date(date).toLocaleDateString('en-US', options);
// Replace 'en-US' with one of these depending on your locale: en-US, en-GB, en-CA, en-AU, fr-FR, de-DE, es-ES, it-IT, ja-JP, ko-KR, pt-BR, ru-RU, zn-CH, ar-SA
};
const updateTextSpans = async function() {
const member = await memberstack.getMemberJSON();
if (!member.data || !member.data['one-time-date']) {
// Member data or one-time date not available, do nothing
return;
}
const oneTimeDate = formatDate(member.data['one-time-date']);
const textSpans = document.querySelectorAll('[ms-code-display-text="one-time-date"]');
textSpans.forEach(span => {
span.textContent = oneTimeDate;
});
};
updateTextSpans();
});
</script>

#46 - Confirmer le mot de passe
Ajoutez une entrée de confirmation de mot de passe à vos formulaires d'inscription et de réinitialisation de mot de passe.
<!-- 💙 MEMBERSCRIPT #46 v0.1 💙 CONFIRM PASSWORD INPUT -->
<script>
var password = document.querySelector('[data-ms-member=password]')
, confirm_password = document.querySelector('[ms-code-password=confirm]')
function validatePassword(){
if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match");
confirm_password.classList.add("invalid")
confirm_password.classList.remove("valid")
} else {
confirm_password.setCustomValidity('');
confirm_password.classList.remove("invalid")
confirm_password.classList.add("valid")
}
}
password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>
<!-- 💙 MEMBERSCRIPT #46 v0.1 💙 CONFIRM PASSWORD INPUT -->
<script>
var password = document.querySelector('[data-ms-member=password]')
, confirm_password = document.querySelector('[ms-code-password=confirm]')
function validatePassword(){
if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match");
confirm_password.classList.add("invalid")
confirm_password.classList.remove("valid")
} else {
confirm_password.setCustomValidity('');
confirm_password.classList.remove("invalid")
confirm_password.classList.add("valid")
}
}
password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>

#45 - Afficher/masquer le mot de passe
Ajoutez un bouton "afficher/masquer le mot de passe" à tout formulaire comportant une entrée de mot de passe.
<!-- 💙 MEMBERSCRIPT #45 v0.2 💙 SHOW AND HIDE PASSWORD -->
<script>
document.querySelectorAll("[ms-code-password='transform']").forEach(function(button) {
button.addEventListener("click", transform);
});
var isPassword = true;
function transform() {
var passwordInputs = document.querySelectorAll("[data-ms-member='password'], [data-ms-member='new-password'], [data-ms-member='current-password']");
passwordInputs.forEach(function(myInput) {
var inputType = myInput.getAttribute("type");
if (isPassword) {
myInput.setAttribute("type", "text");
} else {
myInput.setAttribute("type", "password");
}
});
isPassword = !isPassword;
}
</script>
<!-- 💙 MEMBERSCRIPT #45 v0.2 💙 SHOW AND HIDE PASSWORD -->
<script>
document.querySelectorAll("[ms-code-password='transform']").forEach(function(button) {
button.addEventListener("click", transform);
});
var isPassword = true;
function transform() {
var passwordInputs = document.querySelectorAll("[data-ms-member='password'], [data-ms-member='new-password'], [data-ms-member='current-password']");
passwordInputs.forEach(function(myInput) {
var inputType = myInput.getAttribute("type");
if (isPassword) {
myInput.setAttribute("type", "text");
} else {
myInput.setAttribute("type", "password");
}
});
isPassword = !isPassword;
}
</script>

#44 - Afficher l'élément si l'attribut correspond à l'identifiant du membre
Affiche conditionnellement les éléments qui ont un attribut correspondant à l'ID des membres.
<!-- 💙 MEMBERSCRIPT #44 v0.1 💙 SHOW ELEMENT IF ATTRIBUTE MATCHES MEMBER ID -->
<script>
document.addEventListener("DOMContentLoaded", function() {
if (localStorage.getItem("_ms-mem")) {
const memberData = JSON.parse(localStorage.getItem("_ms-mem"));
const memberId = memberData.id;
const elements = document.querySelectorAll("[ms-code-member-id='" + memberId + "']");
elements.forEach(element => {
element.style.display = "block";
});
}
});
</script>
<!-- 💙 MEMBERSCRIPT #44 v0.1 💙 SHOW ELEMENT IF ATTRIBUTE MATCHES MEMBER ID -->
<script>
document.addEventListener("DOMContentLoaded", function() {
if (localStorage.getItem("_ms-mem")) {
const memberData = JSON.parse(localStorage.getItem("_ms-mem"));
const memberId = memberData.id;
const elements = document.querySelectorAll("[ms-code-member-id='" + memberId + "']");
elements.forEach(element => {
element.style.display = "block";
});
}
});
</script>

#43 - Bloquer le défilement lorsque la fenêtre modale est ouverte
Empêcher le défilement de la page lorsque quelqu'un ouvre une fenêtre modale.
<!-- 💙 MEMBERSCRIPT #43 v0.1 💙 BLOCK SCROLLING WHEN MODAL IS OPEN -->
<style>
.no-scroll {
overflow: hidden;
}
</style>
<script>
function isDesktopViewport() {
return window.innerWidth >= 900; // Adjust the breakpoint width as needed
}
const codeBlocks = document.querySelectorAll('[ms-code-block-scroll]');
function handleScrollBlock(event) {
if (isDesktopViewport()) {
document.body.classList.add('no-scroll');
}
}
function handleScrollUnblock(event) {
if (isDesktopViewport()) {
document.body.classList.remove('no-scroll');
}
}
codeBlocks.forEach(codeBlock => {
codeBlock.addEventListener('mouseenter', handleScrollBlock);
codeBlock.addEventListener('mouseleave', handleScrollUnblock);
});
</script>
<!-- 💙 MEMBERSCRIPT #43 v0.1 💙 BLOCK SCROLLING WHEN MODAL IS OPEN -->
<style>
.no-scroll {
overflow: hidden;
}
</style>
<script>
function isDesktopViewport() {
return window.innerWidth >= 900; // Adjust the breakpoint width as needed
}
const codeBlocks = document.querySelectorAll('[ms-code-block-scroll]');
function handleScrollBlock(event) {
if (isDesktopViewport()) {
document.body.classList.add('no-scroll');
}
}
function handleScrollUnblock(event) {
if (isDesktopViewport()) {
document.body.classList.remove('no-scroll');
}
}
codeBlocks.forEach(codeBlock => {
codeBlock.addEventListener('mouseenter', handleScrollBlock);
codeBlock.addEventListener('mouseleave', handleScrollUnblock);
});
</script>

#42 - Champ de formulaire de l'éditeur d'images
Permettez aux gens de télécharger et de modifier des photos, puis de les envoyer sur Google Drive !
Code de la tête
Place this in your page <head>
<!-- 💙 MEMBERSCRIPT #42 HEAD CODE v0.2 💙 FILE EDITOR FEATURE -->
<link rel="stylesheet" href="https://unpkg.com/filepond@^4/dist/filepond.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" />
Code du corps
Place this in your page </body>
<!-- 💙 MEMBERSCRIPT #42 BODY CODE v0.2 💙 FILE EDITOR FEATURE -->
<script> src="https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.js"> </script>
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script> src="https://scaleflex.cloudimg.io/v7/plugins/filerobot-image-editor/latest/filerobot-image-editor.min.js"> </script>
<style>
.dXhZSB {
background-color: #2962ff;
}
.FIE_root * {
font-family: inherit !important;
}
.SfxModal-Wrapper * {
font-family: inherit !important;
}
.jpHEiD {
font-family: inherit !important;
}
#editor_container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Register the plugins
FilePond.registerPlugin(FilePondPluginImagePreview);
FilePond.registerPlugin(FilePondPluginImageEdit);
const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create(inputElement, {
credits: false,
name: 'fileToUpload',
storeAsFile: true,
imageEditEditor: {
open: (file, instructions) => {
console.log('Open editor', file, instructions);
openFilerobotImageEditor(file, instructions);
},
onconfirm: (output) => {
console.log('Confirm editor', output);
handleImageEditConfirm(output);
},
oncancel: () => {
console.log('Cancel editor');
handleImageEditCancel();
},
onclose: () => {
console.log('Close editor');
handleImageEditClose();
}
}
});
function openFilerobotImageEditor(file, instructions) {
const imageURL = URL.createObjectURL(file);
const config = {
source: imageURL,
onSave: (updatedImage) => {
confirmCallback(updatedImage);
},
annotationsCommon: {
fill: '#ff0000'
},
Text: {
text: 'Add your text here',
font: 'inherit'
}, // Set font to inherit from the page body
Rotate: {
angle: instructions.rotation,
componentType: 'slider'
},
tabsIds: [
'Adjust',
'Annotate',
'Watermark'
],
defaultTabId: 'Annotate',
defaultToolId: 'Text'
};
const editorContainer = document.createElement('div');
editorContainer.id = 'editor_container';
document.body.appendChild(editorContainer);
const filerobotImageEditor = new window.FilerobotImageEditor(editorContainer, config);
const confirmCallback = (output) => {
console.log('Confirmed:', output);
const dataURL = output.imageBase64;
const file = dataURLToFile(dataURL, output.name);
// Add the file to FilePond
pond.addFiles([file]);
document.body.removeChild(editorContainer); // Remove the editor container
};
function dataURLToFile(dataURL, fileName) {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const fileExtension = mime.split('/')[1];
const updatedFileName = fileName + '.' + fileExtension;
const bstr = atob(arr[1]);
const n = bstr.length;
const u8arr = new Uint8Array(n);
for (let i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i);
}
return new File([u8arr], updatedFileName, { type: mime });
}
const cancelCallback = () => {
console.log('Canceled');
document.body.removeChild(editorContainer); // Remove the editor container
};
const closeButton = document.createElement('button');
closeButton.textContent = 'Close';
closeButton.addEventListener('click', () => {
filerobotImageEditor.onClose();
});
const buttonContainer = document.createElement('div');
buttonContainer.appendChild(closeButton);
editorContainer.appendChild(buttonContainer);
filerobotImageEditor.render({
onClose: (closingReason) => {
console.log('Closing reason', closingReason);
filerobotImageEditor.terminate();
},
});
}
function handleImageEditConfirm(output) {
console.log('Image edit confirmed:', output);
// Handle the confirmed output here
}
function handleImageEditCancel() {
console.log('Image edit canceled');
// Handle the canceled edit here
}
function handleImageEditClose() {
console.log('Image editor closed');
// Handle the editor close here
}
});
</script>
Code de la tête
Place this in your page <head>
<!-- 💙 MEMBERSCRIPT #42 HEAD CODE v0.2 💙 FILE EDITOR FEATURE -->
<link rel="stylesheet" href="https://unpkg.com/filepond@^4/dist/filepond.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" />
Code du corps
Place this in your page </body>
<!-- 💙 MEMBERSCRIPT #42 BODY CODE v0.2 💙 FILE EDITOR FEATURE -->
<script> src="https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.js"> </script>
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script> src="https://scaleflex.cloudimg.io/v7/plugins/filerobot-image-editor/latest/filerobot-image-editor.min.js"> </script>
<style>
.dXhZSB {
background-color: #2962ff;
}
.FIE_root * {
font-family: inherit !important;
}
.SfxModal-Wrapper * {
font-family: inherit !important;
}
.jpHEiD {
font-family: inherit !important;
}
#editor_container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Register the plugins
FilePond.registerPlugin(FilePondPluginImagePreview);
FilePond.registerPlugin(FilePondPluginImageEdit);
const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create(inputElement, {
credits: false,
name: 'fileToUpload',
storeAsFile: true,
imageEditEditor: {
open: (file, instructions) => {
console.log('Open editor', file, instructions);
openFilerobotImageEditor(file, instructions);
},
onconfirm: (output) => {
console.log('Confirm editor', output);
handleImageEditConfirm(output);
},
oncancel: () => {
console.log('Cancel editor');
handleImageEditCancel();
},
onclose: () => {
console.log('Close editor');
handleImageEditClose();
}
}
});
function openFilerobotImageEditor(file, instructions) {
const imageURL = URL.createObjectURL(file);
const config = {
source: imageURL,
onSave: (updatedImage) => {
confirmCallback(updatedImage);
},
annotationsCommon: {
fill: '#ff0000'
},
Text: {
text: 'Add your text here',
font: 'inherit'
}, // Set font to inherit from the page body
Rotate: {
angle: instructions.rotation,
componentType: 'slider'
},
tabsIds: [
'Adjust',
'Annotate',
'Watermark'
],
defaultTabId: 'Annotate',
defaultToolId: 'Text'
};
const editorContainer = document.createElement('div');
editorContainer.id = 'editor_container';
document.body.appendChild(editorContainer);
const filerobotImageEditor = new window.FilerobotImageEditor(editorContainer, config);
const confirmCallback = (output) => {
console.log('Confirmed:', output);
const dataURL = output.imageBase64;
const file = dataURLToFile(dataURL, output.name);
// Add the file to FilePond
pond.addFiles([file]);
document.body.removeChild(editorContainer); // Remove the editor container
};
function dataURLToFile(dataURL, fileName) {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const fileExtension = mime.split('/')[1];
const updatedFileName = fileName + '.' + fileExtension;
const bstr = atob(arr[1]);
const n = bstr.length;
const u8arr = new Uint8Array(n);
for (let i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i);
}
return new File([u8arr], updatedFileName, { type: mime });
}
const cancelCallback = () => {
console.log('Canceled');
document.body.removeChild(editorContainer); // Remove the editor container
};
const closeButton = document.createElement('button');
closeButton.textContent = 'Close';
closeButton.addEventListener('click', () => {
filerobotImageEditor.onClose();
});
const buttonContainer = document.createElement('div');
buttonContainer.appendChild(closeButton);
editorContainer.appendChild(buttonContainer);
filerobotImageEditor.render({
onClose: (closingReason) => {
console.log('Closing reason', closingReason);
filerobotImageEditor.terminate();
},
});
}
function handleImageEditConfirm(output) {
console.log('Image edit confirmed:', output);
// Handle the confirmed output here
}
function handleImageEditCancel() {
console.log('Image edit canceled');
// Handle the canceled edit here
}
function handleImageEditClose() {
console.log('Image editor closed');
// Handle the editor close here
}
});
</script>

#41 - Des entrées de numéros de téléphone parfaites
Les entrées de numéros de téléphone internationaux, comme elles devraient l'être.
Avec recherche d'IP
Utilisez cette option si vous souhaitez que le pays IP de l'utilisateur soit automatiquement prérempli. IMPORTANT : N'utilisez pas cette option avec les formulaires de profil, car elle se comporterait de manière erratique.
<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
$(document).ready(function() {
$('input[ms-code-phone-number]').each(function() {
var input = this;
var preferredCountries = $(input).attr('ms-code-phone-number').split(',');
var iti = window.intlTelInput(input, {
preferredCountries: preferredCountries,
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
});
$.get("https://ipinfo.io", function(response) {
var countryCode = response.country;
iti.setCountry(countryCode);
}, "jsonp");
input.addEventListener('change', formatPhoneNumber);
input.addEventListener('keyup', formatPhoneNumber);
function formatPhoneNumber() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
}
var form = $(input).closest('form');
form.submit(function() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
});
});
});
</script>
Sans recherche d'IP
Utilisez cette option pour les formulaires de profil et/ou si vous ne souhaitez pas procéder à un pré-remplissage automatique sur la base de l'adresse IP de l'utilisateur.
<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
$(document).ready(function() {
$('input[ms-code-phone-number]').each(function() {
var input = this;
var preferredCountries = $(input).attr('ms-code-phone-number').split(',');
var iti = window.intlTelInput(input, {
preferredCountries: preferredCountries,
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
});
input.addEventListener('change', formatPhoneNumber);
input.addEventListener('keyup', formatPhoneNumber);
function formatPhoneNumber() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
}
var form = $(input).closest('form');
form.submit(function() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
});
});
});
</script>
Avec recherche d'IP
Utilisez cette option si vous souhaitez que le pays IP de l'utilisateur soit automatiquement prérempli. IMPORTANT : N'utilisez pas cette option avec les formulaires de profil, car elle se comporterait de manière erratique.
<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
$(document).ready(function() {
$('input[ms-code-phone-number]').each(function() {
var input = this;
var preferredCountries = $(input).attr('ms-code-phone-number').split(',');
var iti = window.intlTelInput(input, {
preferredCountries: preferredCountries,
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
});
$.get("https://ipinfo.io", function(response) {
var countryCode = response.country;
iti.setCountry(countryCode);
}, "jsonp");
input.addEventListener('change', formatPhoneNumber);
input.addEventListener('keyup', formatPhoneNumber);
function formatPhoneNumber() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
}
var form = $(input).closest('form');
form.submit(function() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
});
});
});
</script>
Sans recherche d'IP
Utilisez cette option pour les formulaires de profil et/ou si vous ne souhaitez pas procéder à un pré-remplissage automatique sur la base de l'adresse IP de l'utilisateur.
<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
$(document).ready(function() {
$('input[ms-code-phone-number]').each(function() {
var input = this;
var preferredCountries = $(input).attr('ms-code-phone-number').split(',');
var iti = window.intlTelInput(input, {
preferredCountries: preferredCountries,
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
});
input.addEventListener('change', formatPhoneNumber);
input.addEventListener('keyup', formatPhoneNumber);
function formatPhoneNumber() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
}
var form = $(input).closest('form');
form.submit(function() {
var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
input.value = formattedNumber;
});
});
});
</script>

#40 - Drag And Drop File Uploader (téléchargeur de fichiers par glisser-déposer)
Ajoutez facilement une fonction de téléchargement de fichiers par glisser-déposer à votre site Webflow !
Important
Si vous utilisez le MemberScript #38, assurez-vous de placer ce script APRES !
<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create(inputElement, {
credits: false,
name: 'fileToUpload',
storeAsFile: true
// for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
});
});
</script>
Important
Si vous utilisez le MemberScript #38, assurez-vous de placer ce script APRES !
<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create(inputElement, {
credits: false,
name: 'fileToUpload',
storeAsFile: true
// for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
});
});
</script>

#39 - Améliorer les champs de sélection
Ajouter des recherches et une meilleure interface utilisateur pour sélectionner et multi-sélectionner les champs !
Code de la tête
Put this in the <head> section of your page.
<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />
Code du corps
Put this in the </body> section of your page.
<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
$('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
Code de la tête
Put this in the <head> section of your page.
<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />
Code du corps
Put this in the </body> section of your page.
<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
$('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
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.