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.

JSON

#53 - Actualizar elementos JSON con un formulario

Permita que sus miembros cambien detalles sobre sus artículos JSON.


<!-- 💙 MEMBERSCRIPT #53 v0.1 💙 UPDATE JSON ITEMS WITH A FORM -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;

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

      // Check if the clicked element has ms-code-edit-item attribute
      const editItem = target.closest('[ms-code-edit-item="prompt"]');
      if (editItem) {
        // Get the item key from the closest ancestor element with ms-code-item-key attribute
        const key = editItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');

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

        // SET THE TARGET - EDIT ME
        let targetObject = member.data.projects; // Update this line with the desired target location

        if (member.data && targetObject && targetObject[key]) {
          // Get the form element with the ms-code-edit-item="form" attribute
          const form = document.querySelector('form[ms-code-edit-item="form"]');

          if (form) {
            // Loop through the form fields
            for (const field of form.elements) {
              const jsonName = field.getAttribute('ms-code-json-name');
              if (jsonName && targetObject[key].hasOwnProperty(jsonName)) {
                // Pre-fill the form field with the corresponding value from the JSON item
                field.value = targetObject[key][jsonName];
              }
            }

            // Get the modal element with the ms-code-edit-item="modal" attribute
            const modal = document.querySelector('[ms-code-edit-item="modal"]');
            if (modal) {
              // Set the display property of the modal to flex
              modal.style.display = 'flex';
            }

            // Add submit event listener to the form
            form.addEventListener("submit", async function(event) {
              event.preventDefault(); // Prevent the form from submitting normally

              // Create an object to hold the updated values
              const updatedValues = {};

              // Loop through the form fields
              for (const field of form.elements) {
                const jsonName = field.getAttribute('ms-code-json-name');
                if (jsonName) {
                  // Update the corresponding value in the updatedValues object
                  updatedValues[jsonName] = field.value;
                }
              }

              // Update the target object with the new values
              targetObject[key] = { ...targetObject[key], ...updatedValues };

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

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

<!-- 💙 MEMBERSCRIPT #53 v0.1 💙 UPDATE JSON ITEMS WITH A FORM -->
<script>
  document.addEventListener("DOMContentLoaded", function() {
    const memberstack = window.$memberstackDom;

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

      // Check if the clicked element has ms-code-edit-item attribute
      const editItem = target.closest('[ms-code-edit-item="prompt"]');
      if (editItem) {
        // Get the item key from the closest ancestor element with ms-code-item-key attribute
        const key = editItem.closest('[ms-code-item-key]').getAttribute('ms-code-item-key');

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

        // SET THE TARGET - EDIT ME
        let targetObject = member.data.projects; // Update this line with the desired target location

        if (member.data && targetObject && targetObject[key]) {
          // Get the form element with the ms-code-edit-item="form" attribute
          const form = document.querySelector('form[ms-code-edit-item="form"]');

          if (form) {
            // Loop through the form fields
            for (const field of form.elements) {
              const jsonName = field.getAttribute('ms-code-json-name');
              if (jsonName && targetObject[key].hasOwnProperty(jsonName)) {
                // Pre-fill the form field with the corresponding value from the JSON item
                field.value = targetObject[key][jsonName];
              }
            }

            // Get the modal element with the ms-code-edit-item="modal" attribute
            const modal = document.querySelector('[ms-code-edit-item="modal"]');
            if (modal) {
              // Set the display property of the modal to flex
              modal.style.display = 'flex';
            }

            // Add submit event listener to the form
            form.addEventListener("submit", async function(event) {
              event.preventDefault(); // Prevent the form from submitting normally

              // Create an object to hold the updated values
              const updatedValues = {};

              // Loop through the form fields
              for (const field of form.elements) {
                const jsonName = field.getAttribute('ms-code-json-name');
                if (jsonName) {
                  // Update the corresponding value in the updatedValues object
                  updatedValues[jsonName] = field.value;
                }
              }

              // Update the target object with the new values
              targetObject[key] = { ...targetObject[key], ...updatedValues };

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

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

#52 - Retraso en la redirección de la página

Redirigir a los miembros a una nueva página con un retraso opcional.


<!-- 💙 MEMBERSCRIPT #52 v0.1 💙 DELAYED REDIRECT TO NEW PAGE -->
<script>
  const redirectToNewPage = function() {
    setTimeout(function() {
      window.location.href = "/your-page";
    }, 1000); // Delay in milliseconds
  };

  redirectToNewPage();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #52 v0.1 💙 DELAYED REDIRECT TO NEW PAGE -->
<script>
  const redirectToNewPage = function() {
    setTimeout(function() {
      window.location.href = "/your-page";
    }, 1000); // Delay in milliseconds
  };

  redirectToNewPage();
</script>
Ver Memberscript
UX

#51 - Mostrar metadatos de los miembros

Muestre los metadatos de los afiliados de forma dinámica en su sitio web.


<!-- 💙 MEMBERSCRIPT #51 v0.2 💙 DISPLAY MEMBER METADATA -->
<script>
  function replaceTextWithMetadata(metadata) {
    var els = Array.from(document.querySelectorAll('[ms-code-member-meta]'));
    els.forEach((el) => {
      const key = el.getAttribute('ms-code-member-meta');
      const value = metadata[key];
      if (value !== undefined) {
        el.innerHTML = value;
        el.value = value;
        el.src = value;
      }
    });
  }

  const memberstack = window.$memberstackDom;
  memberstack.getCurrentMember()
    .then(({ data: member }) => {
      if (member && member.metaData) {
        replaceTextWithMetadata(member.metaData);
      }
    })
    .catch((error) => {
      console.error('Error retrieving member data:', error);
    });
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #51 v0.2 💙 DISPLAY MEMBER METADATA -->
<script>
  function replaceTextWithMetadata(metadata) {
    var els = Array.from(document.querySelectorAll('[ms-code-member-meta]'));
    els.forEach((el) => {
      const key = el.getAttribute('ms-code-member-meta');
      const value = metadata[key];
      if (value !== undefined) {
        el.innerHTML = value;
        el.value = value;
        el.src = value;
      }
    });
  }

  const memberstack = window.$memberstackDom;
  memberstack.getCurrentMember()
    .then(({ data: member }) => {
      if (member && member.metaData) {
        replaceTextWithMetadata(member.metaData);
      }
    })
    .catch((error) => {
      console.error('Error retrieving member data:', error);
    });
</script>
Ver Memberscript
JSON
UX

#50 - Modo oscuro entre dispositivos

Opción de modo oscuro persistente que sigue funcionando en los diferentes dispositivos de sus miembros.

Código de la cabeza

Put this in the <head> section of your site.


<!-- 💙 MEMBERSCRIPT #50 HEAD CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script> 
  document.addEventListener('DOMContentLoaded', function() {
    const themePreference = localStorage.getItem('themePreference');
    if (themePreference === 'dark') {
      document.body.classList.add('dark');
    }
  });
</script>

Código del cuerpo

Put this in the </body> section of your site.


<!-- 💙 MEMBERSCRIPT #50 BODY CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const darkModeToggle = document.querySelector('[ms-code-dark-mode="toggle"]');
    const bodyElement = document.querySelector('.body');

    // Function to check if theme preference is saved in member JSON
    function checkThemePreference() {
      const memberstack = window.$memberstackDom;
      memberstack.getMemberJSON()
        .then(function(memberData) {
          const themePreference = memberData.data?.themePreference;
          if (themePreference === 'dark') {
            enableDarkMode();
          } else {
            disableDarkMode();
          }
        })
        .catch(function(error) {
          console.error('Error retrieving member data:', error);
        });
    }

    // Function to enable dark mode
    function enableDarkMode() {
      darkModeToggle.classList.add('dark');
      bodyElement.classList.add('dark');
      updateThemePreference('dark');
    }

    // Function to disable dark mode
    function disableDarkMode() {
      darkModeToggle.classList.remove('dark');
      bodyElement.classList.remove('dark');
      updateThemePreference('light');
    }

    // Function to update theme preference in member JSON
    function updateThemePreference(themePreference) {
      const memberstack = window.$memberstackDom;
      memberstack.getMemberJSON()
        .then(function(memberData) {
          memberData.data = memberData.data || {};
          memberData.data.themePreference = themePreference;
          memberstack.updateMemberJSON({ json: memberData.data })
            .then(function() {
              localStorage.setItem('themePreference', themePreference);
            })
            .catch(function(error) {
              console.error('Error updating member data:', error);
            });
        })
        .catch(function(error) {
          console.error('Error retrieving member data:', error);
        });
    }

    // Event listener for dark mode toggle
    darkModeToggle.addEventListener('click', function() {
      if (darkModeToggle.classList.contains('dark')) {
        disableDarkMode();
      } else {
        enableDarkMode();
      }
    });

    // Apply transition duration and timing to all elements
    const transitionDuration = '0.0s';
    const transitionTiming = 'ease';
    const elementsToTransition = [darkModeToggle, bodyElement];
    elementsToTransition.forEach(function(element) {
      element.style.transitionDuration = transitionDuration;
      element.style.transitionTimingFunction = transitionTiming;
    });

    // Check theme preference on page load
    const savedThemePreference = localStorage.getItem('themePreference');
    if (savedThemePreference === 'dark') {
      enableDarkMode();
    } else {
      disableDarkMode();
    }
    checkThemePreference();
  });
</script>
v0.1

Código de la cabeza

Put this in the <head> section of your site.


<!-- 💙 MEMBERSCRIPT #50 HEAD CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script> 
  document.addEventListener('DOMContentLoaded', function() {
    const themePreference = localStorage.getItem('themePreference');
    if (themePreference === 'dark') {
      document.body.classList.add('dark');
    }
  });
</script>

Código del cuerpo

Put this in the </body> section of your site.


<!-- 💙 MEMBERSCRIPT #50 BODY CODE v0.1 💙 CROSS-DEVICE DARK MODE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const darkModeToggle = document.querySelector('[ms-code-dark-mode="toggle"]');
    const bodyElement = document.querySelector('.body');

    // Function to check if theme preference is saved in member JSON
    function checkThemePreference() {
      const memberstack = window.$memberstackDom;
      memberstack.getMemberJSON()
        .then(function(memberData) {
          const themePreference = memberData.data?.themePreference;
          if (themePreference === 'dark') {
            enableDarkMode();
          } else {
            disableDarkMode();
          }
        })
        .catch(function(error) {
          console.error('Error retrieving member data:', error);
        });
    }

    // Function to enable dark mode
    function enableDarkMode() {
      darkModeToggle.classList.add('dark');
      bodyElement.classList.add('dark');
      updateThemePreference('dark');
    }

    // Function to disable dark mode
    function disableDarkMode() {
      darkModeToggle.classList.remove('dark');
      bodyElement.classList.remove('dark');
      updateThemePreference('light');
    }

    // Function to update theme preference in member JSON
    function updateThemePreference(themePreference) {
      const memberstack = window.$memberstackDom;
      memberstack.getMemberJSON()
        .then(function(memberData) {
          memberData.data = memberData.data || {};
          memberData.data.themePreference = themePreference;
          memberstack.updateMemberJSON({ json: memberData.data })
            .then(function() {
              localStorage.setItem('themePreference', themePreference);
            })
            .catch(function(error) {
              console.error('Error updating member data:', error);
            });
        })
        .catch(function(error) {
          console.error('Error retrieving member data:', error);
        });
    }

    // Event listener for dark mode toggle
    darkModeToggle.addEventListener('click', function() {
      if (darkModeToggle.classList.contains('dark')) {
        disableDarkMode();
      } else {
        enableDarkMode();
      }
    });

    // Apply transition duration and timing to all elements
    const transitionDuration = '0.0s';
    const transitionTiming = 'ease';
    const elementsToTransition = [darkModeToggle, bodyElement];
    elementsToTransition.forEach(function(element) {
      element.style.transitionDuration = transitionDuration;
      element.style.transitionTimingFunction = transitionTiming;
    });

    // Check theme preference on page load
    const savedThemePreference = localStorage.getItem('themePreference');
    if (savedThemePreference === 'dark') {
      enableDarkMode();
    } else {
      disableDarkMode();
    }
    checkThemePreference();
  });
</script>
Ver Memberscript
Campos personalizados
UX

#49 - Desactivar la primera opción en una entrada de selección

Evite que los usuarios seleccionen la opción de marcador de posición en sus entradas de selección.


<!-- 💙 MEMBERSCRIPT #49 v0.1 💙 DISABLE FIRST OPTION IN SELECT INPUT -->
<script>
    let selects = document.querySelectorAll("select[ms-code=hide-first-option]");
    selects.forEach((select) => { 
        let options = select.getElementsByTagName("option");
        options[0].hidden = true;
    });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #49 v0.1 💙 DISABLE FIRST OPTION IN SELECT INPUT -->
<script>
    let selects = document.querySelectorAll("select[ms-code=hide-first-option]");
    selects.forEach((select) => { 
        let options = select.getElementsByTagName("option");
        options[0].hidden = true;
    });
</script>
Ver Memberscript
Campos personalizados
UX

#48 - Autocompletar entradas de dirección

Rellene previamente todas las entradas de direcciones mediante la API de Google Places.

Código de la cabeza

Place this in your page <head>


<!-- 💙 MEMBERSCRIPT #48 HEAD CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR-API-KEY&libraries=places&callback=initAutocomplete" async defer> </script>
<style>
.pac-logo::after {
display: none;
}
.pac-container {
border-radius: 5px;
border: 1px solid #ccc;
}
.pac-item {
padding: 0 10px;
}
</style>

Código del cuerpo

Place this in your page </body>


<!-- 💙 MEMBERSCRIPT #48 BODY CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script>
let autocomplete;

function initAutocomplete() {
  autocomplete = new google.maps.places.Autocomplete(
    document.querySelector('input[ms-code-input="address"]'),
    {
      componentRestrictions: { country: ['US'] },
      fields: ['address_components'],
      types: ['address']
    }
  );

  autocomplete.addListener('place_changed', function() {
    const place = autocomplete.getPlace();

    if (place) {
      const addressInput = document.querySelector('input[ms-code-input="address"]');
      const cityInput = document.querySelector('input[ms-code-input="city"]');
      const regionInput = document.querySelector('input[ms-code-input="region"]');
      const countryInput = document.querySelector('input[ms-code-input="country"]');
      const postalCodeInput = document.querySelector('input[ms-code-input="postal-code"]');

      addressInput.value = extractAddress(place);
      cityInput.value = extractCity(place);
      regionInput.value = extractRegion(place);
      countryInput.value = extractCountry(place);
      postalCodeInput.value = extractPostalCode(place);
    }
  });
}

function extractAddress(place) {
  let address = '';
  const streetNumber = extractComponent(place, 'street_number');
  const route = extractComponent(place, 'route');

  if (streetNumber) {
    address += streetNumber + ' ';
  }
  if (route) {
    address += route;
  }

  return address.trim();
}

function extractComponent(place, componentType) {
  for (const component of place.address_components) {
    if (component.types.includes(componentType)) {
      return component.long_name;
    }
  }
  return '';
}

function extractCity(place) {
  for (const component of place.address_components) {
    if (component.types.includes('locality')) {
      return component.long_name;
    }
  }
  return '';
}

function extractRegion(place) {
  for (const component of place.address_components) {
    if (component.types.includes('administrative_area_level_1')) {
      return component.long_name;
    }
  }
  return '';
}

function extractCountry(place) {
  for (const component of place.address_components) {
    if (component.types.includes('country')) {
      return component.long_name;
    }
  }
  return '';
}

function extractPostalCode(place) {
  for (const component of place.address_components) {
    if (component.types.includes('postal_code')) {
      return component.long_name;
    }
  }
  return '';
}
</script>
v0.1

Código de la cabeza

Place this in your page <head>


<!-- 💙 MEMBERSCRIPT #48 HEAD CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR-API-KEY&libraries=places&callback=initAutocomplete" async defer> </script>
<style>
.pac-logo::after {
display: none;
}
.pac-container {
border-radius: 5px;
border: 1px solid #ccc;
}
.pac-item {
padding: 0 10px;
}
</style>

Código del cuerpo

Place this in your page </body>


<!-- 💙 MEMBERSCRIPT #48 BODY CODE v0.1 💙 AUTOFILL ADDRESS INPUTS -->
<script>
let autocomplete;

function initAutocomplete() {
  autocomplete = new google.maps.places.Autocomplete(
    document.querySelector('input[ms-code-input="address"]'),
    {
      componentRestrictions: { country: ['US'] },
      fields: ['address_components'],
      types: ['address']
    }
  );

  autocomplete.addListener('place_changed', function() {
    const place = autocomplete.getPlace();

    if (place) {
      const addressInput = document.querySelector('input[ms-code-input="address"]');
      const cityInput = document.querySelector('input[ms-code-input="city"]');
      const regionInput = document.querySelector('input[ms-code-input="region"]');
      const countryInput = document.querySelector('input[ms-code-input="country"]');
      const postalCodeInput = document.querySelector('input[ms-code-input="postal-code"]');

      addressInput.value = extractAddress(place);
      cityInput.value = extractCity(place);
      regionInput.value = extractRegion(place);
      countryInput.value = extractCountry(place);
      postalCodeInput.value = extractPostalCode(place);
    }
  });
}

function extractAddress(place) {
  let address = '';
  const streetNumber = extractComponent(place, 'street_number');
  const route = extractComponent(place, 'route');

  if (streetNumber) {
    address += streetNumber + ' ';
  }
  if (route) {
    address += route;
  }

  return address.trim();
}

function extractComponent(place, componentType) {
  for (const component of place.address_components) {
    if (component.types.includes(componentType)) {
      return component.long_name;
    }
  }
  return '';
}

function extractCity(place) {
  for (const component of place.address_components) {
    if (component.types.includes('locality')) {
      return component.long_name;
    }
  }
  return '';
}

function extractRegion(place) {
  for (const component of place.address_components) {
    if (component.types.includes('administrative_area_level_1')) {
      return component.long_name;
    }
  }
  return '';
}

function extractCountry(place) {
  for (const component of place.address_components) {
    if (component.types.includes('country')) {
      return component.long_name;
    }
  }
  return '';
}

function extractPostalCode(place) {
  for (const component of place.address_components) {
    if (component.types.includes('postal_code')) {
      return component.long_name;
    }
  }
  return '';
}
</script>
Ver Memberscript
JSON
UX

#47 - Mostrar fecha del miembro JSON

Indique a los afiliados una fecha, por ejemplo, cuándo vence su plan.


<!-- 💙 MEMBERSCRIPT #47 v0.1 💙 DISPLAY ONE TIME DATE -->
<script>
  document.addEventListener("DOMContentLoaded", async function() {
    const memberstack = window.$memberstackDom;

    const formatDate = function(date) {
      const options = { month: 'long', day: 'numeric', year: 'numeric' };
      return new Date(date).toLocaleDateString('en-US', options);
      // Replace 'en-US' with one of these depending on your locale: en-US, en-GB, en-CA, en-AU, fr-FR, de-DE, es-ES, it-IT, ja-JP, ko-KR, pt-BR, ru-RU, zn-CH, ar-SA
    };

    const updateTextSpans = async function() {
      const member = await memberstack.getMemberJSON();

      if (!member.data || !member.data['one-time-date']) {
        // Member data or one-time date not available, do nothing
        return;
      }

      const oneTimeDate = formatDate(member.data['one-time-date']);
      const textSpans = document.querySelectorAll('[ms-code-display-text="one-time-date"]');

      textSpans.forEach(span => {
        span.textContent = oneTimeDate;
      });
    };

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

<!-- 💙 MEMBERSCRIPT #47 v0.1 💙 DISPLAY ONE TIME DATE -->
<script>
  document.addEventListener("DOMContentLoaded", async function() {
    const memberstack = window.$memberstackDom;

    const formatDate = function(date) {
      const options = { month: 'long', day: 'numeric', year: 'numeric' };
      return new Date(date).toLocaleDateString('en-US', options);
      // Replace 'en-US' with one of these depending on your locale: en-US, en-GB, en-CA, en-AU, fr-FR, de-DE, es-ES, it-IT, ja-JP, ko-KR, pt-BR, ru-RU, zn-CH, ar-SA
    };

    const updateTextSpans = async function() {
      const member = await memberstack.getMemberJSON();

      if (!member.data || !member.data['one-time-date']) {
        // Member data or one-time date not available, do nothing
        return;
      }

      const oneTimeDate = formatDate(member.data['one-time-date']);
      const textSpans = document.querySelectorAll('[ms-code-display-text="one-time-date"]');

      textSpans.forEach(span => {
        span.textContent = oneTimeDate;
      });
    };

    updateTextSpans();
  });
</script>
Ver Memberscript
Campos personalizados
UX

#46 - Confirmar contraseña

Añade una entrada de confirmación de contraseña a tus formularios de registro y restablecimiento de contraseña.


<!-- 💙 MEMBERSCRIPT #46 v0.1 💙 CONFIRM PASSWORD INPUT -->
<script>

var password = document.querySelector('[data-ms-member=password]')
  , confirm_password = document.querySelector('[ms-code-password=confirm]')

function validatePassword(){
  if(password.value != confirm_password.value) {
 		confirm_password.setCustomValidity("Passwords Don't Match");
    confirm_password.classList.add("invalid")
    confirm_password.classList.remove("valid")
  } else {
 		confirm_password.setCustomValidity('');
    confirm_password.classList.remove("invalid")
    confirm_password.classList.add("valid")
  }
}

password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #46 v0.1 💙 CONFIRM PASSWORD INPUT -->
<script>

var password = document.querySelector('[data-ms-member=password]')
  , confirm_password = document.querySelector('[ms-code-password=confirm]')

function validatePassword(){
  if(password.value != confirm_password.value) {
 		confirm_password.setCustomValidity("Passwords Don't Match");
    confirm_password.classList.add("invalid")
    confirm_password.classList.remove("valid")
  } else {
 		confirm_password.setCustomValidity('');
    confirm_password.classList.remove("invalid")
    confirm_password.classList.add("valid")
  }
}

password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>
Ver Memberscript
Campos personalizados
UX

#45 - Mostrar/Ocultar contraseña

Añade un botón de mostrar/ocultar contraseña a cualquier formulario con una entrada de contraseña.


<!-- 💙 MEMBERSCRIPT #45 v0.2 💙 SHOW AND HIDE PASSWORD -->
<script>
  document.querySelectorAll("[ms-code-password='transform']").forEach(function(button) {
    button.addEventListener("click", transform);
  });

  var isPassword = true;

  function transform() {
    var passwordInputs = document.querySelectorAll("[data-ms-member='password'], [data-ms-member='new-password'], [data-ms-member='current-password']");

    passwordInputs.forEach(function(myInput) {
      var inputType = myInput.getAttribute("type");

      if (isPassword) {
        myInput.setAttribute("type", "text");
      } else {
        myInput.setAttribute("type", "password");
      }
    });

    isPassword = !isPassword;
  }
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #45 v0.2 💙 SHOW AND HIDE PASSWORD -->
<script>
  document.querySelectorAll("[ms-code-password='transform']").forEach(function(button) {
    button.addEventListener("click", transform);
  });

  var isPassword = true;

  function transform() {
    var passwordInputs = document.querySelectorAll("[data-ms-member='password'], [data-ms-member='new-password'], [data-ms-member='current-password']");

    passwordInputs.forEach(function(myInput) {
      var inputType = myInput.getAttribute("type");

      if (isPassword) {
        myInput.setAttribute("type", "text");
      } else {
        myInput.setAttribute("type", "password");
      }
    });

    isPassword = !isPassword;
  }
</script>
Ver Memberscript
Visibilidad condicional

#44 - Mostrar elemento si atributo coincide con ID de miembro

Mostrar elementos condicionalmente si tienen un atributo que coincida con el ID de los miembros.


<!-- 💙 MEMBERSCRIPT #44 v0.1 💙 SHOW ELEMENT IF ATTRIBUTE MATCHES MEMBER ID -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  if (localStorage.getItem("_ms-mem")) {
    const memberData = JSON.parse(localStorage.getItem("_ms-mem"));
    const memberId = memberData.id;

    const elements = document.querySelectorAll("[ms-code-member-id='" + memberId + "']");
    elements.forEach(element => {
      element.style.display = "block";
    });
  }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #44 v0.1 💙 SHOW ELEMENT IF ATTRIBUTE MATCHES MEMBER ID -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  if (localStorage.getItem("_ms-mem")) {
    const memberData = JSON.parse(localStorage.getItem("_ms-mem"));
    const memberId = memberData.id;

    const elements = document.querySelectorAll("[ms-code-member-id='" + memberId + "']");
    elements.forEach(element => {
      element.style.display = "block";
    });
  }
});
</script>
Ver Memberscript
Modales

#43 - Bloquear el desplazamiento cuando el modal está abierto

Evitar que la página se desplace cuando alguien abre un modal.


<!-- 💙 MEMBERSCRIPT #43 v0.1 💙 BLOCK SCROLLING WHEN MODAL IS OPEN -->
<style>
.no-scroll {
  overflow: hidden;
}
</style>
<script>
function isDesktopViewport() {
  return window.innerWidth >= 900; // Adjust the breakpoint width as needed
}

const codeBlocks = document.querySelectorAll('[ms-code-block-scroll]');

function handleScrollBlock(event) {
  if (isDesktopViewport()) {
    document.body.classList.add('no-scroll');
  }
}

function handleScrollUnblock(event) {
  if (isDesktopViewport()) {
    document.body.classList.remove('no-scroll');
  }
}

codeBlocks.forEach(codeBlock => {
  codeBlock.addEventListener('mouseenter', handleScrollBlock);
  codeBlock.addEventListener('mouseleave', handleScrollUnblock);
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #43 v0.1 💙 BLOCK SCROLLING WHEN MODAL IS OPEN -->
<style>
.no-scroll {
  overflow: hidden;
}
</style>
<script>
function isDesktopViewport() {
  return window.innerWidth >= 900; // Adjust the breakpoint width as needed
}

const codeBlocks = document.querySelectorAll('[ms-code-block-scroll]');

function handleScrollBlock(event) {
  if (isDesktopViewport()) {
    document.body.classList.add('no-scroll');
  }
}

function handleScrollUnblock(event) {
  if (isDesktopViewport()) {
    document.body.classList.remove('no-scroll');
  }
}

codeBlocks.forEach(codeBlock => {
  codeBlock.addEventListener('mouseenter', handleScrollBlock);
  codeBlock.addEventListener('mouseleave', handleScrollUnblock);
});
</script>
Ver Memberscript
Campos personalizados

#42 - Campo de formulario del editor de imágenes

Permite que los usuarios suban y editen fotos y, a continuación, envíalas a Google Drive.

Código de la cabeza

Place this in your page <head>


<!-- 💙 MEMBERSCRIPT #42 HEAD CODE v0.2 💙 FILE EDITOR FEATURE -->
<link rel="stylesheet" href="https://unpkg.com/filepond@^4/dist/filepond.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" />

Código del cuerpo

Place this in your page </body>


<!-- 💙 MEMBERSCRIPT #42 BODY CODE v0.2 💙 FILE EDITOR FEATURE -->
<script> src="https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.js"> </script>
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script> src="https://scaleflex.cloudimg.io/v7/plugins/filerobot-image-editor/latest/filerobot-image-editor.min.js"> </script>
<style>
  .dXhZSB {
  background-color: #2962ff;
  }
  .FIE_root * {
  font-family: inherit !important;
  }
  .SfxModal-Wrapper * {
  font-family: inherit !important;
  }
  .jpHEiD {
  font-family: inherit !important;
  }
  #editor_container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 999;
  }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
  // Register the plugins
  FilePond.registerPlugin(FilePondPluginImagePreview);
  FilePond.registerPlugin(FilePondPluginImageEdit);

  const inputElement = document.querySelector('input[type="file"]');
  const pond = FilePond.create(inputElement, {
    credits: false,
    name: 'fileToUpload',
    storeAsFile: true,
    imageEditEditor: {
      open: (file, instructions) => {
        console.log('Open editor', file, instructions);
        openFilerobotImageEditor(file, instructions);
      },
      onconfirm: (output) => {
        console.log('Confirm editor', output);
        handleImageEditConfirm(output);
      },
      oncancel: () => {
        console.log('Cancel editor');
        handleImageEditCancel();
      },
      onclose: () => {
        console.log('Close editor');
        handleImageEditClose();
      }
    }
  });

  function openFilerobotImageEditor(file, instructions) {
    const imageURL = URL.createObjectURL(file);
    const config = {
      source: imageURL,
      onSave: (updatedImage) => {
        confirmCallback(updatedImage);
      },
      annotationsCommon: {
        fill: '#ff0000'
      },
      Text: {
        text: 'Add your text here',
        font: 'inherit'
      }, // Set font to inherit from the page body
      Rotate: {
        angle: instructions.rotation,
        componentType: 'slider'
      },
      tabsIds: [
        'Adjust',
        'Annotate',
        'Watermark'
      ],
      defaultTabId: 'Annotate',
      defaultToolId: 'Text'
    };

    const editorContainer = document.createElement('div');
    editorContainer.id = 'editor_container';
    document.body.appendChild(editorContainer);

    const filerobotImageEditor = new window.FilerobotImageEditor(editorContainer, config);

    const confirmCallback = (output) => {
      console.log('Confirmed:', output);

      const dataURL = output.imageBase64;
      const file = dataURLToFile(dataURL, output.name);

      // Add the file to FilePond
      pond.addFiles([file]);

      document.body.removeChild(editorContainer); // Remove the editor container
    };

    function dataURLToFile(dataURL, fileName) {
      const arr = dataURL.split(',');
      const mime = arr[0].match(/:(.*?);/)[1];
      const fileExtension = mime.split('/')[1];
      const updatedFileName = fileName + '.' + fileExtension;
      const bstr = atob(arr[1]);
      const n = bstr.length;
      const u8arr = new Uint8Array(n);
      for (let i = 0; i < n; i++) {
        u8arr[i] = bstr.charCodeAt(i);
      }
      return new File([u8arr], updatedFileName, { type: mime });
    }

    const cancelCallback = () => {
      console.log('Canceled');
      document.body.removeChild(editorContainer); // Remove the editor container
    };

    const closeButton = document.createElement('button');
    closeButton.textContent = 'Close';
    closeButton.addEventListener('click', () => {
      filerobotImageEditor.onClose();
    });

    const buttonContainer = document.createElement('div');
    buttonContainer.appendChild(closeButton);

    editorContainer.appendChild(buttonContainer);

    filerobotImageEditor.render({
      onClose: (closingReason) => {
        console.log('Closing reason', closingReason);
        filerobotImageEditor.terminate();
      },
    });
  }

  function handleImageEditConfirm(output) {
    console.log('Image edit confirmed:', output);
    // Handle the confirmed output here
  }

  function handleImageEditCancel() {
    console.log('Image edit canceled');
    // Handle the canceled edit here
  }

  function handleImageEditClose() {
    console.log('Image editor closed');
    // Handle the editor close here
  }
});
</script>

v0.2

Código de la cabeza

Place this in your page <head>


<!-- 💙 MEMBERSCRIPT #42 HEAD CODE v0.2 💙 FILE EDITOR FEATURE -->
<link rel="stylesheet" href="https://unpkg.com/filepond@^4/dist/filepond.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.css" />
<link rel="stylesheet" href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" />

Código del cuerpo

Place this in your page </body>


<!-- 💙 MEMBERSCRIPT #42 BODY CODE v0.2 💙 FILE EDITOR FEATURE -->
<script> src="https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"> </script>
<script> src="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.js"> </script>
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script> src="https://scaleflex.cloudimg.io/v7/plugins/filerobot-image-editor/latest/filerobot-image-editor.min.js"> </script>
<style>
  .dXhZSB {
  background-color: #2962ff;
  }
  .FIE_root * {
  font-family: inherit !important;
  }
  .SfxModal-Wrapper * {
  font-family: inherit !important;
  }
  .jpHEiD {
  font-family: inherit !important;
  }
  #editor_container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 999;
  }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
  // Register the plugins
  FilePond.registerPlugin(FilePondPluginImagePreview);
  FilePond.registerPlugin(FilePondPluginImageEdit);

  const inputElement = document.querySelector('input[type="file"]');
  const pond = FilePond.create(inputElement, {
    credits: false,
    name: 'fileToUpload',
    storeAsFile: true,
    imageEditEditor: {
      open: (file, instructions) => {
        console.log('Open editor', file, instructions);
        openFilerobotImageEditor(file, instructions);
      },
      onconfirm: (output) => {
        console.log('Confirm editor', output);
        handleImageEditConfirm(output);
      },
      oncancel: () => {
        console.log('Cancel editor');
        handleImageEditCancel();
      },
      onclose: () => {
        console.log('Close editor');
        handleImageEditClose();
      }
    }
  });

  function openFilerobotImageEditor(file, instructions) {
    const imageURL = URL.createObjectURL(file);
    const config = {
      source: imageURL,
      onSave: (updatedImage) => {
        confirmCallback(updatedImage);
      },
      annotationsCommon: {
        fill: '#ff0000'
      },
      Text: {
        text: 'Add your text here',
        font: 'inherit'
      }, // Set font to inherit from the page body
      Rotate: {
        angle: instructions.rotation,
        componentType: 'slider'
      },
      tabsIds: [
        'Adjust',
        'Annotate',
        'Watermark'
      ],
      defaultTabId: 'Annotate',
      defaultToolId: 'Text'
    };

    const editorContainer = document.createElement('div');
    editorContainer.id = 'editor_container';
    document.body.appendChild(editorContainer);

    const filerobotImageEditor = new window.FilerobotImageEditor(editorContainer, config);

    const confirmCallback = (output) => {
      console.log('Confirmed:', output);

      const dataURL = output.imageBase64;
      const file = dataURLToFile(dataURL, output.name);

      // Add the file to FilePond
      pond.addFiles([file]);

      document.body.removeChild(editorContainer); // Remove the editor container
    };

    function dataURLToFile(dataURL, fileName) {
      const arr = dataURL.split(',');
      const mime = arr[0].match(/:(.*?);/)[1];
      const fileExtension = mime.split('/')[1];
      const updatedFileName = fileName + '.' + fileExtension;
      const bstr = atob(arr[1]);
      const n = bstr.length;
      const u8arr = new Uint8Array(n);
      for (let i = 0; i < n; i++) {
        u8arr[i] = bstr.charCodeAt(i);
      }
      return new File([u8arr], updatedFileName, { type: mime });
    }

    const cancelCallback = () => {
      console.log('Canceled');
      document.body.removeChild(editorContainer); // Remove the editor container
    };

    const closeButton = document.createElement('button');
    closeButton.textContent = 'Close';
    closeButton.addEventListener('click', () => {
      filerobotImageEditor.onClose();
    });

    const buttonContainer = document.createElement('div');
    buttonContainer.appendChild(closeButton);

    editorContainer.appendChild(buttonContainer);

    filerobotImageEditor.render({
      onClose: (closingReason) => {
        console.log('Closing reason', closingReason);
        filerobotImageEditor.terminate();
      },
    });
  }

  function handleImageEditConfirm(output) {
    console.log('Image edit confirmed:', output);
    // Handle the confirmed output here
  }

  function handleImageEditCancel() {
    console.log('Image edit canceled');
    // Handle the canceled edit here
  }

  function handleImageEditClose() {
    console.log('Image editor closed');
    // Handle the editor close here
  }
});
</script>

Ver Memberscript
Campos personalizados

#41 - Entradas de números de teléfono perfectas

Entradas de números de teléfono internacionales, como debe ser.

Con búsqueda de IP

Utilice esta opción si desea que el país IP de los usuarios se rellene automáticamente. IMPORTANTE: No utilice esta opción con formularios de perfil o se comportará de forma errática.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      $.get("https://ipinfo.io", function(response) {
        var countryCode = response.country;
        iti.setCountry(countryCode);
      }, "jsonp");

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>

Sin búsqueda de IP

Utilícelo en formularios de perfil y/o si no desea que se rellene automáticamente en función de la IP del usuario.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>
v0.2

Con búsqueda de IP

Utilice esta opción si desea que el país IP de los usuarios se rellene automáticamente. IMPORTANTE: No utilice esta opción con formularios de perfil o se comportará de forma errática.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITH IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      $.get("https://ipinfo.io", function(response) {
        var countryCode = response.country;
        iti.setCountry(countryCode);
      }, "jsonp");

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>

Sin búsqueda de IP

Utilícelo en formularios de perfil y/o si no desea que se rellene automáticamente en función de la IP del usuario.


<!-- 💙 MEMBERSCRIPT #41 v0.2 💙 PERFECT PHONE NUMBER INPUTS (WITHOUT IP LOOKUP) -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"> </script>
<script>
  $(document).ready(function() {
    $('input[ms-code-phone-number]').each(function() {
      var input = this;
      var preferredCountries = $(input).attr('ms-code-phone-number').split(',');

      var iti = window.intlTelInput(input, {
        preferredCountries: preferredCountries,
        utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js"
      });

      input.addEventListener('change', formatPhoneNumber);
      input.addEventListener('keyup', formatPhoneNumber);

      function formatPhoneNumber() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      }

      var form = $(input).closest('form');
      form.submit(function() {
        var formattedNumber = iti.getNumber(intlTelInputUtils.numberFormat.INTERNATIONAL);
        input.value = formattedNumber;
      });
    });
  });
</script>
Ver Memberscript
Campos personalizados
UX

#40 - Cargador de archivos de arrastrar y soltar

Añada fácilmente una función de carga de archivos mediante arrastrar y soltar a su sitio Webflow.

Importante

Si estás usando MemberScript #38 , ¡asegúrate de poner este script DESPUÉS!


<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
      credits: false,
      name: 'fileToUpload',
      storeAsFile: true
      // for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
    });
  });
</script>
v0.1

Importante

Si estás usando MemberScript #38 , ¡asegúrate de poner este script DESPUÉS!


<!-- 💙 MEMBERSCRIPT #40 v0.1 💙 DRAG AND DROP FILE UPLOADER -->
<script> src="https://unpkg.com/filepond@^4/dist/filepond.js"> </script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
      credits: false,
      name: 'fileToUpload',
      storeAsFile: true
      // for more property options, go to https://pqina.nl/filepond/docs/api/instance/properties/
    });
  });
</script>
Ver Memberscript
Campos personalizados
UX

#39 - Seleccionar mejor los campos

Añade búsquedas y una mejor interfaz de usuario para seleccionar y multiseleccionar campos.

Código de la cabeza

Put this in the <head> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />

Código del cuerpo

Put this in the </body> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
    $('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
v0.1

Código de la cabeza

Put this in the <head> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 HEAD CODE 💙 BETTER SELECT FIELDS -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" />

Código del cuerpo

Put this in the </body> section of your page.


<!-- 💙 MEMBERSCRIPT #39 v0.1 BODY CODE 💙 BETTER SELECT FIELDS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"> </script>
<script>
$(document).ready(function() {
    $('[ms-code-custom-select="select-with-search"]').select2();
});
</script>
Ver Memberscript
Campos personalizados

#38 - Campo de carga de archivos

Añade un cargador de archivos a cualquier sitio y envía el envío a Google Drive, correo electrónico o donde quieras.


<!-- 💙 MEMBERSCRIPT #38 v0.1 💙 FORM FILE UPLOADER -->
<script>
const forms = document.querySelectorAll('form[ms-code-file-upload="form"]');

forms.forEach((form) => {
  form.setAttribute('enctype', 'multipart/form-data');
  const uploadInputs = form.querySelectorAll('[ms-code-file-upload-input]');

  uploadInputs.forEach((uploadInput) => {
    const inputName = uploadInput.getAttribute('ms-code-file-upload-input');

    const fileInput = document.createElement('input');
    fileInput.setAttribute('type', 'file');
    fileInput.setAttribute('name', inputName);
    fileInput.setAttribute('id', inputName);
    fileInput.required = true; // delete this line to make the input optional

    uploadInput.appendChild(fileInput);
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #38 v0.1 💙 FORM FILE UPLOADER -->
<script>
const forms = document.querySelectorAll('form[ms-code-file-upload="form"]');

forms.forEach((form) => {
  form.setAttribute('enctype', 'multipart/form-data');
  const uploadInputs = form.querySelectorAll('[ms-code-file-upload-input]');

  uploadInputs.forEach((uploadInput) => {
    const inputName = uploadInput.getAttribute('ms-code-file-upload-input');

    const fileInput = document.createElement('input');
    fileInput.setAttribute('type', 'file');
    fileInput.setAttribute('name', inputName);
    fileInput.setAttribute('id', inputName);
    fileInput.required = true; // delete this line to make the input optional

    uploadInput.appendChild(fileInput);
  });
});
</script>
Ver Memberscript
JSON
Marketing

#37 - Eliminar automáticamente el plan gratuito

Elimine automáticamente un plan gratuito después de un tiempo determinado.


<!-- 💙 MEMBERSCRIPT #37 v0.1 💙 MAKE FREE TRIAL EXPIRE AFTER SET TIME -->
<script>
let memberPlanId = "your_plan_ID"; // replace with your actual FREE plan ID

document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  // Fetch the member's data
  const member = await memberstack.getMemberJSON();

  // Fetch the member's planConnections from local storage
  const memberDataFromLocalStorage = JSON.parse(localStorage.getItem('_ms-mem'));
  const planConnections = memberDataFromLocalStorage.planConnections;

  // Check if the member has x plan
  let hasPlan = false;
  if (planConnections) {
    hasPlan = planConnections.some(planConnection => planConnection.planId === memberPlanId);
  }

  if (hasPlan) {
    // Check the members one-time-date
    let currentDate = new Date();
    let oneTimeDate = new Date(member.data['one-time-date']);

    if (currentDate > oneTimeDate) {
      // If the members' one time date has passed, remove x plan
      memberstack.removePlan({
        planId: memberPlanId
      }).then(() => {
        // Redirect to /free-trial-expired
        window.location.href = "/free-trial-expired";
      }).catch(error => {
        // Handle error
      });
    }
  }
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #37 v0.1 💙 MAKE FREE TRIAL EXPIRE AFTER SET TIME -->
<script>
let memberPlanId = "your_plan_ID"; // replace with your actual FREE plan ID

document.addEventListener("DOMContentLoaded", async function() {
  const memberstack = window.$memberstackDom;

  // Fetch the member's data
  const member = await memberstack.getMemberJSON();

  // Fetch the member's planConnections from local storage
  const memberDataFromLocalStorage = JSON.parse(localStorage.getItem('_ms-mem'));
  const planConnections = memberDataFromLocalStorage.planConnections;

  // Check if the member has x plan
  let hasPlan = false;
  if (planConnections) {
    hasPlan = planConnections.some(planConnection => planConnection.planId === memberPlanId);
  }

  if (hasPlan) {
    // Check the members one-time-date
    let currentDate = new Date();
    let oneTimeDate = new Date(member.data['one-time-date']);

    if (currentDate > oneTimeDate) {
      // If the members' one time date has passed, remove x plan
      memberstack.removePlan({
        planId: memberPlanId
      }).then(() => {
        // Redirect to /free-trial-expired
        window.location.href = "/free-trial-expired";
      }).catch(error => {
        // Handle error
      });
    }
  }
});
</script>
Ver Memberscript
Visibilidad condicional
UX

#36 - Validación de contraseñas

Utilice este sencillo método para confirmar que sus usuarios han introducido una contraseña segura.


<!-- 💙 MEMBERSCRIPT #36 v0.1 💙 PASSWORD VALIDATION -->
<script>
window.addEventListener('load', function() {
    const passwordInput = document.querySelector('input[data-ms-member="password"]');
    const submitButton = document.querySelector('[ms-code-submit-button]');
    
    if (!passwordInput || !submitButton) return;  // Return if essential elements are not found

    function checkAllValid() {
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        return Array.from(validationPoints).every(validationPoint => {
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            return validIcon && validIcon.style.display === 'flex';  // Check for validIcon existence before accessing style
        });
    }

    passwordInput.addEventListener('keyup', function() {
        const password = passwordInput.value;
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        
        validationPoints.forEach(function(validationPoint) {
            const rule = validationPoint.getAttribute('ms-code-pw-validation');
            let isValid = false;
            
            // MINIMUM LENGTH VALIDATION POINT
            if (rule.startsWith('minlength-')) {
                const minLength = parseInt(rule.split('-')[1]);
                isValid = password.length >= minLength;
            }

            // SPECIAL CHARACTER VALIDATION POINT
            else if (rule === 'special-character') {
                isValid = /[!@#$%^&*(),.?":{}|<>]/g.test(password);
            }

            // UPPER AND LOWER CASE VALIDATION POINT
            else if (rule === 'upper-lower-case') {
                isValid = /[a-z]/.test(password) && /[A-Z]/.test(password);
            }

            // NUMBER VALIDATION POINT
            else if (rule === 'number') {
                isValid = /\d/.test(password);
            }
            
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            const invalidIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="false"]');
            
            if (validIcon && invalidIcon) {  // Check for existence before accessing style
                if (isValid) {
                    validIcon.style.display = 'flex';
                    invalidIcon.style.display = 'none';
                } else {
                    validIcon.style.display = 'none';
                    invalidIcon.style.display = 'flex';
                }
            }
        });

        if (checkAllValid()) {
            submitButton.classList.remove('disabled');
        } else {
            submitButton.classList.add('disabled');
        }
    });

    // Trigger keyup event after adding event listener
    var event = new Event('keyup');
    passwordInput.dispatchEvent(event);
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #36 v0.1 💙 PASSWORD VALIDATION -->
<script>
window.addEventListener('load', function() {
    const passwordInput = document.querySelector('input[data-ms-member="password"]');
    const submitButton = document.querySelector('[ms-code-submit-button]');
    
    if (!passwordInput || !submitButton) return;  // Return if essential elements are not found

    function checkAllValid() {
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        return Array.from(validationPoints).every(validationPoint => {
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            return validIcon && validIcon.style.display === 'flex';  // Check for validIcon existence before accessing style
        });
    }

    passwordInput.addEventListener('keyup', function() {
        const password = passwordInput.value;
        const validationPoints = document.querySelectorAll('[ms-code-pw-validation]');
        
        validationPoints.forEach(function(validationPoint) {
            const rule = validationPoint.getAttribute('ms-code-pw-validation');
            let isValid = false;
            
            // MINIMUM LENGTH VALIDATION POINT
            if (rule.startsWith('minlength-')) {
                const minLength = parseInt(rule.split('-')[1]);
                isValid = password.length >= minLength;
            }

            // SPECIAL CHARACTER VALIDATION POINT
            else if (rule === 'special-character') {
                isValid = /[!@#$%^&*(),.?":{}|<>]/g.test(password);
            }

            // UPPER AND LOWER CASE VALIDATION POINT
            else if (rule === 'upper-lower-case') {
                isValid = /[a-z]/.test(password) && /[A-Z]/.test(password);
            }

            // NUMBER VALIDATION POINT
            else if (rule === 'number') {
                isValid = /\d/.test(password);
            }
            
            const validIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="true"]');
            const invalidIcon = validationPoint.querySelector('[ms-code-pw-validation-icon="false"]');
            
            if (validIcon && invalidIcon) {  // Check for existence before accessing style
                if (isValid) {
                    validIcon.style.display = 'flex';
                    invalidIcon.style.display = 'none';
                } else {
                    validIcon.style.display = 'none';
                    invalidIcon.style.display = 'flex';
                }
            }
        });

        if (checkAllValid()) {
            submitButton.classList.remove('disabled');
        } else {
            submitButton.classList.add('disabled');
        }
    });

    // Trigger keyup event after adding event listener
    var event = new Event('keyup');
    passwordInput.dispatchEvent(event);
});
</script>
Ver Memberscript
SEO

#35 - Añadir fácilmente FAQ Schema/Rich Snippets

Añade un script y 2 atributos para habilitar la actualización constante de rich snippets en tu página.


<!-- 💙 MEMBERSCRIPT #35 v0.1 💙 FAQ RICH SNIPPETS -->
<script>
let faqArray = [];
let questionElements = document.querySelectorAll('[ms-code-snippet-q]');
let answerElements = document.querySelectorAll('[ms-code-snippet-a]');

for (let i = 0; i < questionElements.length; i++) {
  let question = questionElements[i].innerText;
  let answer = '';

  for (let j = 0; j < answerElements.length; j++) {
    if (questionElements[i].getAttribute('ms-code-snippet-q') === answerElements[j].getAttribute('ms-code-snippet-a')) {
      answer = answerElements[j].innerText;
      break;
    }
  }

  faqArray.push({
    "@type": "Question",
    "name": question,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": answer
    }
  });
}

let faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqArray
}

let script = document.createElement('script');
script.type = "application/ld+json";
script.innerHTML = JSON.stringify(faqSchema);

document.getElementsByTagName('head')[0].appendChild(script);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #35 v0.1 💙 FAQ RICH SNIPPETS -->
<script>
let faqArray = [];
let questionElements = document.querySelectorAll('[ms-code-snippet-q]');
let answerElements = document.querySelectorAll('[ms-code-snippet-a]');

for (let i = 0; i < questionElements.length; i++) {
  let question = questionElements[i].innerText;
  let answer = '';

  for (let j = 0; j < answerElements.length; j++) {
    if (questionElements[i].getAttribute('ms-code-snippet-q') === answerElements[j].getAttribute('ms-code-snippet-a')) {
      answer = answerElements[j].innerText;
      break;
    }
  }

  faqArray.push({
    "@type": "Question",
    "name": question,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": answer
    }
  });
}

let faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqArray
}

let script = document.createElement('script');
script.type = "application/ld+json";
script.innerHTML = JSON.stringify(faqSchema);

document.getElementsByTagName('head')[0].appendChild(script);
</script>
Ver Memberscript
UX

#34 - Requerir correo electrónico profesional para el envío de formularios

Bloquea a las personas que envíen un formulario si su correo electrónico utiliza un correo personal como gmail.


<!-- 💙 MEMBERSCRIPT #34 v0.1 💙 REQUIRE BUSINESS EMAILS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"> </script>
<script>
function isPersonalEmail(email) {
  var personalDomains = [
    "gmail.com", 
    "yahoo.com", 
    "hotmail.com", 
    "aol.com", 
    "msn.com", 
    "comcast.net", 
    "live.com", 
    "outlook.com", 
    "ymail.com",
    "icloud.com"
  ];
  var emailDomain = email.split('@')[1];
  return personalDomains.includes(emailDomain);
}

window.Parsley.addValidator('businessEmail', {
  validateString: function(value) {
    return !isPersonalEmail(value);
  },
  messages: {
    en: 'Please enter a business email.'
  }
});

$(document).ready(function() {
  $('form[ms-code-validate-form]').attr('data-parsley-validate', '');
  $('input[ms-code-business-email]').attr('data-parsley-business-email', '');
  $('form').parsley();
});
  $('form').parsley().on('form:error', function() {
  $('.parsley-errors-list').addClass('ms-code-validation-error');
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #34 v0.1 💙 REQUIRE BUSINESS EMAILS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"> </script>
<script>
function isPersonalEmail(email) {
  var personalDomains = [
    "gmail.com", 
    "yahoo.com", 
    "hotmail.com", 
    "aol.com", 
    "msn.com", 
    "comcast.net", 
    "live.com", 
    "outlook.com", 
    "ymail.com",
    "icloud.com"
  ];
  var emailDomain = email.split('@')[1];
  return personalDomains.includes(emailDomain);
}

window.Parsley.addValidator('businessEmail', {
  validateString: function(value) {
    return !isPersonalEmail(value);
  },
  messages: {
    en: 'Please enter a business email.'
  }
});

$(document).ready(function() {
  $('form[ms-code-validate-form]').attr('data-parsley-validate', '');
  $('input[ms-code-business-email]').attr('data-parsley-business-email', '');
  $('form').parsley();
});
  $('form').parsley().on('form:error', function() {
  $('.parsley-errors-list').addClass('ms-code-validation-error');
});
</script>
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.