Mitglieder-Skripte
Eine attributbasierte Lösung zum Hinzufügen von Funktionen zu Ihrer Webflow-Site.
Kopieren Sie einfach etwas Code, fügen Sie einige Attribute hinzu, und schon sind Sie fertig.
Alle Memberstack-Kunden können im 2.0 Slack um Unterstützung bitten. Bitte beachten Sie, dass dies keine offiziellen Funktionen sind und der Support nicht garantiert werden kann.

#118 - Speichern der letzten IP-Adresse des Mitglieds
Aktualisieren Sie ein benutzerdefiniertes Feld mit der letzten IP-Adresse, mit der sich Ihre Mitglieder anmelden.
<!-- 💙 MEMBERSCRIPT #118 v0.1 💙 - SAVE LAST IP ADDRESS -->
<script>
const memberstack = window.$memberstackDom;
memberstack.getCurrentMember().then(async (response) => {
if (response && response.data) {
const member = response.data;
try {
// Fetch the current IP address from the ipify API
const ipResponse = await fetch('https://api.ipify.org?format=json');
const ipData = await ipResponse.json();
const currentIpAddress = ipData.ip;
// Retrieve the stored IP address from Memberstack's custom fields
const storedIpAddress = member.customFields["last-ip"];
// Check if the IP address has changed and update if necessary
if (currentIpAddress !== storedIpAddress) {
await memberstack.updateMember({
customFields: {
"last-ip": currentIpAddress
}
});
// Optional: Uncomment the line below to log a message when the IP is updated
// console.log('IP address updated');
} else {
// Optional: Uncomment the line below to log when the IP remains unchanged
// console.log('IP address unchanged, no update needed');
}
} catch (error) {
// Log any errors encountered during the fetch or update process
console.error('Error checking or updating member IP:', error);
}
} else {
// Optional: Uncomment the line below to log when no member is logged in
// console.log('No member is currently logged in');
}
});
</script>
<!-- 💙 MEMBERSCRIPT #118 v0.1 💙 - SAVE LAST IP ADDRESS -->
<script>
const memberstack = window.$memberstackDom;
memberstack.getCurrentMember().then(async (response) => {
if (response && response.data) {
const member = response.data;
try {
// Fetch the current IP address from the ipify API
const ipResponse = await fetch('https://api.ipify.org?format=json');
const ipData = await ipResponse.json();
const currentIpAddress = ipData.ip;
// Retrieve the stored IP address from Memberstack's custom fields
const storedIpAddress = member.customFields["last-ip"];
// Check if the IP address has changed and update if necessary
if (currentIpAddress !== storedIpAddress) {
await memberstack.updateMember({
customFields: {
"last-ip": currentIpAddress
}
});
// Optional: Uncomment the line below to log a message when the IP is updated
// console.log('IP address updated');
} else {
// Optional: Uncomment the line below to log when the IP remains unchanged
// console.log('IP address unchanged, no update needed');
}
} catch (error) {
// Log any errors encountered during the fetch or update process
console.error('Error checking or updating member IP:', error);
}
} else {
// Optional: Uncomment the line below to log when no member is logged in
// console.log('No member is currently logged in');
}
});
</script>

#Nr. 117 - Fortschrittsbalken für das Seitenblättern
Eine flexible und benutzerdefinierte Anzeige für das Blättern der Seite, um den Fortschritt des Blätterns anzuzeigen.
<!-- 💙 MEMBERSCRIPT #117 v0.1 💙 - PAGE SCROLL PROGRESS BAR -->
<script>
// Function to update the progress bar
function updateProgressBar() {
const container = document.querySelector('[ms-code-ps="container"]');
const bar = document.querySelector('[ms-code-ps="bar"]');
const startElement = document.querySelector('[ms-code-ps="start"]');
const endElement = document.querySelector('[ms-code-ps="end"]');
if (!container || !bar) return;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
let startPosition = 0;
let endPosition = documentHeight - windowHeight;
if (startElement) {
const startRect = startElement.getBoundingClientRect();
startPosition = scrollTop + startRect.top - windowHeight;
}
if (endElement) {
const endRect = endElement.getBoundingClientRect();
endPosition = scrollTop + endRect.top - windowHeight;
}
const scrollRange = endPosition - startPosition;
const scrollProgress = scrollTop - startPosition;
const scrollPercentage = Math.max(0, Math.min(100, (scrollProgress / scrollRange) * 100));
// Use requestAnimationFrame for smooth animation
requestAnimationFrame(() => {
bar.style.width = `${scrollPercentage}%`;
bar.style.transition = 'width 0.1s linear';
});
}
// Throttle function to limit how often updateProgressBar is called
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Add scroll event listener with throttling
window.addEventListener('scroll', throttle(updateProgressBar, 10));
// Initial call to set the correct width on page load
updateProgressBar();
</script>
<!-- 💙 MEMBERSCRIPT #117 v0.1 💙 - PAGE SCROLL PROGRESS BAR -->
<script>
// Function to update the progress bar
function updateProgressBar() {
const container = document.querySelector('[ms-code-ps="container"]');
const bar = document.querySelector('[ms-code-ps="bar"]');
const startElement = document.querySelector('[ms-code-ps="start"]');
const endElement = document.querySelector('[ms-code-ps="end"]');
if (!container || !bar) return;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
let startPosition = 0;
let endPosition = documentHeight - windowHeight;
if (startElement) {
const startRect = startElement.getBoundingClientRect();
startPosition = scrollTop + startRect.top - windowHeight;
}
if (endElement) {
const endRect = endElement.getBoundingClientRect();
endPosition = scrollTop + endRect.top - windowHeight;
}
const scrollRange = endPosition - startPosition;
const scrollProgress = scrollTop - startPosition;
const scrollPercentage = Math.max(0, Math.min(100, (scrollProgress / scrollRange) * 100));
// Use requestAnimationFrame for smooth animation
requestAnimationFrame(() => {
bar.style.width = `${scrollPercentage}%`;
bar.style.transition = 'width 0.1s linear';
});
}
// Throttle function to limit how often updateProgressBar is called
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Add scroll event listener with throttling
window.addEventListener('scroll', throttle(updateProgressBar, 10));
// Initial call to set the correct width on page load
updateProgressBar();
</script>

#116 - Hervorgehobene Textlinks teilen
Erlauben Sie den Benutzern, Text zu markieren und den Link mit anderen zu teilen!
<!-- 💙 MEMBERSCRIPT #116 v0.1 💙 - SHARE HIGHLIGHTED TEXT LINKS -->
<script>
// Function to encode text and position for URL
function encodeSelection(text, nodeIndex, textOffset) {
return btoa(encodeURIComponent(JSON.stringify({ text, nodeIndex, textOffset })));
}
// Function to decode selection from URL
function decodeSelection(encoded) {
try {
return JSON.parse(decodeURIComponent(atob(encoded)));
} catch (e) {
// If parsing fails, assume it's just the text in the old format
return { text: decodeURIComponent(atob(encoded)) };
}
}
// Function to remove existing highlight
function removeExistingHighlight() {
const existingHighlight = document.querySelector('.ms-highlight');
if (existingHighlight) {
const parent = existingHighlight.parentNode;
parent.replaceChild(document.createTextNode(existingHighlight.textContent), existingHighlight);
parent.normalize(); // Merge adjacent text nodes
}
}
// Function to handle text selection
function handleSelection() {
const selection = window.getSelection();
if (selection.toString().length > 0) {
removeExistingHighlight();
const range = selection.getRangeAt(0);
const selectedText = selection.toString();
const textNodes = getAllTextNodes(document.body);
const nodeIndex = textNodes.indexOf(range.startContainer);
const textOffset = range.startOffset;
// Create a unique identifier for the selection
const selectionId = encodeSelection(selectedText, nodeIndex, textOffset);
// Update URL with the selection parameter
const url = new URL(window.location);
url.searchParams.set('highlight', selectionId);
window.history.pushState({}, '', url);
// Highlight the selected text
highlightText(selectionId, range);
}
}
// Function to highlight text
function highlightText(selectionId, range) {
const span = document.createElement('span');
span.className = 'ms-highlight';
span.id = selectionId;
range.surroundContents(span);
}
// Function to highlight and scroll to text based on URL parameter
function highlightFromURL() {
removeExistingHighlight();
const url = new URL(window.location);
const highlightId = url.searchParams.get('highlight');
if (highlightId) {
const { text, nodeIndex, textOffset } = decodeSelection(highlightId);
const textNodes = getAllTextNodes(document.body);
if (nodeIndex !== undefined && textOffset !== undefined) {
// Use precise location if available
if (nodeIndex < textNodes.length) {
const node = textNodes[nodeIndex];
if (node.textContent.substr(textOffset, text.length) === text) {
const range = document.createRange();
range.setStart(node, textOffset);
range.setEnd(node, textOffset + text.length);
highlightText(highlightId, range);
}
}
} else {
// Fall back to searching for the first occurrence of the text
for (let node of textNodes) {
const index = node.textContent.indexOf(text);
if (index !== -1) {
const range = document.createRange();
range.setStart(node, index);
range.setEnd(node, index + text.length);
highlightText(highlightId, range);
break;
}
}
}
const highlightedSpan = document.getElementById(highlightId);
if (highlightedSpan) {
highlightedSpan.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
// Helper function to get all text nodes
function getAllTextNodes(element) {
const textNodes = [];
const walk = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
let node;
while (node = walk.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
// Add event listener for text selection
document.addEventListener('mouseup', handleSelection);
// Call highlightFromURL when the page loads
window.addEventListener('load', highlightFromURL);
</script>
<!-- 💙 MEMBERSCRIPT #116 v0.1 💙 - SHARE HIGHLIGHTED TEXT LINKS -->
<script>
// Function to encode text and position for URL
function encodeSelection(text, nodeIndex, textOffset) {
return btoa(encodeURIComponent(JSON.stringify({ text, nodeIndex, textOffset })));
}
// Function to decode selection from URL
function decodeSelection(encoded) {
try {
return JSON.parse(decodeURIComponent(atob(encoded)));
} catch (e) {
// If parsing fails, assume it's just the text in the old format
return { text: decodeURIComponent(atob(encoded)) };
}
}
// Function to remove existing highlight
function removeExistingHighlight() {
const existingHighlight = document.querySelector('.ms-highlight');
if (existingHighlight) {
const parent = existingHighlight.parentNode;
parent.replaceChild(document.createTextNode(existingHighlight.textContent), existingHighlight);
parent.normalize(); // Merge adjacent text nodes
}
}
// Function to handle text selection
function handleSelection() {
const selection = window.getSelection();
if (selection.toString().length > 0) {
removeExistingHighlight();
const range = selection.getRangeAt(0);
const selectedText = selection.toString();
const textNodes = getAllTextNodes(document.body);
const nodeIndex = textNodes.indexOf(range.startContainer);
const textOffset = range.startOffset;
// Create a unique identifier for the selection
const selectionId = encodeSelection(selectedText, nodeIndex, textOffset);
// Update URL with the selection parameter
const url = new URL(window.location);
url.searchParams.set('highlight', selectionId);
window.history.pushState({}, '', url);
// Highlight the selected text
highlightText(selectionId, range);
}
}
// Function to highlight text
function highlightText(selectionId, range) {
const span = document.createElement('span');
span.className = 'ms-highlight';
span.id = selectionId;
range.surroundContents(span);
}
// Function to highlight and scroll to text based on URL parameter
function highlightFromURL() {
removeExistingHighlight();
const url = new URL(window.location);
const highlightId = url.searchParams.get('highlight');
if (highlightId) {
const { text, nodeIndex, textOffset } = decodeSelection(highlightId);
const textNodes = getAllTextNodes(document.body);
if (nodeIndex !== undefined && textOffset !== undefined) {
// Use precise location if available
if (nodeIndex < textNodes.length) {
const node = textNodes[nodeIndex];
if (node.textContent.substr(textOffset, text.length) === text) {
const range = document.createRange();
range.setStart(node, textOffset);
range.setEnd(node, textOffset + text.length);
highlightText(highlightId, range);
}
}
} else {
// Fall back to searching for the first occurrence of the text
for (let node of textNodes) {
const index = node.textContent.indexOf(text);
if (index !== -1) {
const range = document.createRange();
range.setStart(node, index);
range.setEnd(node, index + text.length);
highlightText(highlightId, range);
break;
}
}
}
const highlightedSpan = document.getElementById(highlightId);
if (highlightedSpan) {
highlightedSpan.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
// Helper function to get all text nodes
function getAllTextNodes(element) {
const textNodes = [];
const walk = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
let node;
while (node = walk.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
// Add event listener for text selection
document.addEventListener('mouseup', handleSelection);
// Call highlightFromURL when the page loads
window.addEventListener('load', highlightFromURL);
</script>

#Nr. 115 - Ein zufälliges Passwort generieren
Reibungslose Anmeldung. Verlangt oder erlaubt den Mitgliedern, in Zukunft ein Passwort festzulegen.
<!-- 💙 MEMBERSCRIPT #115 v0.1 💙 - GENERATE PASSWORD-->
<script>
document.addEventListener('DOMContentLoaded', function() {
var passwordInput = document.querySelector('[data-ms-member="password"]');
if (passwordInput) {
// Function to generate random password
function generatePassword() {
var timestamp = Date.now().toString(36);
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+{}[]|:;<>,.?/~';
var randomChars = '';
for (var i = 0; i < 16; i++) {
randomChars += characters.charAt(Math.floor(Math.random() * characters.length));
}
return (timestamp + randomChars).slice(0, 32);
}
// Generate and set password
passwordInput.value = generatePassword();
// Block password managers and prevent editing
passwordInput.setAttribute('autocomplete', 'off');
passwordInput.setAttribute('readonly', 'readonly');
// Prevent copy and paste
passwordInput.addEventListener('copy', function(e) {
e.preventDefault();
});
passwordInput.addEventListener('paste', function(e) {
e.preventDefault();
});
// Prevent dragging
passwordInput.addEventListener('dragstart', function(e) {
e.preventDefault();
});
// Prevent context menu
passwordInput.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
}
});
</script>
<!-- 💙 MEMBERSCRIPT #115 v0.1 💙 - GENERATE PASSWORD-->
<script>
document.addEventListener('DOMContentLoaded', function() {
var passwordInput = document.querySelector('[data-ms-member="password"]');
if (passwordInput) {
// Function to generate random password
function generatePassword() {
var timestamp = Date.now().toString(36);
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+{}[]|:;<>,.?/~';
var randomChars = '';
for (var i = 0; i < 16; i++) {
randomChars += characters.charAt(Math.floor(Math.random() * characters.length));
}
return (timestamp + randomChars).slice(0, 32);
}
// Generate and set password
passwordInput.value = generatePassword();
// Block password managers and prevent editing
passwordInput.setAttribute('autocomplete', 'off');
passwordInput.setAttribute('readonly', 'readonly');
// Prevent copy and paste
passwordInput.addEventListener('copy', function(e) {
e.preventDefault();
});
passwordInput.addEventListener('paste', function(e) {
e.preventDefault();
});
// Prevent dragging
passwordInput.addEventListener('dragstart', function(e) {
e.preventDefault();
});
// Prevent context menu
passwordInput.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
}
});
</script>

#Nr. 114 - Schaltfläche "Nach oben scrollen
Fügen Sie eine Schaltfläche hinzu, die beim Anklicken an den Anfang der Seite blättert,
<!-- 💙 MEMBERSCRIPT #114 v0.1 💙 - SCROLL TO TOP BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var scrollTopButton = document.querySelector('[ms-code-scroll-top="button"]');
if (scrollTopButton) {
// Set initial styles
scrollTopButton.style.opacity = '0';
scrollTopButton.style.visibility = 'hidden';
scrollTopButton.style.transition = 'opacity 0.3s, visibility 0.3s';
// Function to check scroll position and toggle button visibility
function toggleButtonVisibility() {
if (window.pageYOffset > 300) {
scrollTopButton.style.opacity = '1';
scrollTopButton.style.visibility = 'visible';
} else {
scrollTopButton.style.opacity = '0';
scrollTopButton.style.visibility = 'hidden';
}
}
// Initial check on page load
toggleButtonVisibility();
// Check on scroll
window.addEventListener('scroll', toggleButtonVisibility);
// Scroll to top when button is clicked
scrollTopButton.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
});
</script>
<!-- 💙 MEMBERSCRIPT #114 v0.1 💙 - SCROLL TO TOP BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var scrollTopButton = document.querySelector('[ms-code-scroll-top="button"]');
if (scrollTopButton) {
// Set initial styles
scrollTopButton.style.opacity = '0';
scrollTopButton.style.visibility = 'hidden';
scrollTopButton.style.transition = 'opacity 0.3s, visibility 0.3s';
// Function to check scroll position and toggle button visibility
function toggleButtonVisibility() {
if (window.pageYOffset > 300) {
scrollTopButton.style.opacity = '1';
scrollTopButton.style.visibility = 'visible';
} else {
scrollTopButton.style.opacity = '0';
scrollTopButton.style.visibility = 'hidden';
}
}
// Initial check on page load
toggleButtonVisibility();
// Check on scroll
window.addEventListener('scroll', toggleButtonVisibility);
// Scroll to top when button is clicked
scrollTopButton.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
});
</script>

#113 - RSS-Feeds
Verwenden Sie eine Webflow-Benutzeroberfläche, um einen RSS-Feed direkt auf Ihrer Website anzuzeigen.
<!-- 💙 MEMBERSCRIPT #113 v0.2 💙 - RSS FEEDS IN WEBFLOW -->
<script>
(function() {
// console.log('RSS Feed Script starting...');
const CORS_PROXIES = [
'https://corsproxy.io/?',
'https://api.allorigins.win/raw?url=',
'https://cors-anywhere.herokuapp.com/',
'https://thingproxy.freeboard.io/fetch/',
'https://yacdn.org/proxy/'
];
function loadScript(src, onLoad, onError) {
const script = document.createElement('script');
script.src = src;
script.onload = onLoad;
script.onerror = onError;
document.head.appendChild(script);
}
async function fetchWithFallback(url) {
for (const proxy of CORS_PROXIES) {
try {
const response = await fetch(proxy + encodeURIComponent(url));
if (response.ok) {
return await response.text();
}
} catch (error) {
console.warn(`Failed to fetch with proxy ${proxy}:`, error);
}
}
throw new Error('All CORS proxies failed');
}
function initRSSFeed() {
if (typeof RSSParser === 'undefined') {
console.error('RSSParser is not defined.');
return;
}
const parser = new RSSParser({
customFields: {
item: [
['media:content', 'mediaContent', {keepArray: true}],
['media:thumbnail', 'mediaThumbnail', {keepArray: true}],
['enclosure', 'enclosure', {keepArray: true}],
]
}
});
document.querySelectorAll('[ms-code-rss-feed]').forEach(element => {
const url = element.getAttribute('ms-code-rss-url');
const limit = parseInt(element.getAttribute('ms-code-rss-limit')) || 5;
fetchWithFallback(url)
.then(str => parser.parseString(str))
.then(feed => {
renderRSSItems(element, feed.items.slice(0, limit), {
showImage: element.getAttribute('ms-code-rss-show-image') !== 'false',
showDate: element.getAttribute('ms-code-rss-show-date') !== 'false',
dateFormat: element.getAttribute('ms-code-rss-date-format') || 'short',
target: element.getAttribute('ms-code-rss-target') || '_self'
});
})
.catch(err => {
console.error('Error fetching or parsing RSS feed:', err);
element.textContent = `Failed to load RSS feed from ${url}. Error: ${err.message}`;
});
});
}
function renderRSSItems(element, items, options) {
const templateItem = element.querySelector('[ms-code-rss-item]');
if (!templateItem) return;
element.innerHTML = ''; // Clear existing items
items.forEach(item => {
const itemElement = templateItem.cloneNode(true);
const title = itemElement.querySelector('[ms-code-rss-title]');
if (title) {
const titleLength = parseInt(title.getAttribute('ms-code-rss-title-length')) || Infinity;
title.textContent = truncate(item.title, titleLength);
}
const description = itemElement.querySelector('[ms-code-rss-description]');
if (description) {
const descriptionLength = parseInt(description.getAttribute('ms-code-rss-description-length')) || Infinity;
description.textContent = truncate(stripHtml(item.content || item.description), descriptionLength);
}
const date = itemElement.querySelector('[ms-code-rss-date]');
if (date && options.showDate && item.pubDate) {
date.textContent = formatDate(new Date(item.pubDate), options.dateFormat);
}
const img = itemElement.querySelector('[ms-code-rss-image]');
if (img && options.showImage) {
const imgUrl = getImageUrl(item);
if (imgUrl) {
img.src = imgUrl;
img.alt = item.title;
img.removeAttribute('srcset');
}
}
const linkElement = itemElement.querySelector('[ms-code-rss-link]');
if (linkElement) {
linkElement.setAttribute('href', item.link);
linkElement.setAttribute('target', options.target);
}
element.appendChild(itemElement);
});
}
function getImageUrl(item) {
const sources = ['mediaContent', 'mediaThumbnail', 'enclosure'];
for (let source of sources) {
if (item[source] && item[source][0]) {
return item[source][0].$ ? item[source][0].$.url : item[source][0].url;
}
}
return null;
}
function truncate(str, length) {
if (!str) return '';
if (length === Infinity) return str;
return str.length > length ? str.slice(0, length) + '...' : str;
}
function stripHtml(html) {
const tmp = document.createElement('DIV');
tmp.innerHTML = html || '';
return tmp.textContent || tmp.innerText || '';
}
function formatDate(date, format) {
if (!(date instanceof Date) || isNaN(date)) return '';
const options = format === 'long' ?
{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' } :
undefined;
return format === 'relative' ? getRelativeTimeString(date) : date.toLocaleDateString(undefined, options);
}
function getRelativeTimeString(date, lang = navigator.language) {
const timeMs = date.getTime();
const deltaSeconds = Math.round((timeMs - Date.now()) / 1000);
const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
const units = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'];
const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(deltaSeconds));
const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
const rtf = new Intl.RelativeTimeFormat(lang, { numeric: 'auto' });
return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex]);
}
loadScript('https://cdn.jsdelivr.net/npm/rss-parser@3.12.0/dist/rss-parser.min.js', initRSSFeed, () => {
console.error('Error loading RSS Parser script');
loadScript('https://unpkg.com/rss-parser@3.12.0/dist/rss-parser.min.js', initRSSFeed, () => {
console.error('Error loading RSS Parser script from backup CDN');
});
});
})();
</script>
<!-- 💙 MEMBERSCRIPT #113 v0.2 💙 - RSS FEEDS IN WEBFLOW -->
<script>
(function() {
// console.log('RSS Feed Script starting...');
const CORS_PROXIES = [
'https://corsproxy.io/?',
'https://api.allorigins.win/raw?url=',
'https://cors-anywhere.herokuapp.com/',
'https://thingproxy.freeboard.io/fetch/',
'https://yacdn.org/proxy/'
];
function loadScript(src, onLoad, onError) {
const script = document.createElement('script');
script.src = src;
script.onload = onLoad;
script.onerror = onError;
document.head.appendChild(script);
}
async function fetchWithFallback(url) {
for (const proxy of CORS_PROXIES) {
try {
const response = await fetch(proxy + encodeURIComponent(url));
if (response.ok) {
return await response.text();
}
} catch (error) {
console.warn(`Failed to fetch with proxy ${proxy}:`, error);
}
}
throw new Error('All CORS proxies failed');
}
function initRSSFeed() {
if (typeof RSSParser === 'undefined') {
console.error('RSSParser is not defined.');
return;
}
const parser = new RSSParser({
customFields: {
item: [
['media:content', 'mediaContent', {keepArray: true}],
['media:thumbnail', 'mediaThumbnail', {keepArray: true}],
['enclosure', 'enclosure', {keepArray: true}],
]
}
});
document.querySelectorAll('[ms-code-rss-feed]').forEach(element => {
const url = element.getAttribute('ms-code-rss-url');
const limit = parseInt(element.getAttribute('ms-code-rss-limit')) || 5;
fetchWithFallback(url)
.then(str => parser.parseString(str))
.then(feed => {
renderRSSItems(element, feed.items.slice(0, limit), {
showImage: element.getAttribute('ms-code-rss-show-image') !== 'false',
showDate: element.getAttribute('ms-code-rss-show-date') !== 'false',
dateFormat: element.getAttribute('ms-code-rss-date-format') || 'short',
target: element.getAttribute('ms-code-rss-target') || '_self'
});
})
.catch(err => {
console.error('Error fetching or parsing RSS feed:', err);
element.textContent = `Failed to load RSS feed from ${url}. Error: ${err.message}`;
});
});
}
function renderRSSItems(element, items, options) {
const templateItem = element.querySelector('[ms-code-rss-item]');
if (!templateItem) return;
element.innerHTML = ''; // Clear existing items
items.forEach(item => {
const itemElement = templateItem.cloneNode(true);
const title = itemElement.querySelector('[ms-code-rss-title]');
if (title) {
const titleLength = parseInt(title.getAttribute('ms-code-rss-title-length')) || Infinity;
title.textContent = truncate(item.title, titleLength);
}
const description = itemElement.querySelector('[ms-code-rss-description]');
if (description) {
const descriptionLength = parseInt(description.getAttribute('ms-code-rss-description-length')) || Infinity;
description.textContent = truncate(stripHtml(item.content || item.description), descriptionLength);
}
const date = itemElement.querySelector('[ms-code-rss-date]');
if (date && options.showDate && item.pubDate) {
date.textContent = formatDate(new Date(item.pubDate), options.dateFormat);
}
const img = itemElement.querySelector('[ms-code-rss-image]');
if (img && options.showImage) {
const imgUrl = getImageUrl(item);
if (imgUrl) {
img.src = imgUrl;
img.alt = item.title;
img.removeAttribute('srcset');
}
}
const linkElement = itemElement.querySelector('[ms-code-rss-link]');
if (linkElement) {
linkElement.setAttribute('href', item.link);
linkElement.setAttribute('target', options.target);
}
element.appendChild(itemElement);
});
}
function getImageUrl(item) {
const sources = ['mediaContent', 'mediaThumbnail', 'enclosure'];
for (let source of sources) {
if (item[source] && item[source][0]) {
return item[source][0].$ ? item[source][0].$.url : item[source][0].url;
}
}
return null;
}
function truncate(str, length) {
if (!str) return '';
if (length === Infinity) return str;
return str.length > length ? str.slice(0, length) + '...' : str;
}
function stripHtml(html) {
const tmp = document.createElement('DIV');
tmp.innerHTML = html || '';
return tmp.textContent || tmp.innerText || '';
}
function formatDate(date, format) {
if (!(date instanceof Date) || isNaN(date)) return '';
const options = format === 'long' ?
{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' } :
undefined;
return format === 'relative' ? getRelativeTimeString(date) : date.toLocaleDateString(undefined, options);
}
function getRelativeTimeString(date, lang = navigator.language) {
const timeMs = date.getTime();
const deltaSeconds = Math.round((timeMs - Date.now()) / 1000);
const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
const units = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'];
const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(deltaSeconds));
const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
const rtf = new Intl.RelativeTimeFormat(lang, { numeric: 'auto' });
return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex]);
}
loadScript('https://cdn.jsdelivr.net/npm/rss-parser@3.12.0/dist/rss-parser.min.js', initRSSFeed, () => {
console.error('Error loading RSS Parser script');
loadScript('https://unpkg.com/rss-parser@3.12.0/dist/rss-parser.min.js', initRSSFeed, () => {
console.error('Error loading RSS Parser script from backup CDN');
});
});
})();
</script>

#Nr. 112 - Vorher & Nachher Schieberegler
Fügen Sie Ihrer Webflow-Website ganz einfach einen Vorher/Nachher-Fotoslider hinzu.
<!-- 💙 MEMBERSCRIPT #112 v0.1 💙 - BEFORE & AFTER SLIDERS -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const wraps = document.querySelectorAll('[ms-code-ba-wrap]');
wraps.forEach(wrap => {
const before = wrap.querySelector('[ms-code-ba-before]');
const after = wrap.querySelector('[ms-code-ba-after]');
// Create slider element
const slider = document.createElement('div');
slider.setAttribute('ms-code-ba-slider', wrap.getAttribute('ms-code-ba-wrap'));
wrap.appendChild(slider);
let isDown = false;
// Ensure proper positioning
wrap.style.position = 'relative';
wrap.style.overflow = 'hidden';
before.style.width = '100%';
before.style.display = 'block';
after.style.position = 'absolute';
after.style.top = '0';
after.style.left = '0';
after.style.width = '100%';
after.style.height = '100%';
slider.style.position = 'absolute';
slider.style.top = '0';
slider.style.bottom = '0';
slider.style.width = '4px';
slider.style.background = 'white';
slider.style.cursor = 'ew-resize';
slider.style.zIndex = '3';
const setPosition = (position) => {
const clampedPosition = Math.max(0, Math.min(1, position));
slider.style.left = `${clampedPosition * 100}%`;
after.style.clipPath = `inset(0 0 0 ${clampedPosition * 100}%)`;
};
const move = (e) => {
if (!isDown && e.type !== 'mousemove') return;
e.preventDefault();
const x = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
const rect = wrap.getBoundingClientRect();
const position = (x - rect.left) / rect.width;
setPosition(position);
};
const easeBack = () => {
setPosition(0.5); // Move back to center
};
wrap.addEventListener('mousedown', () => isDown = true);
wrap.addEventListener('mouseup', () => isDown = false);
wrap.addEventListener('mouseleave', () => {
isDown = false;
easeBack();
});
wrap.addEventListener('mousemove', move);
wrap.addEventListener('touchstart', (e) => {
isDown = true;
move(e);
});
wrap.addEventListener('touchmove', move);
wrap.addEventListener('touchend', () => {
isDown = false;
easeBack();
});
// Initialize position
setPosition(0.5);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #112 v0.1 💙 - BEFORE & AFTER SLIDERS -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const wraps = document.querySelectorAll('[ms-code-ba-wrap]');
wraps.forEach(wrap => {
const before = wrap.querySelector('[ms-code-ba-before]');
const after = wrap.querySelector('[ms-code-ba-after]');
// Create slider element
const slider = document.createElement('div');
slider.setAttribute('ms-code-ba-slider', wrap.getAttribute('ms-code-ba-wrap'));
wrap.appendChild(slider);
let isDown = false;
// Ensure proper positioning
wrap.style.position = 'relative';
wrap.style.overflow = 'hidden';
before.style.width = '100%';
before.style.display = 'block';
after.style.position = 'absolute';
after.style.top = '0';
after.style.left = '0';
after.style.width = '100%';
after.style.height = '100%';
slider.style.position = 'absolute';
slider.style.top = '0';
slider.style.bottom = '0';
slider.style.width = '4px';
slider.style.background = 'white';
slider.style.cursor = 'ew-resize';
slider.style.zIndex = '3';
const setPosition = (position) => {
const clampedPosition = Math.max(0, Math.min(1, position));
slider.style.left = `${clampedPosition * 100}%`;
after.style.clipPath = `inset(0 0 0 ${clampedPosition * 100}%)`;
};
const move = (e) => {
if (!isDown && e.type !== 'mousemove') return;
e.preventDefault();
const x = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
const rect = wrap.getBoundingClientRect();
const position = (x - rect.left) / rect.width;
setPosition(position);
};
const easeBack = () => {
setPosition(0.5); // Move back to center
};
wrap.addEventListener('mousedown', () => isDown = true);
wrap.addEventListener('mouseup', () => isDown = false);
wrap.addEventListener('mouseleave', () => {
isDown = false;
easeBack();
});
wrap.addEventListener('mousemove', move);
wrap.addEventListener('touchstart', (e) => {
isDown = true;
move(e);
});
wrap.addEventListener('touchmove', move);
wrap.addEventListener('touchend', () => {
isDown = false;
easeBack();
});
// Initialize position
setPosition(0.5);
});
});
</script>

#Nr. 111 - Automatisch rotierende Registerkarten
Der einfachste Weg, um Ihre Tabs mit einem Timer automatisch rotieren zu lassen.
<!-- 💙 MEMBERSCRIPT #111 v0.1 💙 - AUTO-ROTATING TABS -->
<script>
// Function to rotate tabs
function initializeTabRotator() {
// Find all tab containers with the ms-code-rotate-tabs attribute
const tabContainers = document.querySelectorAll('[ms-code-rotate-tabs]');
tabContainers.forEach(container => {
const interval = parseInt(container.getAttribute('ms-code-rotate-tabs'), 10);
const tabLinks = container.querySelectorAll('.w-tab-link');
const tabContent = container.closest('.w-tabs').querySelector('.w-tab-content');
const tabPanes = tabContent.querySelectorAll('.w-tab-pane');
let currentIndex = Array.from(tabLinks).findIndex(link => link.classList.contains('w--current'));
let rotationTimer;
// ANIMATION CONFIGURATION
// Modify these values to adjust the animation behavior
const FADE_OUT_DURATION = 300; // Duration for fading out the current tab (in milliseconds)
const FADE_IN_DURATION = 100; // Duration for fading in the new tab (in milliseconds)
const EASING_FUNCTION = 'ease'; // Choose from: 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'
// or use a cubic-bezier function like 'cubic-bezier(0.1, 0.7, 1.0, 0.1)'
// Additional easing options (uncomment to use):
// const EASING_FUNCTION = 'ease-in-quad';
// const EASING_FUNCTION = 'ease-out-quad';
// const EASING_FUNCTION = 'ease-in-out-quad';
// const EASING_FUNCTION = 'ease-in-cubic';
// const EASING_FUNCTION = 'ease-out-cubic';
// const EASING_FUNCTION = 'ease-in-out-cubic';
// const EASING_FUNCTION = 'ease-in-quart';
// const EASING_FUNCTION = 'ease-out-quart';
// const EASING_FUNCTION = 'ease-in-out-quart';
// const EASING_FUNCTION = 'ease-in-quint';
// const EASING_FUNCTION = 'ease-out-quint';
// const EASING_FUNCTION = 'ease-in-out-quint';
// const EASING_FUNCTION = 'ease-in-sine';
// const EASING_FUNCTION = 'ease-out-sine';
// const EASING_FUNCTION = 'ease-in-out-sine';
// const EASING_FUNCTION = 'ease-in-expo';
// const EASING_FUNCTION = 'ease-out-expo';
// const EASING_FUNCTION = 'ease-in-out-expo';
// const EASING_FUNCTION = 'ease-in-circ';
// const EASING_FUNCTION = 'ease-out-circ';
// const EASING_FUNCTION = 'ease-in-out-circ';
// const EASING_FUNCTION = 'ease-in-back';
// const EASING_FUNCTION = 'ease-out-back';
// const EASING_FUNCTION = 'ease-in-out-back';
// END OF ANIMATION CONFIGURATION
function switchToTab(index) {
// Fade out current tab
tabPanes[currentIndex].style.transition = `opacity ${FADE_OUT_DURATION}ms ${EASING_FUNCTION}`;
tabPanes[currentIndex].style.opacity = '0';
setTimeout(() => {
// Remove active classes and update ARIA attributes for current tab and pane
tabLinks[currentIndex].classList.remove('w--current');
tabLinks[currentIndex].setAttribute('aria-selected', 'false');
tabLinks[currentIndex].setAttribute('tabindex', '-1');
tabPanes[currentIndex].classList.remove('w--tab-active');
// Update current index
currentIndex = index;
// Add active classes and update ARIA attributes for new current tab and pane
tabLinks[currentIndex].classList.add('w--current');
tabLinks[currentIndex].setAttribute('aria-selected', 'true');
tabLinks[currentIndex].setAttribute('tabindex', '0');
tabPanes[currentIndex].classList.add('w--tab-active');
// Fade in new tab
tabPanes[currentIndex].style.transition = `opacity ${FADE_IN_DURATION}ms ${EASING_FUNCTION}`;
tabPanes[currentIndex].style.opacity = '1';
// Update the data-current attribute on the parent w-tabs element
const wTabsElement = container.closest('.w-tabs');
if (wTabsElement) {
wTabsElement.setAttribute('data-current', tabLinks[currentIndex].getAttribute('data-w-tab'));
}
}, FADE_OUT_DURATION);
}
function rotateToNextTab() {
const nextIndex = (currentIndex + 1) % tabLinks.length;
switchToTab(nextIndex);
}
function startRotation() {
clearInterval(rotationTimer);
rotationTimer = setInterval(rotateToNextTab, interval);
}
// Add click event listeners to tab links
tabLinks.forEach((link, index) => {
link.addEventListener('click', (e) => {
e.preventDefault();
switchToTab(index);
startRotation(); // Restart rotation from this tab
});
});
// Start the initial rotation
startRotation();
});
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initializeTabRotator);
</script>
<!-- 💙 MEMBERSCRIPT #111 v0.1 💙 - AUTO-ROTATING TABS -->
<script>
// Function to rotate tabs
function initializeTabRotator() {
// Find all tab containers with the ms-code-rotate-tabs attribute
const tabContainers = document.querySelectorAll('[ms-code-rotate-tabs]');
tabContainers.forEach(container => {
const interval = parseInt(container.getAttribute('ms-code-rotate-tabs'), 10);
const tabLinks = container.querySelectorAll('.w-tab-link');
const tabContent = container.closest('.w-tabs').querySelector('.w-tab-content');
const tabPanes = tabContent.querySelectorAll('.w-tab-pane');
let currentIndex = Array.from(tabLinks).findIndex(link => link.classList.contains('w--current'));
let rotationTimer;
// ANIMATION CONFIGURATION
// Modify these values to adjust the animation behavior
const FADE_OUT_DURATION = 300; // Duration for fading out the current tab (in milliseconds)
const FADE_IN_DURATION = 100; // Duration for fading in the new tab (in milliseconds)
const EASING_FUNCTION = 'ease'; // Choose from: 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'
// or use a cubic-bezier function like 'cubic-bezier(0.1, 0.7, 1.0, 0.1)'
// Additional easing options (uncomment to use):
// const EASING_FUNCTION = 'ease-in-quad';
// const EASING_FUNCTION = 'ease-out-quad';
// const EASING_FUNCTION = 'ease-in-out-quad';
// const EASING_FUNCTION = 'ease-in-cubic';
// const EASING_FUNCTION = 'ease-out-cubic';
// const EASING_FUNCTION = 'ease-in-out-cubic';
// const EASING_FUNCTION = 'ease-in-quart';
// const EASING_FUNCTION = 'ease-out-quart';
// const EASING_FUNCTION = 'ease-in-out-quart';
// const EASING_FUNCTION = 'ease-in-quint';
// const EASING_FUNCTION = 'ease-out-quint';
// const EASING_FUNCTION = 'ease-in-out-quint';
// const EASING_FUNCTION = 'ease-in-sine';
// const EASING_FUNCTION = 'ease-out-sine';
// const EASING_FUNCTION = 'ease-in-out-sine';
// const EASING_FUNCTION = 'ease-in-expo';
// const EASING_FUNCTION = 'ease-out-expo';
// const EASING_FUNCTION = 'ease-in-out-expo';
// const EASING_FUNCTION = 'ease-in-circ';
// const EASING_FUNCTION = 'ease-out-circ';
// const EASING_FUNCTION = 'ease-in-out-circ';
// const EASING_FUNCTION = 'ease-in-back';
// const EASING_FUNCTION = 'ease-out-back';
// const EASING_FUNCTION = 'ease-in-out-back';
// END OF ANIMATION CONFIGURATION
function switchToTab(index) {
// Fade out current tab
tabPanes[currentIndex].style.transition = `opacity ${FADE_OUT_DURATION}ms ${EASING_FUNCTION}`;
tabPanes[currentIndex].style.opacity = '0';
setTimeout(() => {
// Remove active classes and update ARIA attributes for current tab and pane
tabLinks[currentIndex].classList.remove('w--current');
tabLinks[currentIndex].setAttribute('aria-selected', 'false');
tabLinks[currentIndex].setAttribute('tabindex', '-1');
tabPanes[currentIndex].classList.remove('w--tab-active');
// Update current index
currentIndex = index;
// Add active classes and update ARIA attributes for new current tab and pane
tabLinks[currentIndex].classList.add('w--current');
tabLinks[currentIndex].setAttribute('aria-selected', 'true');
tabLinks[currentIndex].setAttribute('tabindex', '0');
tabPanes[currentIndex].classList.add('w--tab-active');
// Fade in new tab
tabPanes[currentIndex].style.transition = `opacity ${FADE_IN_DURATION}ms ${EASING_FUNCTION}`;
tabPanes[currentIndex].style.opacity = '1';
// Update the data-current attribute on the parent w-tabs element
const wTabsElement = container.closest('.w-tabs');
if (wTabsElement) {
wTabsElement.setAttribute('data-current', tabLinks[currentIndex].getAttribute('data-w-tab'));
}
}, FADE_OUT_DURATION);
}
function rotateToNextTab() {
const nextIndex = (currentIndex + 1) % tabLinks.length;
switchToTab(nextIndex);
}
function startRotation() {
clearInterval(rotationTimer);
rotationTimer = setInterval(rotateToNextTab, interval);
}
// Add click event listeners to tab links
tabLinks.forEach((link, index) => {
link.addEventListener('click', (e) => {
e.preventDefault();
switchToTab(index);
startRotation(); // Restart rotation from this tab
});
});
// Start the initial rotation
startRotation();
});
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initializeTabRotator);
</script>

#Nr. 110 - QuickInfos für Webflow
Fügen Sie Ihrer Webflow-Website mit Attributen ganz einfach Tippy.js-Tooltips hinzu.
<!-- 💙 MEMBERSCRIPT #110 v0.1 💙 - TOOLTIPS FOR WEBFLOW -->
<script>
// Function to load Tippy.js, its CSS, and additional theme/animation CSS
function loadTippy(callback) {
// Load Tippy.js script
const script = document.createElement('script');
script.src = 'https://unpkg.com/@popperjs/core@2';
script.onload = function() {
const tippyScript = document.createElement('script');
tippyScript.src = 'https://unpkg.com/tippy.js@6';
tippyScript.onload = function() {
// Load Tippy.js CSS
const cssFiles = [
'https://unpkg.com/tippy.js@6/dist/tippy.css',
'https://unpkg.com/tippy.js@6/themes/light.css',
'https://unpkg.com/tippy.js@6/themes/light-border.css',
'https://unpkg.com/tippy.js@6/animations/shift-away.css',
'https://unpkg.com/tippy.js@6/animations/shift-toward.css',
'https://unpkg.com/tippy.js@6/animations/scale.css',
'https://unpkg.com/tippy.js@6/animations/perspective.css'
];
let loadedCount = 0;
cssFiles.forEach(file => {
const link = document.createElement('link');
link.href = file;
link.rel = 'stylesheet';
link.onload = function() {
loadedCount++;
if (loadedCount === cssFiles.length) {
// Call the callback function when everything is loaded
callback();
}
};
document.head.appendChild(link);
});
};
document.head.appendChild(tippyScript);
};
document.head.appendChild(script);
}
// Function to initialize Tippy tooltips
function initializeTippyTooltips() {
// Select all elements with any ms-code-tooltip-* attribute
const elements = document.querySelectorAll('[ms-code-tooltip-top], [ms-code-tooltip-bottom], [ms-code-tooltip-left], [ms-code-tooltip-right], [ms-code-tooltip-content]');
elements.forEach(element => {
const tippyOptions = {};
// Content and Placement
if (element.hasAttribute('ms-code-tooltip-top')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-top');
tippyOptions.placement = 'top';
} else if (element.hasAttribute('ms-code-tooltip-bottom')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-bottom');
tippyOptions.placement = 'bottom';
} else if (element.hasAttribute('ms-code-tooltip-left')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-left');
tippyOptions.placement = 'left';
} else if (element.hasAttribute('ms-code-tooltip-right')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-right');
tippyOptions.placement = 'right';
} else if (element.hasAttribute('ms-code-tooltip-content')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-content');
}
if (element.hasAttribute('ms-code-tooltip-placement')) {
tippyOptions.placement = element.getAttribute('ms-code-tooltip-placement');
}
// Theme
if (element.hasAttribute('ms-code-tooltip-theme')) {
tippyOptions.theme = element.getAttribute('ms-code-tooltip-theme');
}
// Animation
if (element.hasAttribute('ms-code-tooltip-animation')) {
tippyOptions.animation = element.getAttribute('ms-code-tooltip-animation');
}
// Max Width
if (element.hasAttribute('ms-code-tooltip-maxwidth')) {
tippyOptions.maxWidth = parseInt(element.getAttribute('ms-code-tooltip-maxwidth'));
}
// Delay
if (element.hasAttribute('ms-code-tooltip-delay')) {
tippyOptions.delay = JSON.parse(element.getAttribute('ms-code-tooltip-delay'));
}
// Duration
if (element.hasAttribute('ms-code-tooltip-duration')) {
tippyOptions.duration = JSON.parse(element.getAttribute('ms-code-tooltip-duration'));
}
// Interactive
if (element.hasAttribute('ms-code-tooltip-interactive')) {
tippyOptions.interactive = element.getAttribute('ms-code-tooltip-interactive') === 'true';
}
// Arrow
if (element.hasAttribute('ms-code-tooltip-arrow')) {
tippyOptions.arrow = element.getAttribute('ms-code-tooltip-arrow') === 'true';
}
// Trigger
if (element.hasAttribute('ms-code-tooltip-trigger')) {
tippyOptions.trigger = element.getAttribute('ms-code-tooltip-trigger');
}
// Hide On Click
if (element.hasAttribute('ms-code-tooltip-hideOnClick')) {
tippyOptions.hideOnClick = element.getAttribute('ms-code-tooltip-hideOnClick') === 'true';
}
// Follow Cursor
if (element.hasAttribute('ms-code-tooltip-followCursor')) {
tippyOptions.followCursor = element.getAttribute('ms-code-tooltip-followCursor');
}
// Offset
if (element.hasAttribute('ms-code-tooltip-offset')) {
tippyOptions.offset = JSON.parse(element.getAttribute('ms-code-tooltip-offset'));
}
// Z-Index
if (element.hasAttribute('ms-code-tooltip-zIndex')) {
tippyOptions.zIndex = parseInt(element.getAttribute('ms-code-tooltip-zIndex'));
}
// Allow HTML
if (element.hasAttribute('ms-code-tooltip-allowHTML')) {
tippyOptions.allowHTML = element.getAttribute('ms-code-tooltip-allowHTML') === 'true';
}
// Touch
if (element.hasAttribute('ms-code-tooltip-touch')) {
const touchValue = element.getAttribute('ms-code-tooltip-touch');
tippyOptions.touch = touchValue === 'true' || touchValue === 'false' ? (touchValue === 'true') : JSON.parse(touchValue);
}
// Initialize Tippy instance
tippy(element, tippyOptions);
});
}
// Wait for the DOM to be fully loaded, then load Tippy and initialize tooltips
document.addEventListener('DOMContentLoaded', function() {
loadTippy(initializeTippyTooltips);
});
</script>
<!-- 💙 MEMBERSCRIPT #110 v0.1 💙 - TOOLTIPS FOR WEBFLOW -->
<script>
// Function to load Tippy.js, its CSS, and additional theme/animation CSS
function loadTippy(callback) {
// Load Tippy.js script
const script = document.createElement('script');
script.src = 'https://unpkg.com/@popperjs/core@2';
script.onload = function() {
const tippyScript = document.createElement('script');
tippyScript.src = 'https://unpkg.com/tippy.js@6';
tippyScript.onload = function() {
// Load Tippy.js CSS
const cssFiles = [
'https://unpkg.com/tippy.js@6/dist/tippy.css',
'https://unpkg.com/tippy.js@6/themes/light.css',
'https://unpkg.com/tippy.js@6/themes/light-border.css',
'https://unpkg.com/tippy.js@6/animations/shift-away.css',
'https://unpkg.com/tippy.js@6/animations/shift-toward.css',
'https://unpkg.com/tippy.js@6/animations/scale.css',
'https://unpkg.com/tippy.js@6/animations/perspective.css'
];
let loadedCount = 0;
cssFiles.forEach(file => {
const link = document.createElement('link');
link.href = file;
link.rel = 'stylesheet';
link.onload = function() {
loadedCount++;
if (loadedCount === cssFiles.length) {
// Call the callback function when everything is loaded
callback();
}
};
document.head.appendChild(link);
});
};
document.head.appendChild(tippyScript);
};
document.head.appendChild(script);
}
// Function to initialize Tippy tooltips
function initializeTippyTooltips() {
// Select all elements with any ms-code-tooltip-* attribute
const elements = document.querySelectorAll('[ms-code-tooltip-top], [ms-code-tooltip-bottom], [ms-code-tooltip-left], [ms-code-tooltip-right], [ms-code-tooltip-content]');
elements.forEach(element => {
const tippyOptions = {};
// Content and Placement
if (element.hasAttribute('ms-code-tooltip-top')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-top');
tippyOptions.placement = 'top';
} else if (element.hasAttribute('ms-code-tooltip-bottom')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-bottom');
tippyOptions.placement = 'bottom';
} else if (element.hasAttribute('ms-code-tooltip-left')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-left');
tippyOptions.placement = 'left';
} else if (element.hasAttribute('ms-code-tooltip-right')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-right');
tippyOptions.placement = 'right';
} else if (element.hasAttribute('ms-code-tooltip-content')) {
tippyOptions.content = element.getAttribute('ms-code-tooltip-content');
}
if (element.hasAttribute('ms-code-tooltip-placement')) {
tippyOptions.placement = element.getAttribute('ms-code-tooltip-placement');
}
// Theme
if (element.hasAttribute('ms-code-tooltip-theme')) {
tippyOptions.theme = element.getAttribute('ms-code-tooltip-theme');
}
// Animation
if (element.hasAttribute('ms-code-tooltip-animation')) {
tippyOptions.animation = element.getAttribute('ms-code-tooltip-animation');
}
// Max Width
if (element.hasAttribute('ms-code-tooltip-maxwidth')) {
tippyOptions.maxWidth = parseInt(element.getAttribute('ms-code-tooltip-maxwidth'));
}
// Delay
if (element.hasAttribute('ms-code-tooltip-delay')) {
tippyOptions.delay = JSON.parse(element.getAttribute('ms-code-tooltip-delay'));
}
// Duration
if (element.hasAttribute('ms-code-tooltip-duration')) {
tippyOptions.duration = JSON.parse(element.getAttribute('ms-code-tooltip-duration'));
}
// Interactive
if (element.hasAttribute('ms-code-tooltip-interactive')) {
tippyOptions.interactive = element.getAttribute('ms-code-tooltip-interactive') === 'true';
}
// Arrow
if (element.hasAttribute('ms-code-tooltip-arrow')) {
tippyOptions.arrow = element.getAttribute('ms-code-tooltip-arrow') === 'true';
}
// Trigger
if (element.hasAttribute('ms-code-tooltip-trigger')) {
tippyOptions.trigger = element.getAttribute('ms-code-tooltip-trigger');
}
// Hide On Click
if (element.hasAttribute('ms-code-tooltip-hideOnClick')) {
tippyOptions.hideOnClick = element.getAttribute('ms-code-tooltip-hideOnClick') === 'true';
}
// Follow Cursor
if (element.hasAttribute('ms-code-tooltip-followCursor')) {
tippyOptions.followCursor = element.getAttribute('ms-code-tooltip-followCursor');
}
// Offset
if (element.hasAttribute('ms-code-tooltip-offset')) {
tippyOptions.offset = JSON.parse(element.getAttribute('ms-code-tooltip-offset'));
}
// Z-Index
if (element.hasAttribute('ms-code-tooltip-zIndex')) {
tippyOptions.zIndex = parseInt(element.getAttribute('ms-code-tooltip-zIndex'));
}
// Allow HTML
if (element.hasAttribute('ms-code-tooltip-allowHTML')) {
tippyOptions.allowHTML = element.getAttribute('ms-code-tooltip-allowHTML') === 'true';
}
// Touch
if (element.hasAttribute('ms-code-tooltip-touch')) {
const touchValue = element.getAttribute('ms-code-tooltip-touch');
tippyOptions.touch = touchValue === 'true' || touchValue === 'false' ? (touchValue === 'true') : JSON.parse(touchValue);
}
// Initialize Tippy instance
tippy(element, tippyOptions);
});
}
// Wait for the DOM to be fully loaded, then load Tippy and initialize tooltips
document.addEventListener('DOMContentLoaded', function() {
loadTippy(initializeTippyTooltips);
});
</script>

#109 - Benutzerdefinierte Multi Selects
Individuell gestaltete Mehrfachauswahlen mit Suche, Tastaturauswahl und mehr.
<!-- 💙 MEMBERSCRIPT #109 v0.1 💙 - CUSTOM MULTI SELECT -->
<script>
$(document).ready(function() {
$('[ms-code-select-wrapper]').each(function() {
const $wrapper = $(this);
const isMulti = $wrapper.attr('ms-code-select-wrapper') === 'multi';
const $input = $wrapper.find('[ms-code-select="input"]');
const $list = $wrapper.find('[ms-code-select="list"]');
const $selectedWrapper = $wrapper.find('[ms-code-select="selected-wrapper"]');
const $emptyState = $wrapper.find('[ms-code-select="empty-state"]');
const options = $input.attr('ms-code-select-options').split(',').map(opt => opt.trim());
let selectedOptions = [];
let highlightedIndex = -1;
const $templateSelectedTag = $selectedWrapper.find('[ms-code-select="tag"]');
const templateSelectedTagHTML = $templateSelectedTag.prop('outerHTML');
$templateSelectedTag.remove();
const $templateNewTag = $list.find('[ms-code-select="tag-name-new"]');
const templateNewTagHTML = $templateNewTag.prop('outerHTML');
$templateNewTag.remove();
function createSelectedTag(value) {
const $newTag = $(templateSelectedTagHTML);
$newTag.find('[ms-code-select="tag-name-selected"]').text(value);
$newTag.find('[ms-code-select="tag-close"]').on('click', function(e) {
e.stopPropagation();
removeTag(value);
});
return $newTag;
}
function addTag(value) {
if (!selectedOptions.includes(value) && options.includes(value)) {
selectedOptions.push(value);
$selectedWrapper.append(createSelectedTag(value));
updateInput();
filterOptions();
}
}
function removeTag(value) {
selectedOptions = selectedOptions.filter(option => option !== value);
$selectedWrapper.find(`[ms-code-select="tag-name-selected"]:contains("${value}")`).closest('[ms-code-select="tag"]').remove();
updateInput();
if (isMulti && selectedOptions.length > 0) {
$input.val($input.val() + ', ');
}
filterOptions();
}
function updateInput() {
$input.val(selectedOptions.join(', '));
}
function toggleList(show) {
$list.toggle(show);
}
function createOptionElement(value) {
const $option = $(templateNewTagHTML);
$option.text(value);
$option.on('click', function() {
selectOption(value);
});
return $option;
}
function selectOption(value) {
if (isMulti) {
addTag(value);
$input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
$input.focus();
} else {
selectedOptions = [value];
$selectedWrapper.empty().append(createSelectedTag(value));
updateInput();
toggleList(false);
}
filterOptions();
}
function filterOptions() {
const inputValue = $input.val();
const searchTerm = isMulti ? inputValue.split(',').pop().trim() : inputValue.trim();
let visibleOptionsCount = 0;
$list.find('[ms-code-select="tag-name-new"]').each(function() {
const $option = $(this);
const optionText = $option.text().toLowerCase();
const matches = optionText.includes(searchTerm.toLowerCase());
const isSelected = selectedOptions.includes($option.text());
$option.toggle(matches && !isSelected);
if (matches && !isSelected) visibleOptionsCount++;
});
$emptyState.toggle(visibleOptionsCount === 0 && searchTerm !== '');
highlightedIndex = -1;
updateHighlight();
}
function cleanInput() {
const inputValues = $input.val().split(',').map(v => v.trim()).filter(v => v);
const validValues = inputValues.filter(v => options.includes(v));
selectedOptions = validValues;
$selectedWrapper.empty();
selectedOptions.forEach(value => $selectedWrapper.append(createSelectedTag(value)));
updateInput();
filterOptions();
}
function handleInputChange() {
const inputValue = $input.val();
const inputValues = inputValue.split(',').map(v => v.trim());
const lastValue = inputValues[inputValues.length - 1];
if (inputValue.endsWith(',') || inputValue.endsWith(', ')) {
inputValues.pop();
const newValidValues = inputValues.filter(v => options.includes(v) && !selectedOptions.includes(v));
newValidValues.forEach(addTag);
$input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
} else if (options.includes(lastValue) && !selectedOptions.includes(lastValue)) {
addTag(lastValue);
$input.val(selectedOptions.join(', ') + ', ');
}
filterOptions();
}
function initializeWithValue() {
const initialValue = $input.val();
if (initialValue) {
const initialValues = initialValue.split(',').map(v => v.trim());
initialValues.forEach(value => {
if (options.includes(value)) {
addTag(value);
}
});
updateInput();
filterOptions();
}
}
function updateHighlight() {
$list.find('[ms-code-select="tag-name-new"]').removeClass('highlighted').css('background-color', '');
if (highlightedIndex >= 0) {
$list.find('[ms-code-select="tag-name-new"]:visible').eq(highlightedIndex)
.addClass('highlighted')
.css('background-color', '#e0e0e0');
}
}
function handleKeyDown(e) {
const visibleOptions = $list.find('[ms-code-select="tag-name-new"]:visible');
const optionCount = visibleOptions.length;
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
highlightedIndex = (highlightedIndex + 1) % optionCount;
updateHighlight();
break;
case 'ArrowUp':
e.preventDefault();
highlightedIndex = (highlightedIndex - 1 + optionCount) % optionCount;
updateHighlight();
break;
case 'Enter':
e.preventDefault();
if (highlightedIndex >= 0) {
const selectedValue = visibleOptions.eq(highlightedIndex).text();
selectOption(selectedValue);
}
break;
}
}
$.each(options, function(i, option) {
$list.append(createOptionElement(option));
});
$input.on('focus', function() {
toggleList(true);
if (isMulti) {
const currentVal = $input.val().trim();
if (currentVal !== '' && !currentVal.endsWith(',')) {
$input.val(currentVal + ', ');
}
this.selectionStart = this.selectionEnd = this.value.length;
}
filterOptions();
});
$input.on('click', function(e) {
e.preventDefault();
this.selectionStart = this.selectionEnd = this.value.length;
});
$input.on('blur', function() {
setTimeout(function() {
if (!$list.is(':hover')) {
toggleList(false);
cleanInput();
}
}, 100);
});
$input.on('input', handleInputChange);
$input.on('keydown', handleKeyDown);
$list.on('mouseenter', '[ms-code-select="tag-name-new"]', function() {
$(this).css('background-color', '#e0e0e0');
});
$list.on('mouseleave', '[ms-code-select="tag-name-new"]', function() {
if (!$(this).hasClass('highlighted')) {
$(this).css('background-color', '');
}
});
initializeWithValue();
toggleList(false);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #109 v0.1 💙 - CUSTOM MULTI SELECT -->
<script>
$(document).ready(function() {
$('[ms-code-select-wrapper]').each(function() {
const $wrapper = $(this);
const isMulti = $wrapper.attr('ms-code-select-wrapper') === 'multi';
const $input = $wrapper.find('[ms-code-select="input"]');
const $list = $wrapper.find('[ms-code-select="list"]');
const $selectedWrapper = $wrapper.find('[ms-code-select="selected-wrapper"]');
const $emptyState = $wrapper.find('[ms-code-select="empty-state"]');
const options = $input.attr('ms-code-select-options').split(',').map(opt => opt.trim());
let selectedOptions = [];
let highlightedIndex = -1;
const $templateSelectedTag = $selectedWrapper.find('[ms-code-select="tag"]');
const templateSelectedTagHTML = $templateSelectedTag.prop('outerHTML');
$templateSelectedTag.remove();
const $templateNewTag = $list.find('[ms-code-select="tag-name-new"]');
const templateNewTagHTML = $templateNewTag.prop('outerHTML');
$templateNewTag.remove();
function createSelectedTag(value) {
const $newTag = $(templateSelectedTagHTML);
$newTag.find('[ms-code-select="tag-name-selected"]').text(value);
$newTag.find('[ms-code-select="tag-close"]').on('click', function(e) {
e.stopPropagation();
removeTag(value);
});
return $newTag;
}
function addTag(value) {
if (!selectedOptions.includes(value) && options.includes(value)) {
selectedOptions.push(value);
$selectedWrapper.append(createSelectedTag(value));
updateInput();
filterOptions();
}
}
function removeTag(value) {
selectedOptions = selectedOptions.filter(option => option !== value);
$selectedWrapper.find(`[ms-code-select="tag-name-selected"]:contains("${value}")`).closest('[ms-code-select="tag"]').remove();
updateInput();
if (isMulti && selectedOptions.length > 0) {
$input.val($input.val() + ', ');
}
filterOptions();
}
function updateInput() {
$input.val(selectedOptions.join(', '));
}
function toggleList(show) {
$list.toggle(show);
}
function createOptionElement(value) {
const $option = $(templateNewTagHTML);
$option.text(value);
$option.on('click', function() {
selectOption(value);
});
return $option;
}
function selectOption(value) {
if (isMulti) {
addTag(value);
$input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
$input.focus();
} else {
selectedOptions = [value];
$selectedWrapper.empty().append(createSelectedTag(value));
updateInput();
toggleList(false);
}
filterOptions();
}
function filterOptions() {
const inputValue = $input.val();
const searchTerm = isMulti ? inputValue.split(',').pop().trim() : inputValue.trim();
let visibleOptionsCount = 0;
$list.find('[ms-code-select="tag-name-new"]').each(function() {
const $option = $(this);
const optionText = $option.text().toLowerCase();
const matches = optionText.includes(searchTerm.toLowerCase());
const isSelected = selectedOptions.includes($option.text());
$option.toggle(matches && !isSelected);
if (matches && !isSelected) visibleOptionsCount++;
});
$emptyState.toggle(visibleOptionsCount === 0 && searchTerm !== '');
highlightedIndex = -1;
updateHighlight();
}
function cleanInput() {
const inputValues = $input.val().split(',').map(v => v.trim()).filter(v => v);
const validValues = inputValues.filter(v => options.includes(v));
selectedOptions = validValues;
$selectedWrapper.empty();
selectedOptions.forEach(value => $selectedWrapper.append(createSelectedTag(value)));
updateInput();
filterOptions();
}
function handleInputChange() {
const inputValue = $input.val();
const inputValues = inputValue.split(',').map(v => v.trim());
const lastValue = inputValues[inputValues.length - 1];
if (inputValue.endsWith(',') || inputValue.endsWith(', ')) {
inputValues.pop();
const newValidValues = inputValues.filter(v => options.includes(v) && !selectedOptions.includes(v));
newValidValues.forEach(addTag);
$input.val(selectedOptions.join(', ') + (selectedOptions.length > 0 ? ', ' : ''));
} else if (options.includes(lastValue) && !selectedOptions.includes(lastValue)) {
addTag(lastValue);
$input.val(selectedOptions.join(', ') + ', ');
}
filterOptions();
}
function initializeWithValue() {
const initialValue = $input.val();
if (initialValue) {
const initialValues = initialValue.split(',').map(v => v.trim());
initialValues.forEach(value => {
if (options.includes(value)) {
addTag(value);
}
});
updateInput();
filterOptions();
}
}
function updateHighlight() {
$list.find('[ms-code-select="tag-name-new"]').removeClass('highlighted').css('background-color', '');
if (highlightedIndex >= 0) {
$list.find('[ms-code-select="tag-name-new"]:visible').eq(highlightedIndex)
.addClass('highlighted')
.css('background-color', '#e0e0e0');
}
}
function handleKeyDown(e) {
const visibleOptions = $list.find('[ms-code-select="tag-name-new"]:visible');
const optionCount = visibleOptions.length;
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
highlightedIndex = (highlightedIndex + 1) % optionCount;
updateHighlight();
break;
case 'ArrowUp':
e.preventDefault();
highlightedIndex = (highlightedIndex - 1 + optionCount) % optionCount;
updateHighlight();
break;
case 'Enter':
e.preventDefault();
if (highlightedIndex >= 0) {
const selectedValue = visibleOptions.eq(highlightedIndex).text();
selectOption(selectedValue);
}
break;
}
}
$.each(options, function(i, option) {
$list.append(createOptionElement(option));
});
$input.on('focus', function() {
toggleList(true);
if (isMulti) {
const currentVal = $input.val().trim();
if (currentVal !== '' && !currentVal.endsWith(',')) {
$input.val(currentVal + ', ');
}
this.selectionStart = this.selectionEnd = this.value.length;
}
filterOptions();
});
$input.on('click', function(e) {
e.preventDefault();
this.selectionStart = this.selectionEnd = this.value.length;
});
$input.on('blur', function() {
setTimeout(function() {
if (!$list.is(':hover')) {
toggleList(false);
cleanInput();
}
}, 100);
});
$input.on('input', handleInputChange);
$input.on('keydown', handleKeyDown);
$list.on('mouseenter', '[ms-code-select="tag-name-new"]', function() {
$(this).css('background-color', '#e0e0e0');
});
$list.on('mouseleave', '[ms-code-select="tag-name-new"]', function() {
if (!$(this).hasClass('highlighted')) {
$(this).css('background-color', '');
}
});
initializeWithValue();
toggleList(false);
});
});
</script>

#Nr. 108 - Benutzerdefinierte Formularübermittlungsschaltflächen
Erstellen Sie ein beliebiges Element in Webflow und verwenden Sie es, um eine beliebige Art von Formular zu übermitteln.
<!-- 💙 MEMBERSCRIPT #108 v0.1 💙 CUSTOM FORM SUBMIT BUTTON -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
// Find all elements with the ms-code-submit-new attribute
const newSubmitButtons = document.querySelectorAll('[ms-code-submit-new]');
// Add click event listeners to each new submit button
newSubmitButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault(); // Prevent default action if it's a link
// Get the value of the ms-code-submit-new attribute
const submitId = this.getAttribute('ms-code-submit-new');
// Find the corresponding old submit button
const oldSubmitButton = document.querySelector(`[ms-code-submit-old="${submitId}"]`);
// If found, trigger a click on the old submit button
if (oldSubmitButton) {
oldSubmitButton.click();
} else {
console.error(`No matching old submit button found for ID: ${submitId}`);
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #108 v0.1 💙 CUSTOM FORM SUBMIT BUTTON -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
// Find all elements with the ms-code-submit-new attribute
const newSubmitButtons = document.querySelectorAll('[ms-code-submit-new]');
// Add click event listeners to each new submit button
newSubmitButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault(); // Prevent default action if it's a link
// Get the value of the ms-code-submit-new attribute
const submitId = this.getAttribute('ms-code-submit-new');
// Find the corresponding old submit button
const oldSubmitButton = document.querySelector(`[ms-code-submit-old="${submitId}"]`);
// If found, trigger a click on the old submit button
if (oldSubmitButton) {
oldSubmitButton.click();
} else {
console.error(`No matching old submit button found for ID: ${submitId}`);
}
});
});
});
</script>

#Nr. 107 - Plan mit Funkgeräten auswählen
Fügen Sie den Anmeldeformularen und den Formularen zur Planaktualisierung ein Auswahlradio für den Plan hinzu.
<!-- 💙 MEMBERSCRIPT #107 v0.1 💙 SELECT PLAN WITH RADIO BUTTONS -->
<script>
(function() {
const PRICE_ATTRIBUTES = [
'data-ms-plan:add',
'data-ms-plan:update',
'data-ms-price:add',
'data-ms-price:update'
];
function findElementWithAttribute(form) {
// First, check if the form itself has one of the attributes
for (let attr of PRICE_ATTRIBUTES) {
if (form.hasAttribute(attr)) {
return { element: form, attribute: attr };
}
}
// If not found on form, search child elements
for (let attr of PRICE_ATTRIBUTES) {
let element = Array.from(form.querySelectorAll('*')).find(el => el.hasAttribute(attr));
if (element) {
return { element, attribute: attr };
}
}
return null;
}
function updateAttribute(radio) {
const form = radio.closest('form');
if (!form) return;
const result = findElementWithAttribute(form);
if (result) {
result.element.setAttribute(result.attribute, radio.value);
}
}
function handleRadioChange(e) {
updateAttribute(e.target);
}
function initializeRadioButtons() {
const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
forms.forEach(form => {
const radios = form.querySelectorAll('input[type="radio"]');
radios.forEach(radio => {
radio.addEventListener('change', handleRadioChange);
if (radio.checked) {
updateAttribute(radio);
}
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeRadioButtons);
} else {
initializeRadioButtons();
}
})();
// Only keep this section if you have an update plan form on the page
(function() {
function updateRadioButtonState(radio) {
radio.checked = true;
radio.dispatchEvent(new Event('change'));
// Update custom radio button UI if present
const customRadio = radio.parentElement.querySelector('.w-radio-input');
if (customRadio) {
customRadio.classList.add('w--redirected-checked');
}
}
function checkAndSelectPlan() {
const msMemData = localStorage.getItem('_ms-mem');
if (!msMemData) return;
try {
const memberData = JSON.parse(msMemData);
const activePlanConnections = memberData.planConnections?.filter(conn => conn.active) || [];
if (activePlanConnections.length === 0) return;
const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
forms.forEach(form => {
const radios = form.querySelectorAll('input[type="radio"]');
radios.forEach(radio => {
const matchingPlan = activePlanConnections.find(conn => conn.payment.priceId === radio.value);
if (matchingPlan) {
updateRadioButtonState(radio);
}
});
});
} catch (error) {
console.error('Error processing _ms-mem data:', error);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', checkAndSelectPlan);
} else {
checkAndSelectPlan();
}
})();
</script>
<!-- 💙 MEMBERSCRIPT #107 v0.1 💙 SELECT PLAN WITH RADIO BUTTONS -->
<script>
(function() {
const PRICE_ATTRIBUTES = [
'data-ms-plan:add',
'data-ms-plan:update',
'data-ms-price:add',
'data-ms-price:update'
];
function findElementWithAttribute(form) {
// First, check if the form itself has one of the attributes
for (let attr of PRICE_ATTRIBUTES) {
if (form.hasAttribute(attr)) {
return { element: form, attribute: attr };
}
}
// If not found on form, search child elements
for (let attr of PRICE_ATTRIBUTES) {
let element = Array.from(form.querySelectorAll('*')).find(el => el.hasAttribute(attr));
if (element) {
return { element, attribute: attr };
}
}
return null;
}
function updateAttribute(radio) {
const form = radio.closest('form');
if (!form) return;
const result = findElementWithAttribute(form);
if (result) {
result.element.setAttribute(result.attribute, radio.value);
}
}
function handleRadioChange(e) {
updateAttribute(e.target);
}
function initializeRadioButtons() {
const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
forms.forEach(form => {
const radios = form.querySelectorAll('input[type="radio"]');
radios.forEach(radio => {
radio.addEventListener('change', handleRadioChange);
if (radio.checked) {
updateAttribute(radio);
}
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeRadioButtons);
} else {
initializeRadioButtons();
}
})();
// Only keep this section if you have an update plan form on the page
(function() {
function updateRadioButtonState(radio) {
radio.checked = true;
radio.dispatchEvent(new Event('change'));
// Update custom radio button UI if present
const customRadio = radio.parentElement.querySelector('.w-radio-input');
if (customRadio) {
customRadio.classList.add('w--redirected-checked');
}
}
function checkAndSelectPlan() {
const msMemData = localStorage.getItem('_ms-mem');
if (!msMemData) return;
try {
const memberData = JSON.parse(msMemData);
const activePlanConnections = memberData.planConnections?.filter(conn => conn.active) || [];
if (activePlanConnections.length === 0) return;
const forms = document.querySelectorAll('[ms-code-radio-plan="form"]');
forms.forEach(form => {
const radios = form.querySelectorAll('input[type="radio"]');
radios.forEach(radio => {
const matchingPlan = activePlanConnections.find(conn => conn.payment.priceId === radio.value);
if (matchingPlan) {
updateRadioButtonState(radio);
}
});
});
} catch (error) {
console.error('Error processing _ms-mem data:', error);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', checkAndSelectPlan);
} else {
checkAndSelectPlan();
}
})();
</script>

#Nr. 106 - Liken und Speichern von CMS-Artikeln
Erlauben Sie Ihren Mitgliedern, CMS-Elemente in ihrem Profil zu speichern.
<!-- 💙 MEMBERSCRIPT #106 v0.2 💙 SAVING & UNSAVING CMS ITEMS -->
<style>
[ms-code-save], [ms-code-unsave] {
display: none;
}
[ms-code-save-item] {
display: none;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
let isLoggedIn = false;
let savedItems = [];
async function checkMemberLogin() {
try {
const member = await memberstack.getCurrentMember();
return !!member;
} catch (error) {
return false;
}
}
function getSavedItems(memberData) {
return memberData.savedItems || [];
}
function updateButtonVisibility() {
const saveButtons = document.querySelectorAll('[ms-code-save]');
const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
saveButtons.forEach(button => {
const itemId = button.getAttribute('ms-code-save');
button.style.display = !savedItems.includes(itemId) ? 'block' : 'none';
});
unsaveButtons.forEach(button => {
const itemId = button.getAttribute('ms-code-unsave');
button.style.display = savedItems.includes(itemId) ? 'block' : 'none';
});
}
function updateItemVisibility() {
const saveLists = document.querySelectorAll('[ms-code-save-list]');
saveLists.forEach(list => {
const filter = list.getAttribute('ms-code-save-list');
const items = list.querySelectorAll('[ms-code-save-item]');
items.forEach(item => {
const saveButton = item.querySelector('[ms-code-save]');
if (!saveButton) {
item.style.display = 'block';
return;
}
const itemId = saveButton.getAttribute('ms-code-save');
if (!isLoggedIn || filter === 'all') {
item.style.display = 'block';
} else if (filter === 'saved' & savedItems.includes(itemId)) {
item.style.display = 'block';
} else if (filter === 'unsaved' & !savedItems.includes(itemId)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
}
async function handleButtonClick(event) {
if (!isLoggedIn) return;
const button = event.currentTarget;
const action = button.getAttribute('ms-code-save') ? 'save' : 'unsave';
const itemId = button.getAttribute(action === 'save' ? 'ms-code-save' : 'ms-code-unsave');
if (action === 'save' && !savedItems.includes(itemId)) {
savedItems.push(itemId);
} else if (action === 'unsave') {
savedItems = savedItems.filter(id => id !== itemId);
}
try {
await memberstack.updateMemberJSON({ json: { savedItems } });
} catch (error) {
// Silently handle the error
}
updateButtonVisibility();
updateItemVisibility();
}
function addClickListeners() {
const saveButtons = document.querySelectorAll('[ms-code-save]');
const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
saveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
unsaveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
}
async function initializeScript() {
isLoggedIn = await checkMemberLogin();
if (isLoggedIn) {
try {
const result = await memberstack.getMemberJSON();
const memberData = result.data || {};
savedItems = getSavedItems(memberData);
} catch (error) {
// Silently handle the error
}
}
updateButtonVisibility();
updateItemVisibility();
addClickListeners();
// Set up a MutationObserver to watch for changes in the DOM
const observer = new MutationObserver((mutations) => {
let shouldUpdate = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
shouldUpdate = true;
}
});
if (shouldUpdate) {
updateButtonVisibility();
updateItemVisibility();
addClickListeners();
}
});
// Start observing the document with the configured parameters
observer.observe(document.body, { childList: true, subtree: true });
}
initializeScript();
});
</script>
<!-- 💙 MEMBERSCRIPT #106 v0.2 💙 SAVING & UNSAVING CMS ITEMS -->
<style>
[ms-code-save], [ms-code-unsave] {
display: none;
}
[ms-code-save-item] {
display: none;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const memberstack = window.$memberstackDom;
let isLoggedIn = false;
let savedItems = [];
async function checkMemberLogin() {
try {
const member = await memberstack.getCurrentMember();
return !!member;
} catch (error) {
return false;
}
}
function getSavedItems(memberData) {
return memberData.savedItems || [];
}
function updateButtonVisibility() {
const saveButtons = document.querySelectorAll('[ms-code-save]');
const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
saveButtons.forEach(button => {
const itemId = button.getAttribute('ms-code-save');
button.style.display = !savedItems.includes(itemId) ? 'block' : 'none';
});
unsaveButtons.forEach(button => {
const itemId = button.getAttribute('ms-code-unsave');
button.style.display = savedItems.includes(itemId) ? 'block' : 'none';
});
}
function updateItemVisibility() {
const saveLists = document.querySelectorAll('[ms-code-save-list]');
saveLists.forEach(list => {
const filter = list.getAttribute('ms-code-save-list');
const items = list.querySelectorAll('[ms-code-save-item]');
items.forEach(item => {
const saveButton = item.querySelector('[ms-code-save]');
if (!saveButton) {
item.style.display = 'block';
return;
}
const itemId = saveButton.getAttribute('ms-code-save');
if (!isLoggedIn || filter === 'all') {
item.style.display = 'block';
} else if (filter === 'saved' & savedItems.includes(itemId)) {
item.style.display = 'block';
} else if (filter === 'unsaved' & !savedItems.includes(itemId)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
}
async function handleButtonClick(event) {
if (!isLoggedIn) return;
const button = event.currentTarget;
const action = button.getAttribute('ms-code-save') ? 'save' : 'unsave';
const itemId = button.getAttribute(action === 'save' ? 'ms-code-save' : 'ms-code-unsave');
if (action === 'save' && !savedItems.includes(itemId)) {
savedItems.push(itemId);
} else if (action === 'unsave') {
savedItems = savedItems.filter(id => id !== itemId);
}
try {
await memberstack.updateMemberJSON({ json: { savedItems } });
} catch (error) {
// Silently handle the error
}
updateButtonVisibility();
updateItemVisibility();
}
function addClickListeners() {
const saveButtons = document.querySelectorAll('[ms-code-save]');
const unsaveButtons = document.querySelectorAll('[ms-code-unsave]');
saveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
unsaveButtons.forEach(button => button.addEventListener('click', handleButtonClick));
}
async function initializeScript() {
isLoggedIn = await checkMemberLogin();
if (isLoggedIn) {
try {
const result = await memberstack.getMemberJSON();
const memberData = result.data || {};
savedItems = getSavedItems(memberData);
} catch (error) {
// Silently handle the error
}
}
updateButtonVisibility();
updateItemVisibility();
addClickListeners();
// Set up a MutationObserver to watch for changes in the DOM
const observer = new MutationObserver((mutations) => {
let shouldUpdate = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
shouldUpdate = true;
}
});
if (shouldUpdate) {
updateButtonVisibility();
updateItemVisibility();
addClickListeners();
}
});
// Start observing the document with the configured parameters
observer.observe(document.body, { childList: true, subtree: true });
}
initializeScript();
});
</script>

#Nr. 105 - Auschecken nach Anmeldung
Automatischer Start der Kasse, wenn ein Mitglied einen Preis auswählt, bevor es sich einloggt.
<!-- 💙 MEMBERSCRIPT #105 v0.1 💙 CHECKOUT AFTER LOGIN -->
<script>
/* Checks if the current URL matches the configured redirect URL, or if no specific URL is required */
function isCorrectPage() {
return redirectOnLoginURL === '' || window.location.pathname === redirectOnLoginURL;
}
/* Checks if Memberstack is fully loaded before running any Memberstack-specific code.*/
function memberstackReady(callback) {
function checkAndExecute() {
if (window.$memberstackDom) {
callback(); // Memberstack is ready, run the callback function.
} else {
setTimeout(checkAndExecute, 100); // Wait for 100ms and check again.
}
}
checkAndExecute(); // Start checking if Memberstack is ready.
}
/* Initiates the Stripe checkout process with a specified price ID.*/
async function initiateCheckout(priceId) {
try {
// Set a flag in session storage to indicate that the checkout page was accessed.
sessionStorage.setItem('ms_checkout_viewed', 'true');
await window.$memberstackDom.purchasePlansWithCheckout({
priceId, // The price ID for the product being purchased.
returnUrl: window.location.href, // Redirect the user back here after completing the checkout.
});
} catch (error) {
console.error('Failed to initiate payment:', error); // Provide error details in the console.
}
}
/* Main execution flow that starts once Memberstack is confirmed to be ready */
memberstackReady(() => {
window.$memberstackDom.getCurrentMember().then(({ data: member }) => {
if (member && sessionStorage.getItem('ms_price') && !sessionStorage.getItem('ms_checkout_viewed')) {
initiateCheckout(sessionStorage.getItem('ms_price')); // Start the checkout process if conditions are met.
}
}).catch(error => {
console.error('Failed to retrieve user data:', error); // Log an error if fetching member data fails.
});
});
</script>
<!-- 💙 MEMBERSCRIPT #105 v0.1 💙 CHECKOUT AFTER LOGIN -->
<script>
/* Checks if the current URL matches the configured redirect URL, or if no specific URL is required */
function isCorrectPage() {
return redirectOnLoginURL === '' || window.location.pathname === redirectOnLoginURL;
}
/* Checks if Memberstack is fully loaded before running any Memberstack-specific code.*/
function memberstackReady(callback) {
function checkAndExecute() {
if (window.$memberstackDom) {
callback(); // Memberstack is ready, run the callback function.
} else {
setTimeout(checkAndExecute, 100); // Wait for 100ms and check again.
}
}
checkAndExecute(); // Start checking if Memberstack is ready.
}
/* Initiates the Stripe checkout process with a specified price ID.*/
async function initiateCheckout(priceId) {
try {
// Set a flag in session storage to indicate that the checkout page was accessed.
sessionStorage.setItem('ms_checkout_viewed', 'true');
await window.$memberstackDom.purchasePlansWithCheckout({
priceId, // The price ID for the product being purchased.
returnUrl: window.location.href, // Redirect the user back here after completing the checkout.
});
} catch (error) {
console.error('Failed to initiate payment:', error); // Provide error details in the console.
}
}
/* Main execution flow that starts once Memberstack is confirmed to be ready */
memberstackReady(() => {
window.$memberstackDom.getCurrentMember().then(({ data: member }) => {
if (member && sessionStorage.getItem('ms_price') && !sessionStorage.getItem('ms_checkout_viewed')) {
initiateCheckout(sessionStorage.getItem('ms_price')); // Start the checkout process if conditions are met.
}
}).catch(error => {
console.error('Failed to retrieve user data:', error); // Log an error if fetching member data fails.
});
});
</script>

#Nr. 104 - Online-Anzeige
Zeigen Sie Ihren Website-Besuchern Ihren Online-Status auf der Grundlage von Zeitzonen.
<!-- 💙 MEMBERSCRIPT #104 v0.1 💙 ONLINE INDICATOR -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const businessHours = {
start: 9, // Business hours start at 9 AM
end: 17, // Business hours end at 5 PM
days: [1, 2, 3, 4, 5] // Monday to Friday
};
const colors = {
businessHours: '#34b426',
outsideBusinessHours: '#F25022'
};
const wrappers = document.querySelectorAll('[ms-code-online-wrapper]');
wrappers.forEach(wrapper => {
const timeZone = wrapper.getAttribute('ms-code-online-wrapper');
const dot = wrapper.querySelector('[ms-code-online="dot"]');
const timeSpan = wrapper.querySelector('[ms-code-online="time"]');
const now = new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
timeZone: timeZone
});
const formattedTime = formatter.format(now);
if (timeSpan) timeSpan.textContent = formattedTime;
const currentDay = now.getDay();
const currentHour = new Date().toLocaleTimeString('en-US', {
hour: '2-digit',
hour12: false,
timeZone: timeZone
});
const isBusinessDay = businessHours.days.includes(currentDay);
const isBusinessHour = currentHour >= businessHours.start && currentHour < businessHours.end;
if (dot) {
dot.style.backgroundColor = (isBusinessDay && isBusinessHour) ? colors.businessHours : colors.outsideBusinessHours;
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #104 v0.1 💙 ONLINE INDICATOR -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const businessHours = {
start: 9, // Business hours start at 9 AM
end: 17, // Business hours end at 5 PM
days: [1, 2, 3, 4, 5] // Monday to Friday
};
const colors = {
businessHours: '#34b426',
outsideBusinessHours: '#F25022'
};
const wrappers = document.querySelectorAll('[ms-code-online-wrapper]');
wrappers.forEach(wrapper => {
const timeZone = wrapper.getAttribute('ms-code-online-wrapper');
const dot = wrapper.querySelector('[ms-code-online="dot"]');
const timeSpan = wrapper.querySelector('[ms-code-online="time"]');
const now = new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
timeZone: timeZone
});
const formattedTime = formatter.format(now);
if (timeSpan) timeSpan.textContent = formattedTime;
const currentDay = now.getDay();
const currentHour = new Date().toLocaleTimeString('en-US', {
hour: '2-digit',
hour12: false,
timeZone: timeZone
});
const isBusinessDay = businessHours.days.includes(currentDay);
const isBusinessHour = currentHour >= businessHours.start && currentHour < businessHours.end;
if (dot) {
dot.style.backgroundColor = (isBusinessDay && isBusinessHour) ? colors.businessHours : colors.outsideBusinessHours;
}
});
});
</script>

#Nr. 103 - Benutzerdefiniertes Buchungsformular
Fügen Sie Ihrer Website ein benutzerdefiniertes Buchungsformular hinzu, das ein Google-Kalenderereignis erstellt.
<!-- 💙 MEMBERSCRIPT #103 v0.1 💙 CUSTOM BOOKING FORM -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
function getNextBusinessDays() {
let businessDays = [];
let currentDate = moment();
currentDate.add(1, 'days');
while (businessDays.length < 14) {
if (currentDate.day() !== 0 && currentDate.day() !== 6) {
let formattedDay = currentDate.format('dddd, MMMM D');
let rawDay = currentDate.format('YYYY-MM-DD');
businessDays.push({formattedDay, rawDay});
}
currentDate.add(1, 'days');
}
return businessDays;
}
function generateTimeSlots() {
let slots = [];
let startHour = 9;
let endHour = 16.5;
let currentTime = moment().startOf('day').add(startHour, 'hours');
while (currentTime.hour() + (currentTime.minute() / 60) <= endHour) {
let formattedTime = currentTime.format('h:mm A');
let timeValue = currentTime.format('HH:mm');
slots.push({formattedTime, timeValue});
currentTime.add(30, 'minutes');
}
return slots;
}
function updateTimestamp(day, time, timezone) {
let timestampInput = document.getElementById('timestamp');
if (!timestampInput) {
timestampInput = document.createElement('input');
timestampInput.type = 'hidden';
timestampInput.id = 'timestamp';
timestampInput.name = 'timestamp';
document.querySelector('form').appendChild(timestampInput);
}
let datetime = moment.tz(`${day} ${time}`, "YYYY-MM-DD HH:mm", timezone);
timestampInput.value = datetime.valueOf();
}
function populateFields() {
const days = getNextBusinessDays();
const times = generateTimeSlots();
const daySelect = document.querySelector('[ms-code-booking="day"]');
const timeSelect = document.querySelector('[ms-code-booking="time"]');
const form = daySelect.closest('form');
const timezone = form.getAttribute('ms-code-booking-timezone') || moment.tz.guess();
days.forEach(({formattedDay, rawDay}) => {
let option = new Option(formattedDay, rawDay);
daySelect.appendChild(option);
});
times.forEach(({formattedTime, timeValue}) => {
let option = new Option(formattedTime, timeValue);
timeSelect.appendChild(option);
});
function handleSelectChange() {
if (daySelect.value && timeSelect.value) {
updateTimestamp(daySelect.value, timeSelect.value, timezone);
}
}
daySelect.addEventListener('change', handleSelectChange);
timeSelect.addEventListener('change', handleSelectChange);
}
populateFields();
});
</script>
<!-- 💙 MEMBERSCRIPT #103 v0.1 💙 CUSTOM BOOKING FORM -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
function getNextBusinessDays() {
let businessDays = [];
let currentDate = moment();
currentDate.add(1, 'days');
while (businessDays.length < 14) {
if (currentDate.day() !== 0 && currentDate.day() !== 6) {
let formattedDay = currentDate.format('dddd, MMMM D');
let rawDay = currentDate.format('YYYY-MM-DD');
businessDays.push({formattedDay, rawDay});
}
currentDate.add(1, 'days');
}
return businessDays;
}
function generateTimeSlots() {
let slots = [];
let startHour = 9;
let endHour = 16.5;
let currentTime = moment().startOf('day').add(startHour, 'hours');
while (currentTime.hour() + (currentTime.minute() / 60) <= endHour) {
let formattedTime = currentTime.format('h:mm A');
let timeValue = currentTime.format('HH:mm');
slots.push({formattedTime, timeValue});
currentTime.add(30, 'minutes');
}
return slots;
}
function updateTimestamp(day, time, timezone) {
let timestampInput = document.getElementById('timestamp');
if (!timestampInput) {
timestampInput = document.createElement('input');
timestampInput.type = 'hidden';
timestampInput.id = 'timestamp';
timestampInput.name = 'timestamp';
document.querySelector('form').appendChild(timestampInput);
}
let datetime = moment.tz(`${day} ${time}`, "YYYY-MM-DD HH:mm", timezone);
timestampInput.value = datetime.valueOf();
}
function populateFields() {
const days = getNextBusinessDays();
const times = generateTimeSlots();
const daySelect = document.querySelector('[ms-code-booking="day"]');
const timeSelect = document.querySelector('[ms-code-booking="time"]');
const form = daySelect.closest('form');
const timezone = form.getAttribute('ms-code-booking-timezone') || moment.tz.guess();
days.forEach(({formattedDay, rawDay}) => {
let option = new Option(formattedDay, rawDay);
daySelect.appendChild(option);
});
times.forEach(({formattedTime, timeValue}) => {
let option = new Option(formattedTime, timeValue);
timeSelect.appendChild(option);
});
function handleSelectChange() {
if (daySelect.value && timeSelect.value) {
updateTimestamp(daySelect.value, timeSelect.value, timezone);
}
}
daySelect.addEventListener('change', handleSelectChange);
timeSelect.addEventListener('change', handleSelectChange);
}
populateFields();
});
</script>

#Nr. 102 - Automatische Anpassung der Textarea-Höhe
Vergrößern oder verkleinern Sie die Höhe einer Textarea je nach ihrem Inhalt.
<!-- 💙 MEMBERSCRIPT #102 v0.1 💙 RESIZE TEXTAREA VERTICALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('[data-ms-post="content"], [ms-code-resize-input="height"]');
elements.forEach(element => {
if (element.tagName.toLowerCase() === 'textarea') {
element.addEventListener('input', function() {
autoResize(this);
}, false);
}
});
function autoResize(element) {
const maxHeight = parseInt(getComputedStyle(element).maxHeight, 10);
element.style.height = 'auto';
element.style.overflow = 'hidden'; // Prevents scrollbar appearance during height adjustment
if (element.scrollHeight > maxHeight) {
element.style.height = `${maxHeight}px`;
element.style.overflow = 'auto'; // Adds scrollbar when content exceeds max height
} else {
element.style.height = `${element.scrollHeight}px`;
}
}
});
</script>
<!-- 💙 MEMBERSCRIPT #102 v0.1 💙 RESIZE TEXTAREA VERTICALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('[data-ms-post="content"], [ms-code-resize-input="height"]');
elements.forEach(element => {
if (element.tagName.toLowerCase() === 'textarea') {
element.addEventListener('input', function() {
autoResize(this);
}, false);
}
});
function autoResize(element) {
const maxHeight = parseInt(getComputedStyle(element).maxHeight, 10);
element.style.height = 'auto';
element.style.overflow = 'hidden'; // Prevents scrollbar appearance during height adjustment
if (element.scrollHeight > maxHeight) {
element.style.height = `${maxHeight}px`;
element.style.overflow = 'auto'; // Adds scrollbar when content exceeds max height
} else {
element.style.height = `${element.scrollHeight}px`;
}
}
});
</script>

#101 - Automatische Anpassung der Eingabebreite
Erhöhen oder verringern Sie die Breite einer Eingabe je nach Inhalt.
<!-- 💙 MEMBERSCRIPT #101 v0.1 💙 RESIZE INPUT HORIZONTALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('[ms-code-resize-input="width"]');
// Store the initial widths
const initialWidths = new Map();
elements.forEach(element => {
initialWidths.set(element, element.offsetWidth);
});
elements.forEach(element => {
element.addEventListener('input', function() {
autoResizeWidth(this);
});
});
function autoResizeWidth(element) {
// Find the nearest hidden measure element
const measurer = element.nextElementSibling.getAttribute('ms-code-resize-input') === 'hidden-measure'
? element.nextElementSibling
: null;
if (!measurer) return; // Exit if no measurer is found
measurer.textContent = element.value;
const maxWidth = parseInt(getComputedStyle(element).maxWidth, 10);
const minWidth = initialWidths.get(element);
const contentWidth = measurer.offsetWidth;
if (contentWidth > minWidth && contentWidth < maxWidth) {
element.style.width = `${contentWidth}px`;
} else if (contentWidth >= maxWidth) {
element.style.width = `${maxWidth}px`;
} else {
element.style.width = `${minWidth}px`;
}
}
});
</script>
<!-- 💙 MEMBERSCRIPT #101 v0.1 💙 RESIZE INPUT HORIZONTALLY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('[ms-code-resize-input="width"]');
// Store the initial widths
const initialWidths = new Map();
elements.forEach(element => {
initialWidths.set(element, element.offsetWidth);
});
elements.forEach(element => {
element.addEventListener('input', function() {
autoResizeWidth(this);
});
});
function autoResizeWidth(element) {
// Find the nearest hidden measure element
const measurer = element.nextElementSibling.getAttribute('ms-code-resize-input') === 'hidden-measure'
? element.nextElementSibling
: null;
if (!measurer) return; // Exit if no measurer is found
measurer.textContent = element.value;
const maxWidth = parseInt(getComputedStyle(element).maxWidth, 10);
const minWidth = initialWidths.get(element);
const contentWidth = measurer.offsetWidth;
if (contentWidth > minWidth && contentWidth < maxWidth) {
element.style.width = `${contentWidth}px`;
} else if (contentWidth >= maxWidth) {
element.style.width = `${maxWidth}px`;
} else {
element.style.width = `${minWidth}px`;
}
}
});
</script>

#Nr. 100 - Bild-Uploads automatisch komprimieren
Komprimieren Sie Bild-Uploads, einschließlich Profilbilder.
<!-- 💙 MEMBERSCRIPT #100 v0.1 💙 AUTO-COMPRESSED IMAGE UPLOADS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/compressorjs/1.2.1/compressor.min.js" integrity="sha512-MgYeYFj8R3S6rvZHiJ1xA9cM/VDGcT4eRRFQwGA7qDP7NHbnWKNmAm28z0LVjOuUqjD0T9JxpDMdVqsZOSHaSA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const compressibleInputs = document.querySelectorAll('input[type="file"][ms-code-file_compress]');
compressibleInputs.forEach(fileInput => {
let isCompressing = false;
fileInput.addEventListener('change', function (event) {
if (isCompressing) {
isCompressing = false;
return;
}
if (fileInput.files.length === 0) {
return;
}
const originalFile = fileInput.files[0];
const compressionLevel = parseFloat(fileInput.getAttribute('ms-code-file_compress'));
new Compressor(originalFile, {
quality: compressionLevel,
maxWidth: 2000,
maxHeight: 2000,
success(compressedResult) {
const compressedFile = new File([compressedResult], originalFile.name, {
type: compressedResult.type,
lastModified: Date.now(),
});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(compressedFile);
fileInput.files = dataTransfer.files;
isCompressing = true;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
},
error(err) {
console.error('Compression Error: ', err.message);
},
});
event.stopPropagation();
}, true);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #100 v0.1 💙 AUTO-COMPRESSED IMAGE UPLOADS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/compressorjs/1.2.1/compressor.min.js" integrity="sha512-MgYeYFj8R3S6rvZHiJ1xA9cM/VDGcT4eRRFQwGA7qDP7NHbnWKNmAm28z0LVjOuUqjD0T9JxpDMdVqsZOSHaSA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const compressibleInputs = document.querySelectorAll('input[type="file"][ms-code-file_compress]');
compressibleInputs.forEach(fileInput => {
let isCompressing = false;
fileInput.addEventListener('change', function (event) {
if (isCompressing) {
isCompressing = false;
return;
}
if (fileInput.files.length === 0) {
return;
}
const originalFile = fileInput.files[0];
const compressionLevel = parseFloat(fileInput.getAttribute('ms-code-file_compress'));
new Compressor(originalFile, {
quality: compressionLevel,
maxWidth: 2000,
maxHeight: 2000,
success(compressedResult) {
const compressedFile = new File([compressedResult], originalFile.name, {
type: compressedResult.type,
lastModified: Date.now(),
});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(compressedFile);
fileInput.files = dataTransfer.files;
isCompressing = true;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
},
error(err) {
console.error('Compression Error: ', err.message);
},
});
event.stopPropagation();
}, true);
});
});
</script>

#Nr. 99 - Benutzerdefinierte Datei-Eingaben
Verwandeln Sie alles in eine Dateieingabe!
<!-- 💙 MEMBERSCRIPT #99 v0.1 💙 CUSTOM FILE UPLOAD INPUT -->
<script>
document.addEventListener('DOMContentLoaded', function () {
const uploadButtons = document.querySelectorAll('[ms-code-file_uploader]');
uploadButtons.forEach(button => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.style.display = 'none';
fileInput.name = button.getAttribute('ms-code-file_uploader');
fileInput.accept = button.getAttribute('ms-code-file_types');
document.body.appendChild(fileInput);
button.addEventListener('click', function (e) {
e.preventDefault();
fileInput.click();
});
fileInput.addEventListener('change', function () {
const fileName = fileInput.files[0].name;
button.querySelector('div').textContent = fileName;
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #99 v0.1 💙 CUSTOM FILE UPLOAD INPUT -->
<script>
document.addEventListener('DOMContentLoaded', function () {
const uploadButtons = document.querySelectorAll('[ms-code-file_uploader]');
uploadButtons.forEach(button => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.style.display = 'none';
fileInput.name = button.getAttribute('ms-code-file_uploader');
fileInput.accept = button.getAttribute('ms-code-file_types');
document.body.appendChild(fileInput);
button.addEventListener('click', function (e) {
e.preventDefault();
fileInput.click();
});
fileInput.addEventListener('change', function () {
const fileName = fileInput.files[0].name;
button.querySelector('div').textContent = fileName;
});
});
});
</script>
Mitglieder-Skripte
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.

#Nr. 138 - Anker Link Scroll Offset
Behebung des Problems mit Ankerlinks und klebrigen/fixierten Navigationsleisten in Webflow.
<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
// Disable Webflow's built-in smooth scrolling
var Webflow = Webflow || [];
Webflow.push(function() {
$(function() {
$(document).off('click.wf-scroll');
});
});
// Smooth scroll implementation with customizable settings
(function() {
// Customizable settings
const SCROLL_SETTINGS = {
duration: 1000, // in milliseconds
easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
};
const EASING_FUNCTIONS = {
linear: t => t,
easeInQuad: t => t * t,
easeOutQuad: t => t * (2 - t),
easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
easeInCubic: t => t * t * t,
easeOutCubic: t => (--t) * t * t + 1,
easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
};
function getOffset() {
const navbar = document.querySelector('[ms-code-scroll-offset]');
if (!navbar) return 0;
const navbarHeight = navbar.offsetHeight;
const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
return navbarHeight + customOffset;
}
function smoothScroll(target) {
const startPosition = window.pageYOffset;
const offset = getOffset();
const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
const distance = targetPosition - startPosition;
let startTime = null;
function animation(currentTime) {
if (startTime === null) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
window.scrollTo(0, startPosition + distance * easeProgress);
if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
}
requestAnimationFrame(animation);
}
function handleClick(e) {
const href = e.currentTarget.getAttribute('href');
if (href.startsWith('#')) {
e.preventDefault();
const target = document.getElementById(href.slice(1));
if (target) smoothScroll(target);
}
}
function handleHashChange() {
if (window.location.hash) {
const target = document.getElementById(window.location.hash.slice(1));
if (target) {
setTimeout(() => smoothScroll(target), 0);
}
}
}
function init() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', handleClick);
});
window.addEventListener('hashchange', handleHashChange);
handleHashChange(); // Handle initial hash on page load
}
document.addEventListener('DOMContentLoaded', init);
window.Webflow && window.Webflow.push(init);
})();
</script>
<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
// Disable Webflow's built-in smooth scrolling
var Webflow = Webflow || [];
Webflow.push(function() {
$(function() {
$(document).off('click.wf-scroll');
});
});
// Smooth scroll implementation with customizable settings
(function() {
// Customizable settings
const SCROLL_SETTINGS = {
duration: 1000, // in milliseconds
easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
};
const EASING_FUNCTIONS = {
linear: t => t,
easeInQuad: t => t * t,
easeOutQuad: t => t * (2 - t),
easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
easeInCubic: t => t * t * t,
easeOutCubic: t => (--t) * t * t + 1,
easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
};
function getOffset() {
const navbar = document.querySelector('[ms-code-scroll-offset]');
if (!navbar) return 0;
const navbarHeight = navbar.offsetHeight;
const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
return navbarHeight + customOffset;
}
function smoothScroll(target) {
const startPosition = window.pageYOffset;
const offset = getOffset();
const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
const distance = targetPosition - startPosition;
let startTime = null;
function animation(currentTime) {
if (startTime === null) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
window.scrollTo(0, startPosition + distance * easeProgress);
if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
}
requestAnimationFrame(animation);
}
function handleClick(e) {
const href = e.currentTarget.getAttribute('href');
if (href.startsWith('#')) {
e.preventDefault();
const target = document.getElementById(href.slice(1));
if (target) smoothScroll(target);
}
}
function handleHashChange() {
if (window.location.hash) {
const target = document.getElementById(window.location.hash.slice(1));
if (target) {
setTimeout(() => smoothScroll(target), 0);
}
}
}
function init() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', handleClick);
});
window.addEventListener('hashchange', handleHashChange);
handleHashChange(); // Handle initial hash on page load
}
document.addEventListener('DOMContentLoaded', init);
window.Webflow && window.Webflow.push(init);
})();
</script>
.avif)
#Nr. 137 - Anzeige des Namens des Besucherlandes
Ersetzen Sie den Text durch das Land, in dem sich ein Benutzer auf der Grundlage seiner IP-Adresse befindet.
<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
document.addEventListener('DOMContentLoaded', function() {
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
if (data.country_name) {
const countryElements = document.querySelectorAll('[ms-code-display-country]');
countryElements.forEach(element => {
element.textContent = data.country_name;
});
}
})
.catch(error => {
console.error('Error fetching country:', error);
});
});
</script>
<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
document.addEventListener('DOMContentLoaded', function() {
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
if (data.country_name) {
const countryElements = document.querySelectorAll('[ms-code-display-country]');
countryElements.forEach(element => {
element.textContent = data.country_name;
});
}
})
.catch(error => {
console.error('Error fetching country:', error);
});
});
</script>

#Nr. 136 - Abschnittspfad aus URL entfernen
Wenn zu einem Abschnitt navigiert wird, wird der Pfad des Ankerlinks entfernt.
<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Check if there's a hash in the URL
if (window.location.hash) {
// Get the target element
const targetId = window.location.hash.substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
// Scroll to the target element
targetElement.scrollIntoView({behavior: 'smooth'});
// Remove the hash after a short delay (to allow scrolling to complete)
setTimeout(function() {
history.pushState("", document.title, window.location.pathname + window.location.search);
}, 100);
}
}
// Add click event listeners to all internal links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
targetElement.scrollIntoView({behavior: 'smooth'});
// Remove the hash after a short delay (to allow scrolling to complete)
setTimeout(function() {
history.pushState("", document.title, window.location.pathname + window.location.search);
}, 100);
}
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Check if there's a hash in the URL
if (window.location.hash) {
// Get the target element
const targetId = window.location.hash.substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
// Scroll to the target element
targetElement.scrollIntoView({behavior: 'smooth'});
// Remove the hash after a short delay (to allow scrolling to complete)
setTimeout(function() {
history.pushState("", document.title, window.location.pathname + window.location.search);
}, 100);
}
}
// Add click event listeners to all internal links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
targetElement.scrollIntoView({behavior: 'smooth'});
// Remove the hash after a short delay (to allow scrolling to complete)
setTimeout(function() {
history.pushState("", document.title, window.location.pathname + window.location.search);
}, 100);
}
});
});
});
</script>

#Nr. 135 - Umleitung auf Basis eines ausgewählten Wertes
Legen Sie die Umleitung des Formulars dynamisch auf der Grundlage der Benutzerauswahl fest.
<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
(function() {
'use strict';
function initDropdownRedirect() {
const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
if (!dropdown) return;
const form = dropdown.closest('form');
if (!form) return;
function updateRedirect() {
const selectedValue = dropdown.value;
form.setAttribute('redirect', selectedValue);
form.setAttribute('data-redirect', selectedValue);
}
function handleSubmit(event) {
event.preventDefault();
const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
if (redirectUrl) {
setTimeout(() => {
window.location.href = redirectUrl;
}, 500); // Delay redirect by 500 milliseconds
} else {
form.submit(); // Fall back to normal form submission if no redirect is set
}
}
dropdown.addEventListener('change', updateRedirect);
form.addEventListener('submit', handleSubmit);
// Initialize redirect on page load
updateRedirect();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDropdownRedirect);
} else {
initDropdownRedirect();
}
})();
</script>
<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
(function() {
'use strict';
function initDropdownRedirect() {
const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
if (!dropdown) return;
const form = dropdown.closest('form');
if (!form) return;
function updateRedirect() {
const selectedValue = dropdown.value;
form.setAttribute('redirect', selectedValue);
form.setAttribute('data-redirect', selectedValue);
}
function handleSubmit(event) {
event.preventDefault();
const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
if (redirectUrl) {
setTimeout(() => {
window.location.href = redirectUrl;
}, 500); // Delay redirect by 500 milliseconds
} else {
form.submit(); // Fall back to normal form submission if no redirect is set
}
}
dropdown.addEventListener('change', updateRedirect);
form.addEventListener('submit', handleSubmit);
// Initialize redirect on page load
updateRedirect();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDropdownRedirect);
} else {
initDropdownRedirect();
}
})();
</script>

#Nr. 134 - Nach oben scrollen bei Tabwechsel
Wenn die Registerkarte gewechselt wird, blättert die Seite an den Anfang des Registerkartenbereichs.
<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Select all tab containers with the ms-code-tab-scroll-top attribute
const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');
tabContainers.forEach(container => {
const tabLinks = container.querySelectorAll('.w-tab-link');
const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');
tabLinks.forEach(link => {
link.addEventListener('click', function(e) {
// Small delay to ensure the new tab content is rendered
setTimeout(() => {
// Find the active tab pane within this container
const activePane = container.querySelector('.w-tab-pane.w--tab-active');
if (activePane) {
// Calculate the new scroll position
const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;
// Scroll to the new position
window.scrollTo({
top: newScrollPosition,
behavior: 'smooth'
});
}
}, 50); // 50ms delay, adjust if needed
});
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Select all tab containers with the ms-code-tab-scroll-top attribute
const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');
tabContainers.forEach(container => {
const tabLinks = container.querySelectorAll('.w-tab-link');
const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');
tabLinks.forEach(link => {
link.addEventListener('click', function(e) {
// Small delay to ensure the new tab content is rendered
setTimeout(() => {
// Find the active tab pane within this container
const activePane = container.querySelector('.w-tab-pane.w--tab-active');
if (activePane) {
// Calculate the new scroll position
const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;
// Scroll to the new position
window.scrollTo({
top: newScrollPosition,
behavior: 'smooth'
});
}
}, 50); // 50ms delay, adjust if needed
});
});
});
});
</script>

#Nr. 133 - Automatisches Wasserzeichen für Bilder
Fügen Sie den Bildern auf Ihrer Webflow-Website ganz einfach ein Wasserzeichen hinzu.
<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
function addWatermarkToImages() {
const images = document.querySelectorAll('img[ms-code-watermark]');
images.forEach(img => {
img.crossOrigin = "Anonymous"; // This allows us to work with images from other domains
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size to match the image
canvas.width = img.width;
canvas.height = img.height;
// Draw the original image onto the canvas
ctx.drawImage(img, 0, 0, img.width, img.height);
// Get watermark text from attribute
const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';
// Add watermark
ctx.font = `${img.width / 20}px Arial`; // Adjust font size based on image width
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Rotate and draw the watermark text
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 4); // Rotate 45 degrees
ctx.fillText(watermarkText, 0, 0);
ctx.restore();
// Preserve the original image's classes and other attributes
canvas.className = img.className;
for (let i = 0; i < img.attributes.length; i++) {
const attr = img.attributes[i];
if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
canvas.setAttribute(attr.name, attr.value);
}
}
// Replace the original image with the watermarked canvas
img.parentNode.replaceChild(canvas, img);
};
// Trigger onload event (in case the image is already loaded)
if (img.complete) {
img.onload();
}
});
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', addWatermarkToImages);
</script>
<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
function addWatermarkToImages() {
const images = document.querySelectorAll('img[ms-code-watermark]');
images.forEach(img => {
img.crossOrigin = "Anonymous"; // This allows us to work with images from other domains
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size to match the image
canvas.width = img.width;
canvas.height = img.height;
// Draw the original image onto the canvas
ctx.drawImage(img, 0, 0, img.width, img.height);
// Get watermark text from attribute
const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';
// Add watermark
ctx.font = `${img.width / 20}px Arial`; // Adjust font size based on image width
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Rotate and draw the watermark text
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 4); // Rotate 45 degrees
ctx.fillText(watermarkText, 0, 0);
ctx.restore();
// Preserve the original image's classes and other attributes
canvas.className = img.className;
for (let i = 0; i < img.attributes.length; i++) {
const attr = img.attributes[i];
if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
canvas.setAttribute(attr.name, attr.value);
}
}
// Replace the original image with the watermarked canvas
img.parentNode.replaceChild(canvas, img);
};
// Trigger onload event (in case the image is already loaded)
if (img.complete) {
img.onload();
}
});
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', addWatermarkToImages);
</script>

#Nr. 132 - Elemente mit der Escape-Taste ausblenden
Fügen Sie ein Attribut hinzu, und wenn die Taste esc angeklickt wird, wird das Element auf "none" gesetzt.
<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
document.addEventListener('keydown', function(event) {
// Check if the pressed key is ESC (key code 27)
if (event.key === 'Escape' || event.keyCode === 27) {
// Find all elements with the attribute ms-code-close-esc
const elements = document.querySelectorAll('[ms-code-close-esc]');
// Loop through the elements and set their display to 'none'
elements.forEach(function(element) {
element.style.display = 'none';
});
}
});
</script>
<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
document.addEventListener('keydown', function(event) {
// Check if the pressed key is ESC (key code 27)
if (event.key === 'Escape' || event.keyCode === 27) {
// Find all elements with the attribute ms-code-close-esc
const elements = document.querySelectorAll('[ms-code-close-esc]');
// Loop through the elements and set their display to 'none'
elements.forEach(function(element) {
element.style.display = 'none';
});
}
});
</script>

#Nr. 131 - Addition von Zahleneingaben
Nehmen Sie den Wert von Zahleneingaben und zeigen Sie ihn in einem Eingabewert oder in einem Textbereich an.
<!-- 💙 MEMBERSCRIPT #131 v0.1 💙 - CALCULATE NUMBER INPUTS -->
<script>
// Function to initialize the counter functionality
function initializeCounter() {
const counters = {};
// Find all elements with ms-code-show-number attribute
document.querySelectorAll('[ms-code-show-number]').forEach(el => {
const counterName = el.getAttribute('ms-code-show-number');
if (!counters[counterName]) {
counters[counterName] = { total: 0, displays: [] };
}
counters[counterName].displays.push(el);
});
// Find all input elements with ms-code-add-number attribute
document.querySelectorAll('input[ms-code-add-number]').forEach(input => {
const counterName = input.getAttribute('ms-code-add-number');
if (counters[counterName]) {
input.addEventListener('input', updateCounter);
}
});
// Function to update counter when input changes
function updateCounter(event) {
const input = event.target;
const counterName = input.getAttribute('ms-code-add-number');
const counter = counters[counterName];
if (counter) {
counter.total = 0;
document.querySelectorAll(`input[ms-code-add-number="${counterName}"]`).forEach(input => {
counter.total += parseInt(input.value) || 0;
});
counter.displays.forEach(display => {
if (display.tagName === 'INPUT') {
display.value = counter.total;
} else {
display.textContent = counter.total;
}
});
}
}
// Initial update for all counters
Object.keys(counters).forEach(counterName => {
const input = document.querySelector(`input[ms-code-add-number="${counterName}"]`);
if (input) {
input.dispatchEvent(new Event('input'));
}
});
}
// Run the initialization when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initializeCounter);
</script>
<!-- 💙 MEMBERSCRIPT #131 v0.1 💙 - CALCULATE NUMBER INPUTS -->
<script>
// Function to initialize the counter functionality
function initializeCounter() {
const counters = {};
// Find all elements with ms-code-show-number attribute
document.querySelectorAll('[ms-code-show-number]').forEach(el => {
const counterName = el.getAttribute('ms-code-show-number');
if (!counters[counterName]) {
counters[counterName] = { total: 0, displays: [] };
}
counters[counterName].displays.push(el);
});
// Find all input elements with ms-code-add-number attribute
document.querySelectorAll('input[ms-code-add-number]').forEach(input => {
const counterName = input.getAttribute('ms-code-add-number');
if (counters[counterName]) {
input.addEventListener('input', updateCounter);
}
});
// Function to update counter when input changes
function updateCounter(event) {
const input = event.target;
const counterName = input.getAttribute('ms-code-add-number');
const counter = counters[counterName];
if (counter) {
counter.total = 0;
document.querySelectorAll(`input[ms-code-add-number="${counterName}"]`).forEach(input => {
counter.total += parseInt(input.value) || 0;
});
counter.displays.forEach(display => {
if (display.tagName === 'INPUT') {
display.value = counter.total;
} else {
display.textContent = counter.total;
}
});
}
}
// Initial update for all counters
Object.keys(counters).forEach(counterName => {
const input = document.querySelector(`input[ms-code-add-number="${counterName}"]`);
if (input) {
input.dispatchEvent(new Event('input'));
}
});
}
// Run the initialization when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initializeCounter);
</script>

#130 - Formular automatisch absenden, wenn sich alle Eingaben ändern
Überspringen Sie die Schaltfläche "Senden" und senden Sie das Formular, wenn sich alle Eingaben geändert haben.
<!-- 💙 MEMBERSCRIPT #130 v0.1 💙 - AUTO SUBMIT FORMS FROM INPUT CHANGE -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form[ms-code-auto-submit]');
forms.forEach(form => {
const fields = form.querySelectorAll('input:not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea');
const fieldStates = new Map(Array.from(fields).map(field => [field, false]));
function updateFieldState(field, checkImmediately = false) {
switch (field.type) {
case 'checkbox':
fieldStates.set(field, true); // Considered interacted with once changed
break;
case 'radio':
const radioGroup = form.querySelectorAll(`input[type="radio"][name="${field.name}"]`);
radioGroup.forEach(radio => fieldStates.set(radio, true));
break;
case 'select-one':
case 'select-multiple':
fieldStates.set(field, field.value !== '');
break;
case 'file':
fieldStates.set(field, field.files.length > 0);
break;
case 'hidden':
fieldStates.set(field, true); // Always consider hidden fields as filled
break;
default:
// For text inputs, only update on blur or if checkImmediately is true
if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
if (checkImmediately || !field.dataset.blurred) {
fieldStates.set(field, field.value.trim() !== '');
}
} else {
fieldStates.set(field, field.value.trim() !== '');
}
}
if (checkImmediately) {
checkAndSubmit();
}
}
function checkAndSubmit() {
if (Array.from(fieldStates.values()).every(state => state)) {
// Create and dispatch a submit event
const submitEvent = new Event('submit', {
bubbles: true,
cancelable: true
});
const submitted = form.dispatchEvent(submitEvent);
// If the event wasn't prevented, manually submit the form
if (submitted) {
form.submit();
}
}
}
fields.forEach(field => {
// Use 'change' event for checkboxes, radios, file inputs, and selects
if (['checkbox', 'radio', 'file', 'select-one', 'select-multiple'].includes(field.type) || field.tagName === 'SELECT') {
field.addEventListener('change', () => updateFieldState(field, true));
}
// For text-like inputs, use 'blur' event
if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
field.addEventListener('blur', () => {
field.dataset.blurred = 'true';
updateFieldState(field, true);
});
// Also check on input, but don't submit immediately
field.addEventListener('input', () => updateFieldState(field, false));
}
});
// Initial check for pre-filled fields (e.g., browser autofill)
fields.forEach(field => updateFieldState(field, false));
checkAndSubmit();
});
});
</script>
<!-- 💙 MEMBERSCRIPT #130 v0.1 💙 - AUTO SUBMIT FORMS FROM INPUT CHANGE -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form[ms-code-auto-submit]');
forms.forEach(form => {
const fields = form.querySelectorAll('input:not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea');
const fieldStates = new Map(Array.from(fields).map(field => [field, false]));
function updateFieldState(field, checkImmediately = false) {
switch (field.type) {
case 'checkbox':
fieldStates.set(field, true); // Considered interacted with once changed
break;
case 'radio':
const radioGroup = form.querySelectorAll(`input[type="radio"][name="${field.name}"]`);
radioGroup.forEach(radio => fieldStates.set(radio, true));
break;
case 'select-one':
case 'select-multiple':
fieldStates.set(field, field.value !== '');
break;
case 'file':
fieldStates.set(field, field.files.length > 0);
break;
case 'hidden':
fieldStates.set(field, true); // Always consider hidden fields as filled
break;
default:
// For text inputs, only update on blur or if checkImmediately is true
if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
if (checkImmediately || !field.dataset.blurred) {
fieldStates.set(field, field.value.trim() !== '');
}
} else {
fieldStates.set(field, field.value.trim() !== '');
}
}
if (checkImmediately) {
checkAndSubmit();
}
}
function checkAndSubmit() {
if (Array.from(fieldStates.values()).every(state => state)) {
// Create and dispatch a submit event
const submitEvent = new Event('submit', {
bubbles: true,
cancelable: true
});
const submitted = form.dispatchEvent(submitEvent);
// If the event wasn't prevented, manually submit the form
if (submitted) {
form.submit();
}
}
}
fields.forEach(field => {
// Use 'change' event for checkboxes, radios, file inputs, and selects
if (['checkbox', 'radio', 'file', 'select-one', 'select-multiple'].includes(field.type) || field.tagName === 'SELECT') {
field.addEventListener('change', () => updateFieldState(field, true));
}
// For text-like inputs, use 'blur' event
if (field.type === 'text' || field.type === 'password' || field.type === 'email' || field.type === 'tel' || field.type === 'url' || field.tagName === 'TEXTAREA') {
field.addEventListener('blur', () => {
field.dataset.blurred = 'true';
updateFieldState(field, true);
});
// Also check on input, but don't submit immediately
field.addEventListener('input', () => updateFieldState(field, false));
}
});
// Initial check for pre-filled fields (e.g., browser autofill)
fields.forEach(field => updateFieldState(field, false));
checkAndSubmit();
});
});
</script>

#Nr. 129 - Country Gating
Sperren Sie den Zugriff auf Ihre Website für Besucher, die sich in einem der verbotenen Länder befinden.
<!-- 💙 MEMBERSCRIPT #129 v0.1 💙 - COUNTRY GATING -->
<script>
// Configuration
const ACCESS_DENIED_PAGE = '/access-denied';
// List of disallowed countries using ISO 3166-1 alpha-2 country codes
const DISALLOWED_COUNTRIES = [
// "US", // United States
// "CN", // China
// "RU", // Russia
// "IN", // India
// "JP", // Japan
// "DE", // Germany
// "GB", // United Kingdom
// "FR", // France
// "BR", // Brazil
// "IT", // Italy
// Add more countries as needed
];
// Function to get visitor's country and check access
function checkCountryAccess() {
// Check if we're already on the access denied page
if (window.location.pathname === ACCESS_DENIED_PAGE) {
return; // Don't redirect if already on the access denied page
}
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
const visitorCountry = data.country_code; // This returns the ISO 3166-1 alpha-2 country code
if (DISALLOWED_COUNTRIES.includes(visitorCountry)) {
window.location.href = ACCESS_DENIED_PAGE;
}
})
.catch(error => {
console.error('Error fetching IP data:', error);
});
}
// Run the check when the page loads
document.addEventListener('DOMContentLoaded', checkCountryAccess);
</script>
<!-- 💙 MEMBERSCRIPT #129 v0.1 💙 - COUNTRY GATING -->
<script>
// Configuration
const ACCESS_DENIED_PAGE = '/access-denied';
// List of disallowed countries using ISO 3166-1 alpha-2 country codes
const DISALLOWED_COUNTRIES = [
// "US", // United States
// "CN", // China
// "RU", // Russia
// "IN", // India
// "JP", // Japan
// "DE", // Germany
// "GB", // United Kingdom
// "FR", // France
// "BR", // Brazil
// "IT", // Italy
// Add more countries as needed
];
// Function to get visitor's country and check access
function checkCountryAccess() {
// Check if we're already on the access denied page
if (window.location.pathname === ACCESS_DENIED_PAGE) {
return; // Don't redirect if already on the access denied page
}
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
const visitorCountry = data.country_code; // This returns the ISO 3166-1 alpha-2 country code
if (DISALLOWED_COUNTRIES.includes(visitorCountry)) {
window.location.href = ACCESS_DENIED_PAGE;
}
})
.catch(error => {
console.error('Error fetching IP data:', error);
});
}
// Run the check when the page loads
document.addEventListener('DOMContentLoaded', checkCountryAccess);
</script>

#128 - Elemente ausblenden, nachdem sie gesehen wurden
Fügen Sie ein Attribut hinzu, und Ihre Besucher werden dieses Element nur einmal sehen. Nach der Aktualisierung ist es verschwunden.
<!-- 💙 MEMBERSCRIPT #128 💙 - ONLY SHOW ELEMENT ONCE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Find all elements with the ms-code-show-once attribute
const elements = document.querySelectorAll('[ms-code-show-once]');
elements.forEach(element => {
const identifier = element.getAttribute('ms-code-show-once');
const storageKey = `ms-code-shown-${identifier}`;
// Check if the element has been seen before
if (localStorage.getItem(storageKey) !== 'true') {
// If not seen, show the element
element.style.display = 'block';
// Mark it as seen in localStorage
localStorage.setItem(storageKey, 'true');
} else {
// If already seen, hide the element
element.style.display = 'none';
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #128 💙 - ONLY SHOW ELEMENT ONCE -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Find all elements with the ms-code-show-once attribute
const elements = document.querySelectorAll('[ms-code-show-once]');
elements.forEach(element => {
const identifier = element.getAttribute('ms-code-show-once');
const storageKey = `ms-code-shown-${identifier}`;
// Check if the element has been seen before
if (localStorage.getItem(storageKey) !== 'true') {
// If not seen, show the element
element.style.display = 'block';
// Mark it as seen in localStorage
localStorage.setItem(storageKey, 'true');
} else {
// If already seen, hide the element
element.style.display = 'none';
}
});
});
</script>

#Nr. 127 - Texteingaben validieren
Überprüfen Sie Texteingaben anhand einer beliebigen Liste von Zeichenfolgen, einschließlich Wildcards!
<!-- 💙 MEMBERSCRIPT #127 v0.1 💙 - TEXT INPUT VALIDATION -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Debounce function
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Find all fields with ms-code-require attribute
const fields = document.querySelectorAll('[ms-code-require]');
fields.forEach(field => {
// Get the error element for this field
const errorElement = document.querySelector(`[ms-code-require-error="${field.getAttribute('ms-code-require')}"]`);
// Hide error message initially
if (errorElement) {
errorElement.style.display = 'none';
}
// Get the form containing the field
const form = field.closest('form');
// Get the submit button
const submitButton = form ? form.querySelector(`[ms-code-submit-button="${field.getAttribute('ms-code-require')}"]`) : null;
// Get the require-list attribute value
const requireList = field.getAttribute('ms-code-require-list');
if (requireList) {
// Convert the require-list to an array of regex patterns
const patterns = requireList.split(',').map(pattern => {
return pattern.replace(/\{([^}]+)\}/g, (match, p1) => {
return p1.split('').map(char => {
switch(char) {
case '0': return '\\d';
case 'A': return '[A-Z]';
case 'a': return '[a-z]';
default: return char;
}
}).join('');
});
});
// Validate function
function validateField() {
const value = field.value;
const isValid = patterns.some(pattern => new RegExp(`^${pattern}$`).test(value));
if (errorElement) {
errorElement.style.display = isValid ? 'none' : 'block';
}
if (submitButton) {
submitButton.style.opacity = isValid ? '1' : '0.5';
submitButton.style.pointerEvents = isValid ? 'auto' : 'none';
}
return isValid;
}
// Debounced validate function
const debouncedValidate = debounce(validateField, 500);
// Add blur event listener
field.addEventListener('blur', validateField);
// Add input event listener for debounced validation
field.addEventListener('input', debouncedValidate);
// Handle form submission
if (form) {
form.addEventListener('submit', function(event) {
if (!validateField() && submitButton) {
event.preventDefault();
field.focus();
}
});
}
}
});
});
</script>
<!-- 💙 MEMBERSCRIPT #127 v0.1 💙 - TEXT INPUT VALIDATION -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Debounce function
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Find all fields with ms-code-require attribute
const fields = document.querySelectorAll('[ms-code-require]');
fields.forEach(field => {
// Get the error element for this field
const errorElement = document.querySelector(`[ms-code-require-error="${field.getAttribute('ms-code-require')}"]`);
// Hide error message initially
if (errorElement) {
errorElement.style.display = 'none';
}
// Get the form containing the field
const form = field.closest('form');
// Get the submit button
const submitButton = form ? form.querySelector(`[ms-code-submit-button="${field.getAttribute('ms-code-require')}"]`) : null;
// Get the require-list attribute value
const requireList = field.getAttribute('ms-code-require-list');
if (requireList) {
// Convert the require-list to an array of regex patterns
const patterns = requireList.split(',').map(pattern => {
return pattern.replace(/\{([^}]+)\}/g, (match, p1) => {
return p1.split('').map(char => {
switch(char) {
case '0': return '\\d';
case 'A': return '[A-Z]';
case 'a': return '[a-z]';
default: return char;
}
}).join('');
});
});
// Validate function
function validateField() {
const value = field.value;
const isValid = patterns.some(pattern => new RegExp(`^${pattern}$`).test(value));
if (errorElement) {
errorElement.style.display = isValid ? 'none' : 'block';
}
if (submitButton) {
submitButton.style.opacity = isValid ? '1' : '0.5';
submitButton.style.pointerEvents = isValid ? 'auto' : 'none';
}
return isValid;
}
// Debounced validate function
const debouncedValidate = debounce(validateField, 500);
// Add blur event listener
field.addEventListener('blur', validateField);
// Add input event listener for debounced validation
field.addEventListener('input', debouncedValidate);
// Handle form submission
if (form) {
form.addEventListener('submit', function(event) {
if (!validateField() && submitButton) {
event.preventDefault();
field.focus();
}
});
}
}
});
});
</script>

#Nr. 126 - Formular ohne Umleitung an Webhook senden
Senden Sie Daten an einen Webhook und behalten Sie das Standardverhalten des Webflow-Formulars bei.
<!-- 💙 MEMBERSCRIPT #126 v0.1 💙 - POST FORM DATA TO WEBHOOK WITHOUT REDIRECTING -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
// Select all forms with the ms-code-form-no-redirect attribute
const forms = document.querySelectorAll('form[ms-code-form-no-redirect]');
forms.forEach(form => {
// Select the success and error message elements for this form
const formWrapper = form.closest('.w-form');
const successMessage = formWrapper.querySelector('.w-form-done');
const errorMessage = formWrapper.querySelector('.w-form-fail');
// Add submit event listener to the form
form.addEventListener('submit', function(event) {
// Prevent the default form submission
event.preventDefault();
// Get the form data
const formData = new FormData(form);
// Get the submit button and set its text to the waiting message
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
const originalButtonText = submitButton.value || submitButton.textContent;
const waitingText = submitButton.getAttribute('data-wait') || 'Please wait...';
if (submitButton.tagName === 'INPUT') {
submitButton.value = waitingText;
} else {
submitButton.textContent = waitingText;
}
// Disable the submit button
submitButton.disabled = true;
// Send the form data to the form's action URL using fetch
fetch(form.action, {
method: 'POST',
body: formData
})
.then(response => {
if (response.ok) {
// If the submission was successful, show the success message
form.style.display = 'none';
successMessage.style.display = 'block';
errorMessage.style.display = 'none';
} else {
// If there was an error, show the error message
throw new Error('Form submission failed');
}
})
.catch(error => {
// If there was a network error or the submission failed, show the error message
console.error('Error:', error);
errorMessage.style.display = 'block';
successMessage.style.display = 'none';
})
.finally(() => {
// Reset the submit button text and re-enable it
if (submitButton.tagName === 'INPUT') {
submitButton.value = originalButtonText;
} else {
submitButton.textContent = originalButtonText;
}
submitButton.disabled = false;
});
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #126 v0.1 💙 - POST FORM DATA TO WEBHOOK WITHOUT REDIRECTING -->
<script>
// Wait for the DOM to be fully loaded
document.addEventListener('DOMContentLoaded', function() {
// Select all forms with the ms-code-form-no-redirect attribute
const forms = document.querySelectorAll('form[ms-code-form-no-redirect]');
forms.forEach(form => {
// Select the success and error message elements for this form
const formWrapper = form.closest('.w-form');
const successMessage = formWrapper.querySelector('.w-form-done');
const errorMessage = formWrapper.querySelector('.w-form-fail');
// Add submit event listener to the form
form.addEventListener('submit', function(event) {
// Prevent the default form submission
event.preventDefault();
// Get the form data
const formData = new FormData(form);
// Get the submit button and set its text to the waiting message
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
const originalButtonText = submitButton.value || submitButton.textContent;
const waitingText = submitButton.getAttribute('data-wait') || 'Please wait...';
if (submitButton.tagName === 'INPUT') {
submitButton.value = waitingText;
} else {
submitButton.textContent = waitingText;
}
// Disable the submit button
submitButton.disabled = true;
// Send the form data to the form's action URL using fetch
fetch(form.action, {
method: 'POST',
body: formData
})
.then(response => {
if (response.ok) {
// If the submission was successful, show the success message
form.style.display = 'none';
successMessage.style.display = 'block';
errorMessage.style.display = 'none';
} else {
// If there was an error, show the error message
throw new Error('Form submission failed');
}
})
.catch(error => {
// If there was a network error or the submission failed, show the error message
console.error('Error:', error);
errorMessage.style.display = 'block';
successMessage.style.display = 'none';
})
.finally(() => {
// Reset the submit button text and re-enable it
if (submitButton.tagName === 'INPUT') {
submitButton.value = originalButtonText;
} else {
submitButton.textContent = originalButtonText;
}
submitButton.disabled = false;
});
});
});
});
</script>

#Nr. 125 - Kontrolle des erforderlichen Zustands mit Kontrollkästchen
Formularfelder basierend auf dem Status eines Kontrollkästchens auf erforderlich/optional setzen.
<!-- 💙 MEMBERSCRIPT #125 v0.1 💙 - CHECKBOX TOGGLE REQUIRED ON FORM FIELDS -->
<script>
// Function to initialize the form field requirements
function initFormFieldRequirements() {
// Handle checkbox changes
document.querySelectorAll('[ms-code-req-if-checked], [ms-code-req-if-unchecked]').forEach(checkbox => {
checkbox.addEventListener('change', updateFieldRequirements);
// Initial update
updateFieldRequirements.call(checkbox);
});
}
// Function to update field requirements based on checkbox state
function updateFieldRequirements() {
const isChecked = this.checked;
const group = this.getAttribute('ms-code-req-if-checked') || this.getAttribute('ms-code-req-if-unchecked');
const ifChecked = this.hasAttribute('ms-code-req-if-checked');
const shouldBeRequired = ifChecked ? isChecked : !isChecked;
const shouldDisableIfNotReq = this.hasAttribute('ms-code-disable-if-not-req');
// Update associated input fields
document.querySelectorAll(`[ms-code-req-input="${group}"]`).forEach(input => {
input.required = shouldBeRequired;
updateInputStyle(input, shouldBeRequired, shouldDisableIfNotReq);
});
// Update associated labels
document.querySelectorAll(`[ms-code-req-label="${group}"]`).forEach(label => {
label.style.display = shouldBeRequired ? '' : 'none';
});
}
// Function to update input style based on required state and disable setting
function updateInputStyle(input, isRequired, shouldDisableIfNotReq) {
if (shouldDisableIfNotReq) {
input.style.opacity = isRequired ? '1' : '0.4';
input.style.pointerEvents = isRequired ? '' : 'none';
} else {
input.style.opacity = '';
input.style.pointerEvents = '';
}
}
// Initialize when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initFormFieldRequirements);
</script>
<!-- 💙 MEMBERSCRIPT #125 v0.1 💙 - CHECKBOX TOGGLE REQUIRED ON FORM FIELDS -->
<script>
// Function to initialize the form field requirements
function initFormFieldRequirements() {
// Handle checkbox changes
document.querySelectorAll('[ms-code-req-if-checked], [ms-code-req-if-unchecked]').forEach(checkbox => {
checkbox.addEventListener('change', updateFieldRequirements);
// Initial update
updateFieldRequirements.call(checkbox);
});
}
// Function to update field requirements based on checkbox state
function updateFieldRequirements() {
const isChecked = this.checked;
const group = this.getAttribute('ms-code-req-if-checked') || this.getAttribute('ms-code-req-if-unchecked');
const ifChecked = this.hasAttribute('ms-code-req-if-checked');
const shouldBeRequired = ifChecked ? isChecked : !isChecked;
const shouldDisableIfNotReq = this.hasAttribute('ms-code-disable-if-not-req');
// Update associated input fields
document.querySelectorAll(`[ms-code-req-input="${group}"]`).forEach(input => {
input.required = shouldBeRequired;
updateInputStyle(input, shouldBeRequired, shouldDisableIfNotReq);
});
// Update associated labels
document.querySelectorAll(`[ms-code-req-label="${group}"]`).forEach(label => {
label.style.display = shouldBeRequired ? '' : 'none';
});
}
// Function to update input style based on required state and disable setting
function updateInputStyle(input, isRequired, shouldDisableIfNotReq) {
if (shouldDisableIfNotReq) {
input.style.opacity = isRequired ? '1' : '0.4';
input.style.pointerEvents = isRequired ? '' : 'none';
} else {
input.style.opacity = '';
input.style.pointerEvents = '';
}
}
// Initialize when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initFormFieldRequirements);
</script>

#Nr. 124 - Sichtbarkeit von Objekten umschalten
Verwenden Sie benutzerdefinierte Schaltflächen, um Elemente ein- und auszublenden, wobei die Zustände im lokalen Speicher gespeichert werden.
<!-- 💙 MEMBERSCRIPT #124 v0.1 💙 - TOGGLE ITEM VISIBILITY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const STORAGE_KEY = 'ms-code-vis-states';
// Load saved states from local storage
let savedStates = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
// Find all unique group identifiers
const groups = new Set([...document.querySelectorAll('[ms-code-vis-item]')].map(el => el.getAttribute('ms-code-vis-item')));
groups.forEach(group => {
const item = document.querySelector(`[ms-code-vis-item="${group}"]`);
const showButton = document.querySelector(`[ms-code-vis-show="${group}"]`);
const hideButton = document.querySelector(`[ms-code-vis-hide="${group}"]`);
if (!item || !showButton || !hideButton) return;
// Function to update visibility
function updateVisibility(isVisible) {
if (isVisible) {
item.style.removeProperty('display');
showButton.style.display = 'none';
hideButton.style.removeProperty('display');
} else {
item.style.display = 'none';
showButton.style.removeProperty('display');
hideButton.style.display = 'none';
}
// Save state to local storage
savedStates[group] = isVisible;
localStorage.setItem(STORAGE_KEY, JSON.stringify(savedStates));
}
// Set initial visibility
const defaultState = item.getAttribute('ms-code-vis-default');
const savedState = savedStates[group];
if (savedState !== undefined) {
updateVisibility(savedState);
} else if (defaultState === 'hide') {
updateVisibility(false);
} else {
updateVisibility(true);
}
// Add click event listeners
showButton.addEventListener('click', function(e) {
e.preventDefault();
updateVisibility(true);
});
hideButton.addEventListener('click', function(e) {
e.preventDefault();
updateVisibility(false);
});
});
});
</script>
<!-- 💙 MEMBERSCRIPT #124 v0.1 💙 - TOGGLE ITEM VISIBILITY -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const STORAGE_KEY = 'ms-code-vis-states';
// Load saved states from local storage
let savedStates = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
// Find all unique group identifiers
const groups = new Set([...document.querySelectorAll('[ms-code-vis-item]')].map(el => el.getAttribute('ms-code-vis-item')));
groups.forEach(group => {
const item = document.querySelector(`[ms-code-vis-item="${group}"]`);
const showButton = document.querySelector(`[ms-code-vis-show="${group}"]`);
const hideButton = document.querySelector(`[ms-code-vis-hide="${group}"]`);
if (!item || !showButton || !hideButton) return;
// Function to update visibility
function updateVisibility(isVisible) {
if (isVisible) {
item.style.removeProperty('display');
showButton.style.display = 'none';
hideButton.style.removeProperty('display');
} else {
item.style.display = 'none';
showButton.style.removeProperty('display');
hideButton.style.display = 'none';
}
// Save state to local storage
savedStates[group] = isVisible;
localStorage.setItem(STORAGE_KEY, JSON.stringify(savedStates));
}
// Set initial visibility
const defaultState = item.getAttribute('ms-code-vis-default');
const savedState = savedStates[group];
if (savedState !== undefined) {
updateVisibility(savedState);
} else if (defaultState === 'hide') {
updateVisibility(false);
} else {
updateVisibility(true);
}
// Add click event listeners
showButton.addEventListener('click', function(e) {
e.preventDefault();
updateVisibility(true);
});
hideButton.addEventListener('click', function(e) {
e.preventDefault();
updateVisibility(false);
});
});
});
</script>

#Nr. 123 - Ein Hörspiel nach dem anderen
Halten Sie automatisch alle anderen Audioplayer an, wenn ein Benutzer auf die Wiedergabe eines Audioplayers klickt.
<!-- 💙 MEMBERSCRIPT #123 💙 - ONE AUDIO AT A TIME -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const pauseOthers = current =>
document.querySelectorAll('audio, video').forEach(el => el !== current && !el.paused && el.pause());
const addPlayListener = el => el.addEventListener('play', e => pauseOthers(e.target));
new MutationObserver(mutations =>
mutations.forEach(m => m.addedNodes.forEach(n =>
(n.nodeName === 'AUDIO' || n.nodeName === 'VIDEO') && addPlayListener(n)
))
).observe(document.body, { childList: true, subtree: true });
document.querySelectorAll('audio, video').forEach(addPlayListener);
});
</script>
<!-- 💙 MEMBERSCRIPT #123 💙 - ONE AUDIO AT A TIME -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const pauseOthers = current =>
document.querySelectorAll('audio, video').forEach(el => el !== current && !el.paused && el.pause());
const addPlayListener = el => el.addEventListener('play', e => pauseOthers(e.target));
new MutationObserver(mutations =>
mutations.forEach(m => m.addedNodes.forEach(n =>
(n.nodeName === 'AUDIO' || n.nodeName === 'VIDEO') && addPlayListener(n)
))
).observe(document.body, { childList: true, subtree: true });
document.querySelectorAll('audio, video').forEach(addPlayListener);
});
</script>

#122 - Externe Links in neuem Tab öffnen
Externe Links werden automatisch in einer neuen Registerkarte geöffnet, und die Attribute nofollow und noreferrer werden hinzugefügt.
<!-- 💙 MEMBERSCRIPT #122 💙 - OPEN EXTERNAL LINKS IN NEW TAB -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const thisDomain = location.hostname;
const externalSelector = `a:not([href*="${thisDomain}"]):not([href^="/"]):not([href^="#"]):not([ms-code-ext-link="ignore"])`;
document.querySelectorAll(externalSelector).forEach(link => {
link.target = '_blank'; // Open in new tab (comment out to disable)
link.rel = (link.rel ? link.rel + ' ' : '') + 'noreferrer'; // Add noreferrer (comment out to disable)
link.rel = (link.rel ? link.rel + ' ' : '') + 'nofollow'; // Add nofollow (comment out to disable)
});
});
</script>
<!-- 💙 MEMBERSCRIPT #122 💙 - OPEN EXTERNAL LINKS IN NEW TAB -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const thisDomain = location.hostname;
const externalSelector = `a:not([href*="${thisDomain}"]):not([href^="/"]):not([href^="#"]):not([ms-code-ext-link="ignore"])`;
document.querySelectorAll(externalSelector).forEach(link => {
link.target = '_blank'; // Open in new tab (comment out to disable)
link.rel = (link.rel ? link.rel + ' ' : '') + 'noreferrer'; // Add noreferrer (comment out to disable)
link.rel = (link.rel ? link.rel + ' ' : '') + 'nofollow'; // Add nofollow (comment out to disable)
});
});
</script>

#Nr. 121 - Array aus benutzerdefiniertem Feld rendern
Zeigen Sie Ihren Mitgliedern in Webflow eine beliebige kommagetrennte Liste an.
<!-- 💙 MEMBERSCRIPT #121 v0.1 💙 - RENDER ARRAY FROM CUSTOM FIELD -->
<script>
// Function to render arrays from Memberstack custom fields
function renderMemberstackArrays() {
// Get all elements with the ms-code-render-array attribute
const arrayElements = document.querySelectorAll('[ms-code-render-array]');
arrayElements.forEach(element => {
const customFieldKey = element.getAttribute('ms-code-render-array');
// Get Memberstack data from localStorage
const memberData = JSON.parse(localStorage.getItem('_ms-mem'));
if (!memberData || !memberData.customFields || !memberData.customFields[customFieldKey]) {
// If no data found, remove the element
element.remove();
return;
}
const arrayString = memberData.customFields[customFieldKey];
// Convert string to array, trim whitespace
const arrayItems = arrayString.split(',').map(item => item.trim()).filter(item => item !== '');
if (arrayItems.length === 0) {
// If array is empty, remove the element
element.remove();
return;
}
// Store the parent element
const parentElement = element.parentNode;
// Clone the template
const templateItem = element.cloneNode(true);
// Remove the ms-code-render-array attribute from the template
templateItem.removeAttribute('ms-code-render-array');
// Create a document fragment to hold the new items
const fragment = document.createDocumentFragment();
// Render array items
arrayItems.forEach(item => {
const newItem = templateItem.cloneNode(true);
// Replace the content of the new item
replaceContent(newItem, item);
fragment.appendChild(newItem);
});
// Replace the original element with the new items
parentElement.replaceChild(fragment, element);
});
}
// Helper function to replace content in the element
function replaceContent(element, newContent) {
if (element.childNodes.length > 0) {
element.childNodes.forEach(child => {
if (child.nodeType === Node.ELEMENT_NODE) {
replaceContent(child, newContent);
} else if (child.nodeType === Node.TEXT_NODE) {
child.textContent = newContent;
}
});
} else {
element.textContent = newContent;
}
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', renderMemberstackArrays);
</script>
<!-- 💙 MEMBERSCRIPT #121 v0.1 💙 - RENDER ARRAY FROM CUSTOM FIELD -->
<script>
// Function to render arrays from Memberstack custom fields
function renderMemberstackArrays() {
// Get all elements with the ms-code-render-array attribute
const arrayElements = document.querySelectorAll('[ms-code-render-array]');
arrayElements.forEach(element => {
const customFieldKey = element.getAttribute('ms-code-render-array');
// Get Memberstack data from localStorage
const memberData = JSON.parse(localStorage.getItem('_ms-mem'));
if (!memberData || !memberData.customFields || !memberData.customFields[customFieldKey]) {
// If no data found, remove the element
element.remove();
return;
}
const arrayString = memberData.customFields[customFieldKey];
// Convert string to array, trim whitespace
const arrayItems = arrayString.split(',').map(item => item.trim()).filter(item => item !== '');
if (arrayItems.length === 0) {
// If array is empty, remove the element
element.remove();
return;
}
// Store the parent element
const parentElement = element.parentNode;
// Clone the template
const templateItem = element.cloneNode(true);
// Remove the ms-code-render-array attribute from the template
templateItem.removeAttribute('ms-code-render-array');
// Create a document fragment to hold the new items
const fragment = document.createDocumentFragment();
// Render array items
arrayItems.forEach(item => {
const newItem = templateItem.cloneNode(true);
// Replace the content of the new item
replaceContent(newItem, item);
fragment.appendChild(newItem);
});
// Replace the original element with the new items
parentElement.replaceChild(fragment, element);
});
}
// Helper function to replace content in the element
function replaceContent(element, newContent) {
if (element.childNodes.length > 0) {
element.childNodes.forEach(child => {
if (child.nodeType === Node.ELEMENT_NODE) {
replaceContent(child, newContent);
} else if (child.nodeType === Node.TEXT_NODE) {
child.textContent = newContent;
}
});
} else {
element.textContent = newContent;
}
}
// Run the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', renderMemberstackArrays);
</script>

#120 - Element auf Basis von Gerät, Betriebssystem oder Browser ein-/ausblenden
Bedingte Sichtbarkeit, je nachdem, welches Gerät, Betriebssystem oder welchen Browser der Besucher verwendet.
<!-- 💙 MEMBERSCRIPT #120 v0.1 💙 - DEVICE/OS/BROWSER CONDITIONAL VISIBILITY -->
<script>
// Define device types, operating systems, and browsers with their detection methods
const detectionTypes = {
// Device types
mobile: () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
tablet: () => /iPad|Android|Silk/i.test(navigator.userAgent) && !/Mobile/i.test(navigator.userAgent),
desktop: () => !(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)),
touchdevice: () => 'ontouchstart' in window || navigator.maxTouchPoints > 0,
landscape: () => window.matchMedia("(orientation: landscape)").matches,
portrait: () => window.matchMedia("(orientation: portrait)").matches,
// Operating Systems
ios: () => /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
android: () => /Android/.test(navigator.userAgent),
macos: () => /Mac OS X/.test(navigator.userAgent) && !(/iPad|iPhone|iPod/.test(navigator.userAgent)),
windows: () => /Win/.test(navigator.platform),
linux: () => /Linux/.test(navigator.platform),
// Browsers
chrome: () => /Chrome/.test(navigator.userAgent) && !/Chromium/.test(navigator.userAgent),
firefox: () => /Firefox/.test(navigator.userAgent),
safari: () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
edge: () => /Edg/.test(navigator.userAgent),
opera: () => /OPR/.test(navigator.userAgent) || /Opera/.test(navigator.userAgent),
ie: () => /Trident/.test(navigator.userAgent)
};
function detectTypes() {
const detected = [];
for (const [type, detector] of Object.entries(detectionTypes)) {
if (detector()) {
detected.push(type);
}
}
return detected;
}
function evaluateCondition(condition, currentTypes) {
const parts = condition.split('&').map(part => part.trim());
return parts.every(part => {
const orParts = part.split('|').map(p => p.trim());
return orParts.some(p => {
if (p.startsWith('!')) {
return !currentTypes.includes(p.slice(1));
}
return currentTypes.includes(p);
});
});
}
function updateElementVisibility() {
const currentTypes = detectTypes();
const elements = document.querySelectorAll('[ms-code-device-show]');
elements.forEach(element => {
const showAttribute = element.getAttribute('ms-code-device-show').toLowerCase();
const shouldShow = evaluateCondition(showAttribute, currentTypes);
element.style.display = shouldShow ? '' : 'none';
});
}
// Run on page load and window resize
window.addEventListener('load', updateElementVisibility);
window.addEventListener('resize', updateElementVisibility);
</script>
<!-- 💙 MEMBERSCRIPT #120 v0.1 💙 - DEVICE/OS/BROWSER CONDITIONAL VISIBILITY -->
<script>
// Define device types, operating systems, and browsers with their detection methods
const detectionTypes = {
// Device types
mobile: () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
tablet: () => /iPad|Android|Silk/i.test(navigator.userAgent) && !/Mobile/i.test(navigator.userAgent),
desktop: () => !(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)),
touchdevice: () => 'ontouchstart' in window || navigator.maxTouchPoints > 0,
landscape: () => window.matchMedia("(orientation: landscape)").matches,
portrait: () => window.matchMedia("(orientation: portrait)").matches,
// Operating Systems
ios: () => /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
android: () => /Android/.test(navigator.userAgent),
macos: () => /Mac OS X/.test(navigator.userAgent) && !(/iPad|iPhone|iPod/.test(navigator.userAgent)),
windows: () => /Win/.test(navigator.platform),
linux: () => /Linux/.test(navigator.platform),
// Browsers
chrome: () => /Chrome/.test(navigator.userAgent) && !/Chromium/.test(navigator.userAgent),
firefox: () => /Firefox/.test(navigator.userAgent),
safari: () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
edge: () => /Edg/.test(navigator.userAgent),
opera: () => /OPR/.test(navigator.userAgent) || /Opera/.test(navigator.userAgent),
ie: () => /Trident/.test(navigator.userAgent)
};
function detectTypes() {
const detected = [];
for (const [type, detector] of Object.entries(detectionTypes)) {
if (detector()) {
detected.push(type);
}
}
return detected;
}
function evaluateCondition(condition, currentTypes) {
const parts = condition.split('&').map(part => part.trim());
return parts.every(part => {
const orParts = part.split('|').map(p => p.trim());
return orParts.some(p => {
if (p.startsWith('!')) {
return !currentTypes.includes(p.slice(1));
}
return currentTypes.includes(p);
});
});
}
function updateElementVisibility() {
const currentTypes = detectTypes();
const elements = document.querySelectorAll('[ms-code-device-show]');
elements.forEach(element => {
const showAttribute = element.getAttribute('ms-code-device-show').toLowerCase();
const shouldShow = evaluateCondition(showAttribute, currentTypes);
element.style.display = shouldShow ? '' : 'none';
});
}
// Run on page load and window resize
window.addEventListener('load', updateElementVisibility);
window.addEventListener('resize', updateElementVisibility);
</script>

#Nr. 119 - Altersberechnung ab Datumseingabe
Berechnen Sie die Gesamtzahl der vergangenen Jahre und füllen Sie eine Eingabe mit dieser Zahl aus.
<!-- 💙 MEMBERSCRIPT #119 v0.1 💙 - AGE CALCULATOR FROM DATE INPUT -->
<script>
// Function to calculate age
function calculateAge(birthDate) {
const today = new Date();
const birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
// Function to update age input
function updateAgeInput(birthdayInput) {
const attrValue = birthdayInput.getAttribute('ms-code-bday-input');
const ageInput = document.querySelector(`[ms-code-age-input="${attrValue}"]`);
if (birthdayInput.value) {
const age = calculateAge(birthdayInput.value);
ageInput.value = age;
} else {
ageInput.value = '';
}
}
// Function to set up event listeners for all birthday inputs
function setupAgeCalculators() {
const birthdayInputs = document.querySelectorAll('[ms-code-bday-input]');
birthdayInputs.forEach(input => {
input.addEventListener('input', () => updateAgeInput(input));
// Initial call to set age if birthday is pre-filled
updateAgeInput(input);
});
}
// Run setup when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', setupAgeCalculators);
</script>
<!-- 💙 MEMBERSCRIPT #119 v0.1 💙 - AGE CALCULATOR FROM DATE INPUT -->
<script>
// Function to calculate age
function calculateAge(birthDate) {
const today = new Date();
const birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
// Function to update age input
function updateAgeInput(birthdayInput) {
const attrValue = birthdayInput.getAttribute('ms-code-bday-input');
const ageInput = document.querySelector(`[ms-code-age-input="${attrValue}"]`);
if (birthdayInput.value) {
const age = calculateAge(birthdayInput.value);
ageInput.value = age;
} else {
ageInput.value = '';
}
}
// Function to set up event listeners for all birthday inputs
function setupAgeCalculators() {
const birthdayInputs = document.querySelectorAll('[ms-code-bday-input]');
birthdayInputs.forEach(input => {
input.addEventListener('input', () => updateAgeInput(input));
// Initial call to set age if birthday is pre-filled
updateAgeInput(input);
});
}
// Run setup when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', setupAgeCalculators);
</script>
Brauchen Sie Hilfe mit MemberScripts? Treten Sie unserer Slack-Community mit über 5.500 Mitgliedern bei! 🙌
MemberScripts sind eine Community-Ressource von Memberstack - wenn du Hilfe brauchst, damit sie mit deinem Projekt funktionieren, melde dich bitte im Memberstack 2.0 Slack an und bitte um Hilfe!
Unserem Slack beitretenEntdecken Sie echte Unternehmen, die mit Memberstack erfolgreich waren
Verlassen Sie sich nicht nur auf unser Wort - schauen Sie sich die Unternehmen aller Größen an, die sich auf Memberstack für ihre Authentifizierung und Zahlungen verlassen.
Bauen Sie Ihre Träume
Memberstack ist 100% kostenlos, bis Sie bereit sind, zu starten - worauf warten Sie also noch? Erstellen Sie Ihre erste App und beginnen Sie noch heute mit der Entwicklung.