Componentes
Plantillas
Atributos
Integraciones
Comprobador del sitio
Código personalizado

MemberScripts

Una solución basada en atributos para añadir funciones a su sitio Webflow.
Simplemente copie algo de código, añada algunos atributos y listo.

Muchas gracias. Hemos recibido su envío.
¡Uy! Algo ha ido mal al enviar el formulario.
¿Necesita ayuda con MemberScripts?

Todos los clientes de Memberstack pueden solicitar asistencia en el Slack 2.0. Tenga en cuenta que no se trata de funciones oficiales y que no se puede garantizar la asistencia.

Visibilidad condicional

#98 - Age Gating

Haga que los usuarios confirmen su edad antes de continuar.


<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const form = document.querySelector('form[ms-code-age-gate]');
  const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
  const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
  const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
  const backButton = document.querySelector('a[ms-code-age-gate="back"]');
  const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
  const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');

  if (localStorage.getItem('ageVerified') === 'true') {
    wrapper.remove();
    return;
  }

  backButton.addEventListener('click', (e) => {
    e.preventDefault();
    window.history.back();
  });

  const inputs = [dayInput, monthInput, yearInput];

  inputs.forEach((input, index) => {
    input.addEventListener('keyup', (e) => {
      const maxChars = input === yearInput ? 4 : 2;
      let value = e.target.value;

      if (input === dayInput && value.length === maxChars) {
        value = value > 31 ? '31' : value.padStart(2, '0');
      } else if (input === monthInput && value.length === maxChars) {
        value = value > 12 ? '12' : value.padStart(2, '0');
      }
      e.target.value = value;

      if (value.length === maxChars) {
        const nextInput = inputs[index + 1];
        if (nextInput) {
          nextInput.focus();
        }
      }
    });
  });

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
    const currentDate = new Date();
    const ageDifference = new Date(currentDate - enteredDate);
    const age = Math.abs(ageDifference.getUTCFullYear() - 1970);

    const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);

    if (age >= ageLimit) {
      console.log('Age verified.');
      errorDiv.style.display = 'none';
      localStorage.setItem('ageVerified', 'true');
      wrapper.remove();
    } else {
      console.log('Age verification failed, user is under the age limit.');
      errorDiv.style.display = 'block';
    }
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #98 v0.1 💙 AGE GATE -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const form = document.querySelector('form[ms-code-age-gate]');
  const dayInput = document.querySelector('input[ms-code-age-gate="day"]');
  const monthInput = document.querySelector('input[ms-code-age-gate="month"]');
  const yearInput = document.querySelector('input[ms-code-age-gate="year"]');
  const backButton = document.querySelector('a[ms-code-age-gate="back"]');
  const wrapper = document.querySelector('[ms-code-age-gate="wrapper"]');
  const errorDiv = document.querySelector('div[ms-code-age-gate="error"]');

  if (localStorage.getItem('ageVerified') === 'true') {
    wrapper.remove();
    return;
  }

  backButton.addEventListener('click', (e) => {
    e.preventDefault();
    window.history.back();
  });

  const inputs = [dayInput, monthInput, yearInput];

  inputs.forEach((input, index) => {
    input.addEventListener('keyup', (e) => {
      const maxChars = input === yearInput ? 4 : 2;
      let value = e.target.value;

      if (input === dayInput && value.length === maxChars) {
        value = value > 31 ? '31' : value.padStart(2, '0');
      } else if (input === monthInput && value.length === maxChars) {
        value = value > 12 ? '12' : value.padStart(2, '0');
      }
      e.target.value = value;

      if (value.length === maxChars) {
        const nextInput = inputs[index + 1];
        if (nextInput) {
          nextInput.focus();
        }
      }
    });
  });

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const enteredDate = new Date(yearInput.value, monthInput.value - 1, dayInput.value);
    const currentDate = new Date();
    const ageDifference = new Date(currentDate - enteredDate);
    const age = Math.abs(ageDifference.getUTCFullYear() - 1970);

    const ageLimit = parseInt(form.getAttribute('ms-code-age-gate').split('-')[1], 10);

    if (age >= ageLimit) {
      console.log('Age verified.');
      errorDiv.style.display = 'none';
      localStorage.setItem('ageVerified', 'true');
      wrapper.remove();
    } else {
      console.log('Age verification failed, user is under the age limit.');
      errorDiv.style.display = 'block';
    }
  });
});
</script>
Ver Memberscript
Integración

#97 - Subir archivos a S3 Bucket

Permitir subidas a un bucket S3 desde un formulario Webflow.


<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
    document.addEventListener('DOMContentLoaded', function() {
        function generateUUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
            input.addEventListener('change', function() {
                if (this.files.length > 0) {
                    const file = this.files[0];
                    const uuid = generateUUID();
                    const extension = file.name.split('.').pop();
                    const newFileName = `${uuid}.${extension}`;
                    const wrapper = this.closest('div[ms-code-s3-wrapper]');
                    const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');

                    s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);

                    const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));

                    fetch(apiGatewayUrl, {
                        method: 'PUT',
                        body: file,
                        headers: { 'Content-Type': file.type }
                    })
                    .then(response => {
                        if (response.status !== 200) {
                            throw new Error(`Upload failed with status: ${response.status}`);
                        }
                        console.log('File uploaded successfully:', newFileName);
                    })
                    .catch(error => {
                        console.error('Upload error:', error);
                        alert('Upload failed.');
                    });
                }
            });
        });

        document.querySelectorAll('form').forEach(form => {
            form.addEventListener('submit', function(event) {
                const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
                const allUrlsSet = s3Inputs.every(input => input.value);

                if (!allUrlsSet) {
                    event.preventDefault();
                    alert('Please wait for all files to finish uploading before submitting.');
                }
            });
        });
    });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #97 v0.1 💙 S3 FILE UPLOADS -->
<script>
    document.addEventListener('DOMContentLoaded', function() {
        function generateUUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        document.querySelectorAll('input[ms-code-s3-uploader]').forEach(input => {
            input.addEventListener('change', function() {
                if (this.files.length > 0) {
                    const file = this.files[0];
                    const uuid = generateUUID();
                    const extension = file.name.split('.').pop();
                    const newFileName = `${uuid}.${extension}`;
                    const wrapper = this.closest('div[ms-code-s3-wrapper]');
                    const s3FileInput = wrapper.querySelector('input[ms-code-s3-file]');

                    s3FileInput.value = s3FileInput.getAttribute('ms-code-s3-file') + encodeURIComponent(newFileName);

                    const apiGatewayUrl = wrapper.getAttribute('ms-code-s3-wrapper').replace('${encodeURIComponent(fileName)}', encodeURIComponent(newFileName));

                    fetch(apiGatewayUrl, {
                        method: 'PUT',
                        body: file,
                        headers: { 'Content-Type': file.type }
                    })
                    .then(response => {
                        if (response.status !== 200) {
                            throw new Error(`Upload failed with status: ${response.status}`);
                        }
                        console.log('File uploaded successfully:', newFileName);
                    })
                    .catch(error => {
                        console.error('Upload error:', error);
                        alert('Upload failed.');
                    });
                }
            });
        });

        document.querySelectorAll('form').forEach(form => {
            form.addEventListener('submit', function(event) {
                const s3Inputs = Array.from(form.querySelectorAll('input[ms-code-s3-file]'));
                const allUrlsSet = s3Inputs.every(input => input.value);

                if (!allUrlsSet) {
                    event.preventDefault();
                    alert('Please wait for all files to finish uploading before submitting.');
                }
            });
        });
    });
</script>
Ver Memberscript
Campos personalizados

#96 - Guardar un recuento

Cree y actualice un recuento que se guarde en un campo personalizado.


<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;
    const addButtons = document.querySelectorAll("[ms-code-add-tally]");

    addButtons.forEach(button => {
        button.addEventListener("click", async () => {
            const tallyKey = button.getAttribute("ms-code-add-tally");
            const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
           
            if(tallyText){
                let currentCount = parseInt(tallyText.textContent, 10);
                currentCount += 1;
                tallyText.textContent = currentCount;

                // Store the new tally count to Memberstack
                let newFields = {};
                newFields[tallyKey] = currentCount;
                await memberstack.updateMember({customFields: newFields});
           }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #96 v0.1 💙 KEEPING A TALLY -->
<script>
document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;
    const addButtons = document.querySelectorAll("[ms-code-add-tally]");

    addButtons.forEach(button => {
        button.addEventListener("click", async () => {
            const tallyKey = button.getAttribute("ms-code-add-tally");
            const tallyText = document.querySelector(`[ms-code-tally="${tallyKey}"]`);
           
            if(tallyText){
                let currentCount = parseInt(tallyText.textContent, 10);
                currentCount += 1;
                tallyText.textContent = currentCount;

                // Store the new tally count to Memberstack
                let newFields = {};
                newFields[tallyKey] = currentCount;
                await memberstack.updateMember({customFields: newFields});
           }
        });
    });
});
</script>
Ver Memberscript
UX

#95 - Confeti al clic

¡Haz volar un divertido confeti al hacer clic!


<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
    const confettiElems = document.querySelectorAll("[ms-code-confetti]");

    confettiElems.forEach(item => {
        item.addEventListener("click", () => {
            const effect = item.getAttribute("ms-code-confetti");
            switch (effect) {
                case "falling":
                    const makeFall = () => {
                        confetti({
                            particleCount: 100,
                            startVelocity: 30,
                            spread: 360,
                            origin: { x: Math.random(), y: 0 },
                            colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
                        });
                    }
                    setInterval(makeFall, 2000);
                    break;
                case "single":
                    confetti({
                        particleCount: 1,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: Math.random() }
                    });
                    break;
                case "sides":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: 0.5 }
                    });
                    break;
                case "explosions":
                    confetti({
                        particleCount: 100,
                        startVelocity: 50,
                        spread: 360
                    });
                    break;
                case "bottom":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: 0.5, y: 1 }
                    });
                    break;
                default:
                    console.log("Unknown confetti effect");
            }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #95 v0.1 💙 CONFETTI -->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-confetti@2.12.0/tsparticles.confetti.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
    const confettiElems = document.querySelectorAll("[ms-code-confetti]");

    confettiElems.forEach(item => {
        item.addEventListener("click", () => {
            const effect = item.getAttribute("ms-code-confetti");
            switch (effect) {
                case "falling":
                    const makeFall = () => {
                        confetti({
                            particleCount: 100,
                            startVelocity: 30,
                            spread: 360,
                            origin: { x: Math.random(), y: 0 },
                            colors: ['#ffffff','#ff0000','#00ff00','#0000ff']
                        });
                    }
                    setInterval(makeFall, 2000);
                    break;
                case "single":
                    confetti({
                        particleCount: 1,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: Math.random() }
                    });
                    break;
                case "sides":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: Math.random(), y: 0.5 }
                    });
                    break;
                case "explosions":
                    confetti({
                        particleCount: 100,
                        startVelocity: 50,
                        spread: 360
                    });
                    break;
                case "bottom":
                    confetti({
                        particleCount: 100,
                        startVelocity: 30,
                        spread: 360,
                        origin: { x: 0.5, y: 1 }
                    });
                    break;
                default:
                    console.log("Unknown confetti effect");
            }
        });
    });
});
</script>
Ver Memberscript
UX

#94 - Establecer atributo href

Establecer dinámicamente un enlace utilizando el Webflow CMS (o cualquier otra cosa)


<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
  var elements = document.querySelectorAll('[ms-code-href]');
  elements.forEach(function(element) {
    var url = element.getAttribute('ms-code-href');
    element.setAttribute('href', url);
  });
};
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #94 v0.1 💙 SET HREF ATTRIBUTE -->
<script>
window.onload = function(){
  var elements = document.querySelectorAll('[ms-code-href]');
  elements.forEach(function(element) {
    var url = element.getAttribute('ms-code-href');
    element.setAttribute('href', url);
  });
};
</script>
Ver Memberscript
Campos personalizados
UX

#93 - Forzar URLs válidas en los formularios

Convierte automáticamente cualquier entrada en una URL válida.


<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');

// Add event listener to each form field
formFields.forEach((field) => {
  field.addEventListener('input', convertToLink);
});

// Function to convert input to a link
function convertToLink(event) {
  const input = event.target;

  // Get user input
  const userInput = input.value.trim();

  // Check if input starts with http:// or https://
  if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
    input.value = userInput; // No conversion needed for valid links
  } else {
    input.value = `http://${userInput}`; // Prepend http:// for simplicity
  }
}
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #93 v0.1 💙 FORCE INPUT TO BE A VALID URL -->
<script>
// Get all form fields with attribute ms-code-convert="link"
const formFields = document.querySelectorAll('input[ms-code-convert="link"], textarea[ms-code-convert="link"]');

// Add event listener to each form field
formFields.forEach((field) => {
  field.addEventListener('input', convertToLink);
});

// Function to convert input to a link
function convertToLink(event) {
  const input = event.target;

  // Get user input
  const userInput = input.value.trim();

  // Check if input starts with http:// or https://
  if (userInput.startsWith('http://') || userInput.startsWith('https://')) {
    input.value = userInput; // No conversion needed for valid links
  } else {
    input.value = `http://${userInput}`; // Prepend http:// for simplicity
  }
}
</script>
Ver Memberscript
UX

#92 - Cambiar de página al hacer clic

Cambia la URL de la página actual al hacer clic en cualquier elemento.


<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
    let target = event.target;

    // Traverse up the DOM tree to find an element with the ms-code-navigate attribute
    while (target && !target.getAttribute('ms-code-navigate')) {
        target = target.parentElement;
    }

    // If we found an element with the ms-code-navigate attribute
    if (target) {
        const navigateUrl = target.getAttribute('ms-code-navigate');

        if (navigateUrl) {
            event.preventDefault();
            // Always open in a new tab
            window.open(navigateUrl, '_blank');
        }
    }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #92 v0.1 💙 TURN ANYTHING INTO A LINK -->
<script>
document.addEventListener('click', function(event) {
    let target = event.target;

    // Traverse up the DOM tree to find an element with the ms-code-navigate attribute
    while (target && !target.getAttribute('ms-code-navigate')) {
        target = target.parentElement;
    }

    // If we found an element with the ms-code-navigate attribute
    if (target) {
        const navigateUrl = target.getAttribute('ms-code-navigate');

        if (navigateUrl) {
            event.preventDefault();
            // Always open in a new tab
            window.open(navigateUrl, '_blank');
        }
    }
});
</script>
Ver Memberscript
Modales
UX

#91 - Ocultar ventana emergente durante un tiempo determinado

Ocultar una ventana emergente durante X tiempo cuando se hace clic en un botón.


<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
  $(document).ready(function() {
    // Look for elements with 'ms-code-hide-popup' attribute
    var items = $('[ms-code-hide-popup]');

    var button;
    var timeElement;

    // Determine which element is the button and which is the timer
    items.each(function(index, item) {
      var value = $(item).attr('ms-code-hide-popup');
      if (value === "button") {
        button = $(item);
      } else {
        timeElement = $(item);
      }
    });

    // Calculate the target date
    var calculateTargetDate = function(timeStr) {
      var splitTime = timeStr.split(':');
      var now = new Date();
      now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
      now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
      now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
      now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
      return now;
    };

    // Check if element should be removed from DOM
    var checkTimeAndRemoveElement = function() {
      var targetDate = localStorage.getItem('targetDate');
      if (targetDate && new Date() < new Date(targetDate)) {
        timeElement.remove();
      } else {
        localStorage.removeItem('targetDate');
      }
    };

    // Action on button click
    button.on('click', function() {
      var time = timeElement.attr('ms-code-hide-popup');
      localStorage.setItem('targetDate', calculateTargetDate(time));
      checkTimeAndRemoveElement();
    });

    // Initial check
    checkTimeAndRemove AndRemoveElement();
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #91 v0.1 💙 HIDE POPUP FOR SET DURATION -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
  $(document).ready(function() {
    // Look for elements with 'ms-code-hide-popup' attribute
    var items = $('[ms-code-hide-popup]');

    var button;
    var timeElement;

    // Determine which element is the button and which is the timer
    items.each(function(index, item) {
      var value = $(item).attr('ms-code-hide-popup');
      if (value === "button") {
        button = $(item);
      } else {
        timeElement = $(item);
      }
    });

    // Calculate the target date
    var calculateTargetDate = function(timeStr) {
      var splitTime = timeStr.split(':');
      var now = new Date();
      now.setDate(now.getDate() + parseInt(splitTime[0])); // add days
      now.setHours(now.getHours() + parseInt(splitTime[1])); // add hours
      now.setMinutes(now.getMinutes() + parseInt(splitTime[2])); // add minutes
      now.setSeconds(now.getSeconds() + parseInt(splitTime[3])); // add seconds
      return now;
    };

    // Check if element should be removed from DOM
    var checkTimeAndRemoveElement = function() {
      var targetDate = localStorage.getItem('targetDate');
      if (targetDate && new Date() < new Date(targetDate)) {
        timeElement.remove();
      } else {
        localStorage.removeItem('targetDate');
      }
    };

    // Action on button click
    button.on('click', function() {
      var time = timeElement.attr('ms-code-hide-popup');
      localStorage.setItem('targetDate', calculateTargetDate(time));
      checkTimeAndRemoveElement();
    });

    // Initial check
    checkTimeAndRemove AndRemoveElement();
  });
</script>
Ver Memberscript
Campos personalizados
UX

#90 - Mostrar elementos al cambiar la entrada

Mostrar 1 o más elementos cuando un usuario cambia el valor de entrada.


<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    // Initially hide all elements
    $('[ms-code-show-item]').css('display', 'none');

    setTimeout(function() {
        $('[ms-code-show-field]').change(function() {
            var field = $(this).attr('ms-code-show-field');
            $('[ms-code-show-item=' + field + ']').css('display', 'block');
        });
    }, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #90 v0.1 💙 SHOW ELEMENTS ON INPUT CHANGE -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
    // Initially hide all elements
    $('[ms-code-show-item]').css('display', 'none');

    setTimeout(function() {
        $('[ms-code-show-field]').change(function() {
            var field = $(this).attr('ms-code-show-field');
            $('[ms-code-show-item=' + field + ']').css('display', 'block');
        });
    }, 500); // Wait 500ms before starting, you can change this time based on your needs
});
</script>
Ver Memberscript
UX

#89 - Menús contextuales personalizados

Muestra un menú contextual personalizado e integrado en Webflow cuando se hace clic con el botón derecho del ratón sobre el elemento.


<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");

// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
    element.addEventListener('contextmenu', event => {
        event.preventDefault(); // Prevents showing the default context menu
        hideAllMenus(); // Make sure other menus are hidden

        // fetch the related menu, make it visible
        const menuItemId = element.getAttribute("ms-code-context-item");
        const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);

        if (menu) {
            menu.classList.remove('hidden');
            menu.classList.add('visible');
        }
    });
});

// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
    menu.addEventListener('click', event => {
        event.stopPropagation();
    });
});

// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);

// Helper function to hide all custom context menus
function hideAllMenus() {
    menus.forEach(menu => {
        menu.classList.remove('visible');
        menu.classList.add('hidden');
    });
}
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #89 v0.1 💙 CUSTOM CONTEXT MENUS -->
<script>
// Cache elements
const items = document.querySelectorAll("[ms-code-context-item]");
const menus = document.querySelectorAll("[ms-code-context-menu]");

// Disable default context menu on item right click and show custom context menu
items.forEach(element => {
    element.addEventListener('contextmenu', event => {
        event.preventDefault(); // Prevents showing the default context menu
        hideAllMenus(); // Make sure other menus are hidden

        // fetch the related menu, make it visible
        const menuItemId = element.getAttribute("ms-code-context-item");
        const menu = document.querySelector(`[ms-code-context-menu="${menuItemId}"]`);

        if (menu) {
            menu.classList.remove('hidden');
            menu.classList.add('visible');
        }
    });
});

// Add click event on custom menus to stop event propagation
menus.forEach(menu => {
    menu.addEventListener('click', event => {
        event.stopPropagation();
    });
});

// Close custom context menu on outside click
document.body.addEventListener('click', hideAllMenus);

// Helper function to hide all custom context menus
function hideAllMenus() {
    menus.forEach(menu => {
        menu.classList.remove('visible');
        menu.classList.add('hidden');
    });
}
</script>
Ver Memberscript
UX

#88 - Mostrar estado actual para CMS, Enlaces de carpetas

Muestre el estado "actual" de Webflow en sus páginas anidadas y elementos CMS.


<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
  var currentUrl = window.location.href;
  var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute

  elements.forEach(function (element) {
    var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
    if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
      element.classList.add('w--current'); // apply the class 
    }
  });
};
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #88 v0.1 💙 SHOW CURRENT STATE FOR NESTED URLS -->
<script>
window.onload = function() {
  var currentUrl = window.location.href;
  var elements = document.querySelectorAll('[ms-code-nested-link]'); // get all elements with ms-code-nested-link attribute

  elements.forEach(function (element) {
    var linkAttrValue = element.getAttribute('ms-code-nested-link'); // get the ms-code-nested-link value
    if (currentUrl.includes(linkAttrValue)) { // check if current url matches the attribute value
      element.classList.add('w--current'); // apply the class 
    }
  });
};
</script>
Ver Memberscript
Flujos personalizados

#87 - Eliminar un plan después de la cuenta atrás

Cree contenidos seguros y urgentes


<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));

// Check if date has passed
const checkDate = async () => {
  const now = new Date();
  if (now > countdown) {
    // Remove member's free plan
    await memberstack.removePlan({
      planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
    });
    console.log("Plan removed");

    // Reload the page
    location.reload();
  }
}

// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #87 v0.1 💙 REMOVE PLAN AFTER COUNTDOWN -->
<script>
const memberstack = window.$memberstackDom;
const countdown = new Date(localStorage.getItem('countdownDateTime'));

// Check if date has passed
const checkDate = async () => {
  const now = new Date();
  if (now > countdown) {
    // Remove member's free plan
    await memberstack.removePlan({
      planId: "pln_10-minutes-of-gif-access-rw1fh0ktg"
    });
    console.log("Plan removed");

    // Reload the page
    location.reload();
  }
}

// Execute checkDate every 10s
const intervalId = setInterval(checkDate, 10000);
</script>
Ver Memberscript
UX

#86 - Texto a voz gratuito y sencillo

Añada un botón que permita a los visitantes escuchar su artículo.


<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
    const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
    const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
    let utterance = new SpeechSynthesisUtterance();

    speakButton.addEventListener('click', () => {
        if(speechSynthesis.speaking || speechSynthesis.paused) {
            speechSynthesis.cancel(); // stops current speech
        } else {
            utterance.text = textDiv.innerText;

            speechSynthesis.speak(utterance); // starts speaking
        }
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #86 v0.1 💙 VOICE TO TEXT BUTTON -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
    const textDiv = document.querySelector('[ms-code-text-to-speech="text"]');
    const speakButton = document.querySelector('[ms-code-text-to-speech="button"]');
    let utterance = new SpeechSynthesisUtterance();

    speakButton.addEventListener('click', () => {
        if(speechSynthesis.speaking || speechSynthesis.paused) {
            speechSynthesis.cancel(); // stops current speech
        } else {
            utterance.text = textDiv.innerText;

            speechSynthesis.speak(utterance); // starts speaking
        }
    });
});
</script>
Ver Memberscript
Campos personalizados

#85 - Entradas del formulario "Añadir una fila

Permitir a los miembros añadir y eliminar filas de un formulario de entrada.


<!-- 💙 MEMBERSCRIPT #85 v0.1 💙 ADD A ROW FORM INPUTS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
  $(document).ready(function() {
    // Hide all rows except the original row
    $('[ms-code-row-input="new"]').hide();

    // Add row button click event
    $('[ms-code-row-input="add-row"]').click(function(e) {
      e.preventDefault();
      var clonedRow = $('[ms-code-row-input="new"]').first().clone();
      clonedRow.find('input').val('');
      clonedRow.show().appendTo('[ms-code-row-input="row-container"]');

      updateHolderValue();
    });

    // Delete row button click event
    $(document).on('click', '[ms-code-row-input="delete"]', function(e) {
      e.preventDefault();
      $(this).closest('[ms-code-row-input="new"]').remove();

      updateHolderValue();
    });

    // Event for all inputs
    $(document).on('input', '[ms-code-row-input="original"], [ms-code-row-input="new-input"], [ms-code-row-input="holder"]', function() {
      if ($(this).is('[ms-code-row-input="holder"]')) {
        updateRowsFromHolder();
      } else {
        updateHolderValue();
      }
    });

    // Function to update the holder input value
    function updateHolderValue() {
      var values = [];
      $('[ms-code-row-input="original"], [ms-code-row-input="new-input"]').each(function() {
        var value = $(this).val().trim();
        if (value) {
          values.push(value);
        }
      });
      $('[ms-code-row-input="holder"]').val(values.join(','));
    }

    // Function to update rows from the holder field
    function updateRowsFromHolder() {
      var holderValue = $('[ms-code-row-input="holder"]').val();
      var values = holderValue.split(',');

      $('[ms-code-row-input="new"]').not(':first').remove();

      // For each holder value, create a new row
      values.forEach(function(val, idx) {
        if (idx === 0) {
          $('[ms-code-row-input="original"]').val(val);
        } else {
          var newRow = $('[ms-code-row-input="new"]').first().clone().appendTo('[ms-code-row-input="row-container"]');
          newRow.find('input').val(val);
          newRow.show();
        }
      });
    }

    // Initial update of the holder input value
    updateHolderValue();

    // Adding MutationObserver to call updateRowsFromHolder on changes to the holder field
    var targetNode = $('[ms-code-row-input="holder"]')[0];
    var config = { attributes: true, childList: true, subtree: true };
    var callback = function(mutationsList, observer) {
      for(let mutation of mutationsList) {
        if (mutation.type === 'childList')
        {
          updateRowsFromHolder();
        }
      }
    };
    var observer = new MutationObserver(callback);
    observer.observe(targetNode, config);
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #85 v0.1 💙 ADD A ROW FORM INPUTS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
  $(document).ready(function() {
    // Hide all rows except the original row
    $('[ms-code-row-input="new"]').hide();

    // Add row button click event
    $('[ms-code-row-input="add-row"]').click(function(e) {
      e.preventDefault();
      var clonedRow = $('[ms-code-row-input="new"]').first().clone();
      clonedRow.find('input').val('');
      clonedRow.show().appendTo('[ms-code-row-input="row-container"]');

      updateHolderValue();
    });

    // Delete row button click event
    $(document).on('click', '[ms-code-row-input="delete"]', function(e) {
      e.preventDefault();
      $(this).closest('[ms-code-row-input="new"]').remove();

      updateHolderValue();
    });

    // Event for all inputs
    $(document).on('input', '[ms-code-row-input="original"], [ms-code-row-input="new-input"], [ms-code-row-input="holder"]', function() {
      if ($(this).is('[ms-code-row-input="holder"]')) {
        updateRowsFromHolder();
      } else {
        updateHolderValue();
      }
    });

    // Function to update the holder input value
    function updateHolderValue() {
      var values = [];
      $('[ms-code-row-input="original"], [ms-code-row-input="new-input"]').each(function() {
        var value = $(this).val().trim();
        if (value) {
          values.push(value);
        }
      });
      $('[ms-code-row-input="holder"]').val(values.join(','));
    }

    // Function to update rows from the holder field
    function updateRowsFromHolder() {
      var holderValue = $('[ms-code-row-input="holder"]').val();
      var values = holderValue.split(',');

      $('[ms-code-row-input="new"]').not(':first').remove();

      // For each holder value, create a new row
      values.forEach(function(val, idx) {
        if (idx === 0) {
          $('[ms-code-row-input="original"]').val(val);
        } else {
          var newRow = $('[ms-code-row-input="new"]').first().clone().appendTo('[ms-code-row-input="row-container"]');
          newRow.find('input').val(val);
          newRow.show();
        }
      });
    }

    // Initial update of the holder input value
    updateHolderValue();

    // Adding MutationObserver to call updateRowsFromHolder on changes to the holder field
    var targetNode = $('[ms-code-row-input="holder"]')[0];
    var config = { attributes: true, childList: true, subtree: true };
    var callback = function(mutationsList, observer) {
      for(let mutation of mutationsList) {
        if (mutation.type === 'childList')
        {
          updateRowsFromHolder();
        }
      }
    };
    var observer = new MutationObserver(callback);
    observer.observe(targetNode, config);
  });
</script>
Ver Memberscript
UX
Campos personalizados

#84 - Borrar entradas en carga

Añada este script a cualquier página para borrar el valor de un campo personalizado al cargar la página.


<!-- 💙 MEMBERSCRIPT #84 v0.1 💙 CLEAR INPUT VALUES ONLOAD -->
<script>
  document.addEventListener('DOMContentLoaded', async function() {
    const memberstack = window.$memberstackDom;
    const fieldsToClear = ["phone", "last-name"]; // Specify the fields to clear

    // Clear inputs and Memberstack fields on page load
    memberstack.getCurrentMember().then(async ({ data: member }) => {
      if (member) {
        const customFieldsToUpdate = {};
      
        fieldsToClear.forEach(fieldName => {
          customFieldsToUpdate[fieldName] = '';
        });

        try {
          await memberstack.updateMember({
            customFields: customFieldsToUpdate
          });
          console.log("Fields cleared on page load.");
        } catch (error) {
          console.error('Error clearing fields on page load:', error);
        }
      }
      
      // Clear input values on page load for specified fields
      fieldsToClear.forEach(fieldName => {
        const inputField = document.querySelector(`[data-ms-member="${fieldName}"]`);
        if (inputField) {
          inputField.value = '';
        }
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #84 v0.1 💙 CLEAR INPUT VALUES ONLOAD -->
<script>
  document.addEventListener('DOMContentLoaded', async function() {
    const memberstack = window.$memberstackDom;
    const fieldsToClear = ["phone", "last-name"]; // Specify the fields to clear

    // Clear inputs and Memberstack fields on page load
    memberstack.getCurrentMember().then(async ({ data: member }) => {
      if (member) {
        const customFieldsToUpdate = {};
      
        fieldsToClear.forEach(fieldName => {
          customFieldsToUpdate[fieldName] = '';
        });

        try {
          await memberstack.updateMember({
            customFields: customFieldsToUpdate
          });
          console.log("Fields cleared on page load.");
        } catch (error) {
          console.error('Error clearing fields on page load:', error);
        }
      }
      
      // Clear input values on page load for specified fields
      fieldsToClear.forEach(fieldName => {
        const inputField = document.querySelector(`[data-ms-member="${fieldName}"]`);
        if (inputField) {
          inputField.value = '';
        }
      });
    });
  });
</script>
Ver Memberscript
UX

#83 - Preferencias de cookies entre dispositivos

Permitir a los usuarios guardar sus preferencias de cookies en su cuenta.


<!-- 💙 MEMBERSCRIPT #83 v0.1 💙 CROSS-DEVICE COOKIE PREFERENCES -->
<script>
// Function to retrieve a cookie value by name
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
}

async function updateMemberConsentPreferences(fsCcCookieValue) {
  try {
    const memberstack = window.$memberstackDom;
    const userData = await memberstack.getCurrentMember();

    if (userData && userData.data.customFields) {
      if (!userData.data.customFields['cookie-consent']) {
        const decodedFsCcCookieValue = decodeURIComponent(fsCcCookieValue);
        await memberstack.updateMember({
          customFields: {
            'cookie-consent': decodedFsCcCookieValue
          }
        });
      } else {
        document.cookie = `fs-cc=${encodeURIComponent(userData.data.customFields['cookie-consent'])}`;
      }
    }
  } catch (error) {}
}

async function initialize() {
  const fsCcCookieValue = getCookie('fs-cc');
  if (fsCcCookieValue) {
    await updateMemberConsentPreferences(fsCcCookieValue);

    const checkboxes = document.querySelectorAll('[fs-cc-checkbox]');
    checkboxes.forEach(checkbox => {
      checkbox.addEventListener('change', async () => {
        const memberstack = window.$memberstackDom;
        const userData = await memberstack.getCurrentMember();
        
        if (userData && userData.data.customFields) {
          const customFieldKey = 'cookie-consent';
          const checkboxName = checkbox.getAttribute('fs-cc-checkbox');

          if (userData.data.customFields[customFieldKey]) {
            const consentData = JSON.parse(userData.data.customFields[customFieldKey]);
            consentData.consents[checkboxName] = checkbox.checked;
            const updatedCustomField = JSON.stringify(consentData);

            await memberstack.updateMember({
              customFields: {
                [customFieldKey]: updatedCustomField
              }
            });

            document.cookie = `fs-cc=${encodeURIComponent(updatedCustomField)}`;
          }
        }
      });
    });
  }
}

// Initialize the script
initialize();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #83 v0.1 💙 CROSS-DEVICE COOKIE PREFERENCES -->
<script>
// Function to retrieve a cookie value by name
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
}

async function updateMemberConsentPreferences(fsCcCookieValue) {
  try {
    const memberstack = window.$memberstackDom;
    const userData = await memberstack.getCurrentMember();

    if (userData && userData.data.customFields) {
      if (!userData.data.customFields['cookie-consent']) {
        const decodedFsCcCookieValue = decodeURIComponent(fsCcCookieValue);
        await memberstack.updateMember({
          customFields: {
            'cookie-consent': decodedFsCcCookieValue
          }
        });
      } else {
        document.cookie = `fs-cc=${encodeURIComponent(userData.data.customFields['cookie-consent'])}`;
      }
    }
  } catch (error) {}
}

async function initialize() {
  const fsCcCookieValue = getCookie('fs-cc');
  if (fsCcCookieValue) {
    await updateMemberConsentPreferences(fsCcCookieValue);

    const checkboxes = document.querySelectorAll('[fs-cc-checkbox]');
    checkboxes.forEach(checkbox => {
      checkbox.addEventListener('change', async () => {
        const memberstack = window.$memberstackDom;
        const userData = await memberstack.getCurrentMember();
        
        if (userData && userData.data.customFields) {
          const customFieldKey = 'cookie-consent';
          const checkboxName = checkbox.getAttribute('fs-cc-checkbox');

          if (userData.data.customFields[customFieldKey]) {
            const consentData = JSON.parse(userData.data.customFields[customFieldKey]);
            consentData.consents[checkboxName] = checkbox.checked;
            const updatedCustomField = JSON.stringify(consentData);

            await memberstack.updateMember({
              customFields: {
                [customFieldKey]: updatedCustomField
              }
            });

            document.cookie = `fs-cc=${encodeURIComponent(updatedCustomField)}`;
          }
        }
      });
    });
  }
}

// Initialize the script
initialize();
</script>
Ver Memberscript
Flujos personalizados

#82 - Claves de licencia

Proteja sus contenidos descargables con claves de licencia.


<!-- 💙 MEMBERSCRIPT #82 v0.1 💙 LICENSE KEYS -->
<script>
const memberstack = window.$memberstackDom;

// Initialize MutationObserver
const observer = new MutationObserver(async (mutations) => {
  const downloadBtn = document.getElementById("download");
  
  if (downloadBtn) {
    // Element exists, so add event listener
    downloadBtn.addEventListener("click", async () => {
      await memberstack.removePlan({
        planId: "pln_activate-license-key-952c0d8u"
      });
      console.log("Plan removed");
    });

    // Stop observing since we found the element
    observer.disconnect();
  }
});

// Observe the whole document
observer.observe(document.body, { childList: true, subtree: true });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #82 v0.1 💙 LICENSE KEYS -->
<script>
const memberstack = window.$memberstackDom;

// Initialize MutationObserver
const observer = new MutationObserver(async (mutations) => {
  const downloadBtn = document.getElementById("download");
  
  if (downloadBtn) {
    // Element exists, so add event listener
    downloadBtn.addEventListener("click", async () => {
      await memberstack.removePlan({
        planId: "pln_activate-license-key-952c0d8u"
      });
      console.log("Plan removed");
    });

    // Stop observing since we found the element
    observer.disconnect();
  }
});

// Observe the whole document
observer.observe(document.body, { childList: true, subtree: true });
</script>
Ver Memberscript
Campos personalizados

#81 - Valores de casilla de verificación personalizados

Pasa un valor único en función de si la casilla está marcada o no.


<!-- 💙 MEMBERSCRIPT #81 v0.1 💙 CUSTOM CHECKBOX VALUES -->
<script>
document.addEventListener('submit', function(e) {
  var checkboxes = document.querySelectorAll('[ms-code-custom-checkbox]');
  
  checkboxes.forEach(function(checkbox) {
    var values = checkbox.getAttribute('ms-code-custom-checkbox').split(',');
    var valueToSubmit = checkbox.checked ? values[0] : values[1];

    var hiddenInput = document.createElement('input');
    
    // Copy all attributes except type and ms-code-custom-checkbox
    for (var i = 0; i < checkbox.attributes.length; i++) {
      var attr = checkbox.attributes[i];
      if (attr.name !== 'type' && attr.name !== 'ms-code-custom-checkbox') {
        hiddenInput.setAttribute(attr.name, attr.value);
      }
    }
    
    hiddenInput.type = 'hidden';
    hiddenInput.value = valueToSubmit;
    
    checkbox.form.appendChild(hiddenInput);
    checkbox.remove(); // Remove the original checkbox so it doesn't interfere with submission
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #81 v0.1 💙 CUSTOM CHECKBOX VALUES -->
<script>
document.addEventListener('submit', function(e) {
  var checkboxes = document.querySelectorAll('[ms-code-custom-checkbox]');
  
  checkboxes.forEach(function(checkbox) {
    var values = checkbox.getAttribute('ms-code-custom-checkbox').split(',');
    var valueToSubmit = checkbox.checked ? values[0] : values[1];

    var hiddenInput = document.createElement('input');
    
    // Copy all attributes except type and ms-code-custom-checkbox
    for (var i = 0; i < checkbox.attributes.length; i++) {
      var attr = checkbox.attributes[i];
      if (attr.name !== 'type' && attr.name !== 'ms-code-custom-checkbox') {
        hiddenInput.setAttribute(attr.name, attr.value);
      }
    }
    
    hiddenInput.type = 'hidden';
    hiddenInput.value = valueToSubmit;
    
    checkbox.form.appendChild(hiddenInput);
    checkbox.remove(); // Remove the original checkbox so it doesn't interfere with submission
  });
});
</script>
Ver Memberscript
UX
Marketing

#80 - Notificación de plan cancelado

Activar una notificación Slack cuando un miembro cancela su plan.

v0.1
Ver Memberscript
UX

#79 - Activar Click onHover

Activa un evento de clic onHover.


<!-- 💙 MEMBERSCRIPT #79 v0.1 💙 HOVER BASED TABS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const hoverTabElements = document.querySelectorAll('[ms-code-onhover="click"]');

    hoverTabElements.forEach(hoverTabElement => {
      hoverTabElement.addEventListener('mouseenter', function() {
        hoverTabElement.click(); // Click on the element when hovering
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #79 v0.1 💙 HOVER BASED TABS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const hoverTabElements = document.querySelectorAll('[ms-code-onhover="click"]');

    hoverTabElements.forEach(hoverTabElement => {
      hoverTabElement.addEventListener('mouseenter', function() {
        hoverTabElement.click(); // Click on the element when hovering
      });
    });
  });
</script>
Ver Memberscript
No hemos podido encontrar ningún script para esa búsqueda... por favor, inténtalo de nuevo.
Slack

¿Necesitas ayuda con MemberScripts? ¡Únete a nuestra comunidad Slack de más de 5.500 miembros! 🙌

Los MemberScripts son un recurso comunitario de Memberstack - si necesitas ayuda para que funcionen con tu proyecto, ¡únete al Slack de Memberstack 2.0 y pide ayuda!

Únete a nuestro Slack
Escaparate

Explore empresas reales que han tenido éxito con Memberstack

No se fíe sólo de nuestra palabra: eche un vistazo a las empresas de todos los tamaños que confían en Memberstack para su autenticación y sus pagos.

Ver todas las historias de éxito
Incluso Webflow utiliza Memberstack.
Empezar a construir

Empieza a construir tus sueños

Memberstack es 100% gratis hasta que estés listo para lanzarla - así que, ¿a qué estás esperando? Crea tu primera aplicación y empieza a construir hoy mismo.