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.

UX

#151 - Onboarding Tour For New Members

Launch a step-by-step product tour the first time a member logs in. Uses Memberstack’s JS API + Intro.js


<!-- 💙 MEMBERSCRIPT #151 v0.1 💙 - ONBOARDING TOUR FOR NEW MEMBERS -->
<script>
  // 1. Wait for Memberstack v2 DOM
  function ready(fn) {
    if (window.$memberstackReady) return fn();
    document.addEventListener("memberstack.ready", fn);
  }

  // 2. Collect all steps from the DOM
  function collectSteps() {
    // all elements with ms-code-step, in a NodeList
    var els = document.querySelectorAll("[ms-code-step]");
    // build an array of { order, element, intro }
    var steps = Array.prototype.map.call(els, function(el) {
      return {
        order: parseInt(el.getAttribute("ms-code-step"), 10),
        element: el,
        intro: el.getAttribute("ms-code-intro") || ""
      };
    });
    // sort by order ascending
    return steps.sort(function(a, b) {
      return a.order - b.order;
    }).map(function(s) {
      return { element: s.element, intro: s.intro };
    });
  }

  // 3. Kick off the tour for first-time members
  function launchTour(member) {
    if (!member || !member.id) return;                // only for logged-in
    if (localStorage.getItem("ms-code-tour-shown")) return;
    // Build steps dynamically
    var options = {
      steps: collectSteps(),
      showProgress: true,
      exitOnOverlayClick: false
    };
    introJs().setOptions(options).start();
    localStorage.setItem("ms-code-tour-shown", "true");
  }

  // 4. Glue it together
  ready(function() {
    window.$memberstackDom
      .getCurrentMember()
      .then(function(res) {
        launchTour(res.data);
      })
      .catch(function(err) {
        console.error("MS-Code-Tour error:", err);
      });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #151 v0.1 💙 - ONBOARDING TOUR FOR NEW MEMBERS -->
<script>
  // 1. Wait for Memberstack v2 DOM
  function ready(fn) {
    if (window.$memberstackReady) return fn();
    document.addEventListener("memberstack.ready", fn);
  }

  // 2. Collect all steps from the DOM
  function collectSteps() {
    // all elements with ms-code-step, in a NodeList
    var els = document.querySelectorAll("[ms-code-step]");
    // build an array of { order, element, intro }
    var steps = Array.prototype.map.call(els, function(el) {
      return {
        order: parseInt(el.getAttribute("ms-code-step"), 10),
        element: el,
        intro: el.getAttribute("ms-code-intro") || ""
      };
    });
    // sort by order ascending
    return steps.sort(function(a, b) {
      return a.order - b.order;
    }).map(function(s) {
      return { element: s.element, intro: s.intro };
    });
  }

  // 3. Kick off the tour for first-time members
  function launchTour(member) {
    if (!member || !member.id) return;                // only for logged-in
    if (localStorage.getItem("ms-code-tour-shown")) return;
    // Build steps dynamically
    var options = {
      steps: collectSteps(),
      showProgress: true,
      exitOnOverlayClick: false
    };
    introJs().setOptions(options).start();
    localStorage.setItem("ms-code-tour-shown", "true");
  }

  // 4. Glue it together
  ready(function() {
    window.$memberstackDom
      .getCurrentMember()
      .then(function(res) {
        launchTour(res.data);
      })
      .catch(function(err) {
        console.error("MS-Code-Tour error:", err);
      });
  });
</script>
Ver Memberscript
JSON

#150 - Save and Unsave Items to Your Collection (Pinterest-style)

A simple save/unsave system that lets members bookmark items into personal collections.


<!-- 💙 MEMBERSCRIPT #150 v0.1 💙 - SAVE AND UNSAVE ITEMS TO YOUR COLLECTION PART 1 -->
<script>
document.addEventListener("DOMContentLoaded", async () => {
  const ms = window.$memberstackDom;
  const member = await ms.getCurrentMember();
  const isLoggedIn = !!member;
  let savedItems = {};

  const fetchSavedItems = async () => {
    try {
      const { data } = await ms.getMemberJSON();
      savedItems = data.savedItems || {};
    } catch {
      savedItems = {};
    }
  };

  const persistSavedItems = async () => {
    try {
      await ms.updateMemberJSON({ json: { savedItems } });
    } catch (err) {
      console.error("Error saving items:", err);
    }
  };

  const updateButtons = () => {
    document.querySelectorAll('[ms-code-add-button]').forEach(btn => {
      const id = btn.getAttribute('ms-code-save');
      const category = btn.getAttribute('ms-code-category');
      const exists = savedItems[category]?.some(i => i.id === id);
      btn.style.display = exists ? 'none' : 'inline-block';
    });

    document.querySelectorAll('[ms-code-unsave-button]').forEach(btn => {
      const id = btn.getAttribute('ms-code-unsave');
      const category = btn.getAttribute('ms-code-category');
      const exists = savedItems[category]?.some(i => i.id === id);
      btn.style.display = exists ? 'inline-block' : 'none';
    });
  };

  const onAddClick = async (e) => {
    e.preventDefault();
    if (!isLoggedIn) return;

    const btn = e.currentTarget;
    const container = btn.closest('[ms-code-save-item]');
    const id = btn.getAttribute('ms-code-save');
    const category = btn.getAttribute('ms-code-category');
    const img = container?.querySelector('[ms-code-image]');
    const url = img?.src;

    if (!savedItems[category]) savedItems[category] = [];
    if (!savedItems[category].some(i => i.id === id)) {
      savedItems[category].push({ id, url });
      updateButtons();
      await persistSavedItems();
    }
  };

  const onUnsaveClick = async (e) => {
    e.preventDefault();
    if (!isLoggedIn) return;

    const btn = e.currentTarget;
    const id = btn.getAttribute('ms-code-unsave');
    const category = btn.getAttribute('ms-code-category');

    if (savedItems[category]) {
      savedItems[category] = savedItems[category].filter(i => i.id !== id);
      if (savedItems[category].length === 0) delete savedItems[category];
      updateButtons();
      await persistSavedItems();
    }
  };

  const onDownloadClick = (e) => {
    e.preventDefault();
    const btn = e.currentTarget;
    const container = btn.closest('[ms-code-save-item]');
    const img = container?.querySelector('[ms-code-image]');
    const url = img?.src;

    if (url) {
      const a = document.createElement('a');
      a.href = url;
      a.download = '';
      document.body.appendChild(a);
      a.click();
      a.remove();
    }
  };

  const attachListeners = () => {
    document.querySelectorAll('[ms-code-add-button]').forEach(b => b.addEventListener('click', onAddClick));
    document.querySelectorAll('[ms-code-unsave-button]').forEach(b => b.addEventListener('click', onUnsaveClick));
    document.querySelectorAll('[ms-code-download-button]').forEach(b => b.addEventListener('click', onDownloadClick));
  };

  await fetchSavedItems();
  updateButtons();
  attachListeners();
});
</script>

<!-- GENERATE PINTEREST GRID STYLE -->
<script>
$(document).ready(function () {
  setTimeout(function() {
    function resizeGridItem(item) {
      grid = document.getElementsByClassName("grid")[0];
      rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
      rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-row-gap'));
      rowSpan = Math.ceil((item.querySelector('.content').getBoundingClientRect().height + rowGap) / (rowHeight + rowGap));
      item.style.gridRowEnd = "span " + rowSpan;
    }

    function resizeAllGridItems() {
      allItems = document.getElementsByClassName("item");
      for (x = 0; x < allItems.length; x++) {
        resizeGridItem(allItems[x]);
      }
    }

    function resizeInstance(instance) {
      item = instance.elements[0];
      resizeGridItem(item);
    }

    window.onload = resizeAllGridItems();
    window.addEventListener("resize", resizeAllGridItems);

    allItems = document.getElementsByClassName("item");
    for (x = 0; x < allItems.length; x++) {
      imagesLoaded(allItems[x], resizeInstance);
    }

    setTimeout(function() { resizeInstance() }, 100);
  }, 800);
})
</script>

<!-- 💙 MEMBERSCRIPT #150 v0.1 💙 - SAVE AND UNSAVE ITEMS TO YOUR COLLECTION PART 2 -->
<script>
document.addEventListener("DOMContentLoaded", async () => {
  const ms = window.$memberstackDom;
  const wrapper = document.querySelector('[ms-code-collections-wrapper]');
  const template = document.querySelector('[ms-code-folder-template]') || document.querySelector('[ms-code-folder]');
  const emptyState = document.querySelector('[ms-code-empty]');
  if (!wrapper || !template) return;

  let member;
  try {
    member = await ms.getCurrentMember();
  } catch {
    wrapper.textContent = "Please log in to view your collections.";
    return;
  }

  let savedItems = {};
  try {
    const { data } = await ms.getMemberJSON();
    savedItems = data?.savedItems || {};
  } catch {
    wrapper.textContent = "Could not load your collections.";
    return;
  }

  if (Object.keys(savedItems).length === 0) {
    wrapper.innerHTML = '';
    if (emptyState) emptyState.style.display = 'block';
    return;
  }

  if (emptyState) emptyState.style.display = 'none';
  wrapper.innerHTML = '';

  const persistSavedItems = async () => {
    try {
      await ms.updateMemberJSON({ json: { savedItems } });
    } catch (err) {
      console.error("Failed to save", err);
    }
  };

  const updateButtons = (modal, id, category) => {
    const addBtn = modal.querySelector('[ms-code-add-button]');
    const unsaveBtn = modal.querySelector('[ms-code-unsave-button]');
    const exists = savedItems[category]?.some(item => item.id === id);
    addBtn.style.display = exists ? 'none' : 'inline-block';
    unsaveBtn.style.display = exists ? 'inline-block' : 'none';
  };

  Object.entries(savedItems).forEach(([category, items]) => {
    const folderClone = template.cloneNode(true);
    const titleEl = folderClone.querySelector('[ms-code-folder-title]');
    if (titleEl) titleEl.textContent = `${category} (${items.length})`;

    const imageContainer = folderClone.querySelector('[ms-code-folder-items]');
    const imageTemplate = folderClone.querySelector('[ms-code-folder-image]');
    if (imageTemplate) imageTemplate.style.display = 'none';

    const modal = folderClone.querySelector('[ms-code-modal]');
    const modalImg = folderClone.querySelector('[ms-code-modal-img]');
    const modalClose = folderClone.querySelector('[ms-code-modal-close]');
    const addButton = folderClone.querySelector('[ms-code-add-button]');
    const unsaveButton = folderClone.querySelector('[ms-code-unsave-button]');
    const downloadButton = folderClone.querySelector('[ms-code-download-button]');
    const hiddenImage = folderClone.querySelector('[ms-code-image]');

    items.forEach(item => {
      const imgClone = imageTemplate.cloneNode(true);
      imgClone.src = item.url;
      imgClone.alt = category;
      imgClone.style.display = 'block';
      imgClone.style.objectFit = 'cover';
      imgClone.style.width = '100%';
      imgClone.style.height = 'auto';
      imgClone.style.maxWidth = '100%';

      imgClone.addEventListener('click', () => {
        if (modal && modalImg) {
          modalImg.src = item.url;
          if (hiddenImage) hiddenImage.src = item.url;

          const id = item.id;
          addButton.onclick = async (e) => {
            e.preventDefault();
            savedItems[category] = savedItems[category] || [];
            if (!savedItems[category].some(i => i.id === id)) {
              savedItems[category].push({ id, url: item.url });
              await persistSavedItems();
              updateButtons(modal, id, category);
            }
          };

          unsaveButton.onclick = async (e) => {
            e.preventDefault();
            savedItems[category] = savedItems[category].filter(i => i.id !== id);
            if (savedItems[category].length === 0) delete savedItems[category];
            await persistSavedItems();
            modal.style.display = 'none';
            location.reload();
          };

          downloadButton.onclick = (e) => {
            e.preventDefault();
            const a = document.createElement('a');
            a.href = item.url;
            a.download = '';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
          };

          updateButtons(modal, id, category);
        }
        modal.style.display = 'flex';
      });

      imageContainer.appendChild(imgClone);
    });

    if (modal && modalClose) {
      modalClose.addEventListener('click', () => {
        modal.style.display = 'none';
        if (modalImg) modalImg.src = '';
      });
    }

    wrapper.appendChild(folderClone);
  });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #150 v0.1 💙 - SAVE AND UNSAVE ITEMS TO YOUR COLLECTION PART 1 -->
<script>
document.addEventListener("DOMContentLoaded", async () => {
  const ms = window.$memberstackDom;
  const member = await ms.getCurrentMember();
  const isLoggedIn = !!member;
  let savedItems = {};

  const fetchSavedItems = async () => {
    try {
      const { data } = await ms.getMemberJSON();
      savedItems = data.savedItems || {};
    } catch {
      savedItems = {};
    }
  };

  const persistSavedItems = async () => {
    try {
      await ms.updateMemberJSON({ json: { savedItems } });
    } catch (err) {
      console.error("Error saving items:", err);
    }
  };

  const updateButtons = () => {
    document.querySelectorAll('[ms-code-add-button]').forEach(btn => {
      const id = btn.getAttribute('ms-code-save');
      const category = btn.getAttribute('ms-code-category');
      const exists = savedItems[category]?.some(i => i.id === id);
      btn.style.display = exists ? 'none' : 'inline-block';
    });

    document.querySelectorAll('[ms-code-unsave-button]').forEach(btn => {
      const id = btn.getAttribute('ms-code-unsave');
      const category = btn.getAttribute('ms-code-category');
      const exists = savedItems[category]?.some(i => i.id === id);
      btn.style.display = exists ? 'inline-block' : 'none';
    });
  };

  const onAddClick = async (e) => {
    e.preventDefault();
    if (!isLoggedIn) return;

    const btn = e.currentTarget;
    const container = btn.closest('[ms-code-save-item]');
    const id = btn.getAttribute('ms-code-save');
    const category = btn.getAttribute('ms-code-category');
    const img = container?.querySelector('[ms-code-image]');
    const url = img?.src;

    if (!savedItems[category]) savedItems[category] = [];
    if (!savedItems[category].some(i => i.id === id)) {
      savedItems[category].push({ id, url });
      updateButtons();
      await persistSavedItems();
    }
  };

  const onUnsaveClick = async (e) => {
    e.preventDefault();
    if (!isLoggedIn) return;

    const btn = e.currentTarget;
    const id = btn.getAttribute('ms-code-unsave');
    const category = btn.getAttribute('ms-code-category');

    if (savedItems[category]) {
      savedItems[category] = savedItems[category].filter(i => i.id !== id);
      if (savedItems[category].length === 0) delete savedItems[category];
      updateButtons();
      await persistSavedItems();
    }
  };

  const onDownloadClick = (e) => {
    e.preventDefault();
    const btn = e.currentTarget;
    const container = btn.closest('[ms-code-save-item]');
    const img = container?.querySelector('[ms-code-image]');
    const url = img?.src;

    if (url) {
      const a = document.createElement('a');
      a.href = url;
      a.download = '';
      document.body.appendChild(a);
      a.click();
      a.remove();
    }
  };

  const attachListeners = () => {
    document.querySelectorAll('[ms-code-add-button]').forEach(b => b.addEventListener('click', onAddClick));
    document.querySelectorAll('[ms-code-unsave-button]').forEach(b => b.addEventListener('click', onUnsaveClick));
    document.querySelectorAll('[ms-code-download-button]').forEach(b => b.addEventListener('click', onDownloadClick));
  };

  await fetchSavedItems();
  updateButtons();
  attachListeners();
});
</script>

<!-- GENERATE PINTEREST GRID STYLE -->
<script>
$(document).ready(function () {
  setTimeout(function() {
    function resizeGridItem(item) {
      grid = document.getElementsByClassName("grid")[0];
      rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
      rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-row-gap'));
      rowSpan = Math.ceil((item.querySelector('.content').getBoundingClientRect().height + rowGap) / (rowHeight + rowGap));
      item.style.gridRowEnd = "span " + rowSpan;
    }

    function resizeAllGridItems() {
      allItems = document.getElementsByClassName("item");
      for (x = 0; x < allItems.length; x++) {
        resizeGridItem(allItems[x]);
      }
    }

    function resizeInstance(instance) {
      item = instance.elements[0];
      resizeGridItem(item);
    }

    window.onload = resizeAllGridItems();
    window.addEventListener("resize", resizeAllGridItems);

    allItems = document.getElementsByClassName("item");
    for (x = 0; x < allItems.length; x++) {
      imagesLoaded(allItems[x], resizeInstance);
    }

    setTimeout(function() { resizeInstance() }, 100);
  }, 800);
})
</script>

<!-- 💙 MEMBERSCRIPT #150 v0.1 💙 - SAVE AND UNSAVE ITEMS TO YOUR COLLECTION PART 2 -->
<script>
document.addEventListener("DOMContentLoaded", async () => {
  const ms = window.$memberstackDom;
  const wrapper = document.querySelector('[ms-code-collections-wrapper]');
  const template = document.querySelector('[ms-code-folder-template]') || document.querySelector('[ms-code-folder]');
  const emptyState = document.querySelector('[ms-code-empty]');
  if (!wrapper || !template) return;

  let member;
  try {
    member = await ms.getCurrentMember();
  } catch {
    wrapper.textContent = "Please log in to view your collections.";
    return;
  }

  let savedItems = {};
  try {
    const { data } = await ms.getMemberJSON();
    savedItems = data?.savedItems || {};
  } catch {
    wrapper.textContent = "Could not load your collections.";
    return;
  }

  if (Object.keys(savedItems).length === 0) {
    wrapper.innerHTML = '';
    if (emptyState) emptyState.style.display = 'block';
    return;
  }

  if (emptyState) emptyState.style.display = 'none';
  wrapper.innerHTML = '';

  const persistSavedItems = async () => {
    try {
      await ms.updateMemberJSON({ json: { savedItems } });
    } catch (err) {
      console.error("Failed to save", err);
    }
  };

  const updateButtons = (modal, id, category) => {
    const addBtn = modal.querySelector('[ms-code-add-button]');
    const unsaveBtn = modal.querySelector('[ms-code-unsave-button]');
    const exists = savedItems[category]?.some(item => item.id === id);
    addBtn.style.display = exists ? 'none' : 'inline-block';
    unsaveBtn.style.display = exists ? 'inline-block' : 'none';
  };

  Object.entries(savedItems).forEach(([category, items]) => {
    const folderClone = template.cloneNode(true);
    const titleEl = folderClone.querySelector('[ms-code-folder-title]');
    if (titleEl) titleEl.textContent = `${category} (${items.length})`;

    const imageContainer = folderClone.querySelector('[ms-code-folder-items]');
    const imageTemplate = folderClone.querySelector('[ms-code-folder-image]');
    if (imageTemplate) imageTemplate.style.display = 'none';

    const modal = folderClone.querySelector('[ms-code-modal]');
    const modalImg = folderClone.querySelector('[ms-code-modal-img]');
    const modalClose = folderClone.querySelector('[ms-code-modal-close]');
    const addButton = folderClone.querySelector('[ms-code-add-button]');
    const unsaveButton = folderClone.querySelector('[ms-code-unsave-button]');
    const downloadButton = folderClone.querySelector('[ms-code-download-button]');
    const hiddenImage = folderClone.querySelector('[ms-code-image]');

    items.forEach(item => {
      const imgClone = imageTemplate.cloneNode(true);
      imgClone.src = item.url;
      imgClone.alt = category;
      imgClone.style.display = 'block';
      imgClone.style.objectFit = 'cover';
      imgClone.style.width = '100%';
      imgClone.style.height = 'auto';
      imgClone.style.maxWidth = '100%';

      imgClone.addEventListener('click', () => {
        if (modal && modalImg) {
          modalImg.src = item.url;
          if (hiddenImage) hiddenImage.src = item.url;

          const id = item.id;
          addButton.onclick = async (e) => {
            e.preventDefault();
            savedItems[category] = savedItems[category] || [];
            if (!savedItems[category].some(i => i.id === id)) {
              savedItems[category].push({ id, url: item.url });
              await persistSavedItems();
              updateButtons(modal, id, category);
            }
          };

          unsaveButton.onclick = async (e) => {
            e.preventDefault();
            savedItems[category] = savedItems[category].filter(i => i.id !== id);
            if (savedItems[category].length === 0) delete savedItems[category];
            await persistSavedItems();
            modal.style.display = 'none';
            location.reload();
          };

          downloadButton.onclick = (e) => {
            e.preventDefault();
            const a = document.createElement('a');
            a.href = item.url;
            a.download = '';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
          };

          updateButtons(modal, id, category);
        }
        modal.style.display = 'flex';
      });

      imageContainer.appendChild(imgClone);
    });

    if (modal && modalClose) {
      modalClose.addEventListener('click', () => {
        modal.style.display = 'none';
        if (modalImg) modalImg.src = '';
      });
    }

    wrapper.appendChild(folderClone);
  });
});
</script>
Ver Memberscript
UX
Accesibilidad

#149 - Favicon for Dark/Light Mode

Use this script to update your website's favicon based on the user's system color scheme preference.


<!-- 💙 MEMBERSCRIPT #149 v0.1 💙 - FAVICON FOR DARK/LIGHT MODE -->
<script>
  // Helper: Retrieve or create a favicon element
  function getFaviconElement() {
    let favicon = document.querySelector('link[rel="icon"]') ||
        document.querySelector('link[rel="shortcut icon"]');
    if (!favicon) {
      favicon = document.createElement('link');
      favicon.rel = 'icon';
      document.head.appendChild(favicon);
    }
    return favicon;
  }

  // Function to update the favicon based on dark mode
  function updateFavicon(e) {
    const darkModeOn = e ? e.matches : window.matchMedia('(prefers-color-scheme: dark)').matches;
    const favicon = getFaviconElement();
    // Update these paths to your favicon assets in Webflow’s Asset Manager or a CDN
    favicon.href = darkModeOn
      ? 'https://cdn.prod.website-files.com/67fcff014042c2f5945437c0/67fd000f85b2a9f281a373ca_Dark%20Mode%20Logo.png'
      : 'https://cdn.prod.website-files.com/67fcff014042c2f5945437c0/67fd000f1c2fa3cebee1b150_Light%20Mode%20Logo.png';
  }

  // Initialize the favicon update on page load
  updateFavicon();

  // Listen for changes in the dark mode media query
  const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
  if (typeof darkModeMediaQuery.addEventListener === 'function') {
    darkModeMediaQuery.addEventListener('change', updateFavicon);
  } else if (typeof darkModeMediaQuery.addListener === 'function') {
    darkModeMediaQuery.addListener(updateFavicon);
  }
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #149 v0.1 💙 - FAVICON FOR DARK/LIGHT MODE -->
<script>
  // Helper: Retrieve or create a favicon element
  function getFaviconElement() {
    let favicon = document.querySelector('link[rel="icon"]') ||
        document.querySelector('link[rel="shortcut icon"]');
    if (!favicon) {
      favicon = document.createElement('link');
      favicon.rel = 'icon';
      document.head.appendChild(favicon);
    }
    return favicon;
  }

  // Function to update the favicon based on dark mode
  function updateFavicon(e) {
    const darkModeOn = e ? e.matches : window.matchMedia('(prefers-color-scheme: dark)').matches;
    const favicon = getFaviconElement();
    // Update these paths to your favicon assets in Webflow’s Asset Manager or a CDN
    favicon.href = darkModeOn
      ? 'https://cdn.prod.website-files.com/67fcff014042c2f5945437c0/67fd000f85b2a9f281a373ca_Dark%20Mode%20Logo.png'
      : 'https://cdn.prod.website-files.com/67fcff014042c2f5945437c0/67fd000f1c2fa3cebee1b150_Light%20Mode%20Logo.png';
  }

  // Initialize the favicon update on page load
  updateFavicon();

  // Listen for changes in the dark mode media query
  const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
  if (typeof darkModeMediaQuery.addEventListener === 'function') {
    darkModeMediaQuery.addEventListener('change', updateFavicon);
  } else if (typeof darkModeMediaQuery.addListener === 'function') {
    darkModeMediaQuery.addListener(updateFavicon);
  }
</script>
Ver Memberscript
UX
Accesibilidad

#148 - Disable Webflow Form Success Window

Use this script to override Webflow's default form submission behavior by hiding the success message.


<!-- 💙 MEMBERSCRIPT #148 v0.1 💙 - DISABLE WEBFLOW FORM SUCCESS WINDOW -->
<script>
  document.addEventListener('DOMContentLoaded', function () {
    const form = document.querySelector('[ms-code-form="form"]');
    const successEl = document.querySelector('[ms-code-success="true"]');

    if (!form || !successEl) return;

    const observer = new MutationObserver(() => {
      const isVisible = window.getComputedStyle(successEl).display !== 'none';
      if (isVisible) {
        successEl.style.display = 'none';
        form.style.display = 'block';
      }
    });

    observer.observe(successEl, { attributes: true, attributeFilter: ['style'] });

    // Cleanup observer when done (optional, for performance)
    form.addEventListener('w-form-success', () => {
      setTimeout(() => {
        observer.disconnect();
      }, 100);
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #148 v0.1 💙 - DISABLE WEBFLOW FORM SUCCESS WINDOW -->
<script>
  document.addEventListener('DOMContentLoaded', function () {
    const form = document.querySelector('[ms-code-form="form"]');
    const successEl = document.querySelector('[ms-code-success="true"]');

    if (!form || !successEl) return;

    const observer = new MutationObserver(() => {
      const isVisible = window.getComputedStyle(successEl).display !== 'none';
      if (isVisible) {
        successEl.style.display = 'none';
        form.style.display = 'block';
      }
    });

    observer.observe(successEl, { attributes: true, attributeFilter: ['style'] });

    // Cleanup observer when done (optional, for performance)
    form.addEventListener('w-form-success', () => {
      setTimeout(() => {
        observer.disconnect();
      }, 100);
    });
  });
</script>
Ver Memberscript
UX
Accesibilidad
Modales
Seguridad

#147 - Age Verification Popup with Cookies in Webflow

Use this script to add an age verification popup to your Webflow site.


<!-- 💙 MEMBERSCRIPT #147 v0.1 💙 - AGE VERIFICATION POPUP WITH COOKIES -->
<script>
  // Simple cookie helper functions
  function setCookie(name, value, days) {
    const date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    document.cookie = name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
  }

  function getCookie(name) {
    const cname = name + "=";
    const decodedCookie = decodeURIComponent(document.cookie);
    const cookies = decodedCookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      let c = cookies[i].trim();
      if (c.indexOf(cname) === 0) {
        return c.substring(cname.length, c.length);
      }
    }
    return "";
  }

  document.addEventListener('DOMContentLoaded', function() {
    // Select the age gate element via ms-code attribute
    const ageGateEl = document.querySelector('[ms-code-agegate]');

    // On page load: if the cookie exists, hide the age gate
    if (getCookie('ms-code-ageVerified') === 'true') {  
      if (ageGateEl) ageGateEl.style.display = 'none';
    }

    // Listen for clicks on elements with ms-code-click
    document.addEventListener('click', function(event) {
      if (event.target.closest('[ms-code-click="confirmAge"]')) {
        setCookie('ms-code-ageVerified', 'true', 30);
        if (ageGateEl) ageGateEl.style.display = 'none';
      } else if (event.target.closest('[ms-code-click="denyAge"]')) {
        window.location.href = 'https://age-verification-popup-with-cookies.webflow.io/access-denied'; // Change URL as needed
      }
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #147 v0.1 💙 - AGE VERIFICATION POPUP WITH COOKIES -->
<script>
  // Simple cookie helper functions
  function setCookie(name, value, days) {
    const date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    document.cookie = name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
  }

  function getCookie(name) {
    const cname = name + "=";
    const decodedCookie = decodeURIComponent(document.cookie);
    const cookies = decodedCookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      let c = cookies[i].trim();
      if (c.indexOf(cname) === 0) {
        return c.substring(cname.length, c.length);
      }
    }
    return "";
  }

  document.addEventListener('DOMContentLoaded', function() {
    // Select the age gate element via ms-code attribute
    const ageGateEl = document.querySelector('[ms-code-agegate]');

    // On page load: if the cookie exists, hide the age gate
    if (getCookie('ms-code-ageVerified') === 'true') {  
      if (ageGateEl) ageGateEl.style.display = 'none';
    }

    // Listen for clicks on elements with ms-code-click
    document.addEventListener('click', function(event) {
      if (event.target.closest('[ms-code-click="confirmAge"]')) {
        setCookie('ms-code-ageVerified', 'true', 30);
        if (ageGateEl) ageGateEl.style.display = 'none';
      } else if (event.target.closest('[ms-code-click="denyAge"]')) {
        window.location.href = 'https://age-verification-popup-with-cookies.webflow.io/access-denied'; // Change URL as needed
      }
    });
  });
</script>
Ver Memberscript
UX
Accesibilidad
Modales

#146 - Stop Videos from Playing When A Modal Closes

Automatically stop video playback when closing modals in Webflow.


<!-- 💙 MEMBERSCRIPT #146 v0.1 💙 - STOP VIDEOS FROM PLAYING WHEN A MODAL CLOSES -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all modals on the page
    var modals = document.querySelectorAll('[data-ms-modal="modal"]');

    function initializeModal(modal) {
      var iframe = modal.querySelector('iframe');
      if (!iframe) return; // If no iframe found, do nothing
      var originalSrc = iframe.dataset.src || iframe.src;

      // Function to stop video playback
      function stopVideo() {
        iframe.src = "";
        setTimeout(() => {
          iframe.src = originalSrc;
        }, 100);
      }

      // Attach event listeners to all close buttons inside the modal
      var closeBtns = modal.querySelectorAll('[data-ms-modal="close"]');
      closeBtns.forEach(function(closeBtn) {
        closeBtn.addEventListener('click', function(e) {
          e.preventDefault();
          stopVideo();
        });
      });

      // Also close the modal when clicking outside the content
      modal.addEventListener('click', function(e) {
        if (e.target === modal) {
          stopVideo();
        }
      });
    }

    // Initialize all modals
    modals.forEach(initializeModal);
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #146 v0.1 💙 - STOP VIDEOS FROM PLAYING WHEN A MODAL CLOSES -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all modals on the page
    var modals = document.querySelectorAll('[data-ms-modal="modal"]');

    function initializeModal(modal) {
      var iframe = modal.querySelector('iframe');
      if (!iframe) return; // If no iframe found, do nothing
      var originalSrc = iframe.dataset.src || iframe.src;

      // Function to stop video playback
      function stopVideo() {
        iframe.src = "";
        setTimeout(() => {
          iframe.src = originalSrc;
        }, 100);
      }

      // Attach event listeners to all close buttons inside the modal
      var closeBtns = modal.querySelectorAll('[data-ms-modal="close"]');
      closeBtns.forEach(function(closeBtn) {
        closeBtn.addEventListener('click', function(e) {
          e.preventDefault();
          stopVideo();
        });
      });

      // Also close the modal when clicking outside the content
      modal.addEventListener('click', function(e) {
        if (e.target === modal) {
          stopVideo();
        }
      });
    }

    // Initialize all modals
    modals.forEach(initializeModal);
  });
</script>
Ver Memberscript
UX
Accesibilidad

#145 - Automatically Save & Prefill Forms

Automatically save and prefill forms in a browsers localStorage upon form submission.


<!-- 💙 MEMBERSCRIPT #145 v0.1 💙 - HOW TO PRE-FILL FORM INPUT FIELDS AT PAGE LOAD -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Function to store form data in localStorage
    function storeFormData() {
      const fields = document.querySelectorAll('input[ms-code-field-id], textarea[ms-code-field-id], select[ms-code-field-id]');
      fields.forEach(function(field) {
        const fieldId = field.getAttribute('ms-code-field-id');
        const value = field.value.trim();
        if (value) {
          localStorage.setItem(fieldId, value);
        }
      });
    }

    // Function to pre-fill form fields with stored data
    function preFillForm() {
      const fields = document.querySelectorAll('input[ms-code-field-id], textarea[ms-code-field-id], select[ms-code-field-id]');
      fields.forEach(function(field) {
        const fieldId = field.getAttribute('ms-code-field-id');
        const storedValue = localStorage.getItem(fieldId);
        if (storedValue) {
          field.value = storedValue;
        }
      });
    }

    // Handle form submission
    const form = document.querySelector('#my-form, form[ms-code-form-id]');
    if (form) {
      form.addEventListener('submit', function(event) {
        event.preventDefault(); // Prevent default form submission
        storeFormData();

        // Refresh the page after storing data
        setTimeout(function() {
          location.reload();
        }, 500); // Short delay to simulate form submission
      });
    }

    // Pre-fill form fields when the page loads
    preFillForm();
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #145 v0.1 💙 - HOW TO PRE-FILL FORM INPUT FIELDS AT PAGE LOAD -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Function to store form data in localStorage
    function storeFormData() {
      const fields = document.querySelectorAll('input[ms-code-field-id], textarea[ms-code-field-id], select[ms-code-field-id]');
      fields.forEach(function(field) {
        const fieldId = field.getAttribute('ms-code-field-id');
        const value = field.value.trim();
        if (value) {
          localStorage.setItem(fieldId, value);
        }
      });
    }

    // Function to pre-fill form fields with stored data
    function preFillForm() {
      const fields = document.querySelectorAll('input[ms-code-field-id], textarea[ms-code-field-id], select[ms-code-field-id]');
      fields.forEach(function(field) {
        const fieldId = field.getAttribute('ms-code-field-id');
        const storedValue = localStorage.getItem(fieldId);
        if (storedValue) {
          field.value = storedValue;
        }
      });
    }

    // Handle form submission
    const form = document.querySelector('#my-form, form[ms-code-form-id]');
    if (form) {
      form.addEventListener('submit', function(event) {
        event.preventDefault(); // Prevent default form submission
        storeFormData();

        // Refresh the page after storing data
        setTimeout(function() {
          location.reload();
        }, 500); // Short delay to simulate form submission
      });
    }

    // Pre-fill form fields when the page loads
    preFillForm();
  });
</script>
Ver Memberscript
Marketing
JSON
SEO

#144 - Track Users Login History & Active Users

Automatically track a members login history, keep a login streak and total visits


<!-- 💙 MEMBERSCRIPT #144 v0.1 💙 - TRACK A USERS LOGIN HISTORY & ACTIVE USERS -->
<script>
  (function(){
    const memberstack = window.$memberstackDom;

    // Helper: Execute callback when Memberstack is ready
    function onMemberstackReady(cb) {
      if (window.$memberstackReady) {
        cb();
      } else {
        document.addEventListener("memberstack.ready", cb);
      }
    }

    async function initTracking() {
      // Check if a member is logged in (via localStorage)
      const currentUser = JSON.parse(localStorage.getItem("_ms-mem") || "null");
      if (!currentUser) {
        console.warn("No logged-in member found. Tracking not applied.");
        return;
      }

      // Retrieve member metadata
      const memberJson = await memberstack.getMemberJSON();
      let metadata = memberJson.data || {};

      // Ensure userVisits exists as an array
      metadata.userVisits = Array.isArray(metadata.userVisits) ? metadata.userVisits : [];

      // Use ISO date (YYYY-MM-DD) to record one visit per day
      const today = new Date().toISOString().split("T")[0];
      if (!metadata.userVisits.includes(today)) {
        metadata.userVisits.push(today);
      }

      // Helper: Compute consecutive login streak from userVisits
      function computeStreak(visits) {
        if (!visits.length) return 0;
        // Ensure dates are unique and sorted ascending
        const uniqueVisits = [...new Set(visits)].sort();
        let streak = 1;
        let currentDate = new Date(uniqueVisits[uniqueVisits.length - 1]);
        for (let i = uniqueVisits.length - 2; i >= 0; i--) {
          const prevDate = new Date(uniqueVisits[i]);
          const diffDays = Math.floor((currentDate - prevDate) / (1000 * 60 * 60 * 24));
          if (diffDays === 1) {
            streak++;
            currentDate = prevDate;
          } else {
            break;
          }
        }
        return streak;
      }

      // Calculate the login streak and total visits
      metadata.loginStreak = computeStreak(metadata.userVisits);
      metadata.totalVisits = metadata.userVisits.length;

      // Update Memberstack metadata
      await memberstack.updateMemberJSON({ json: metadata });

      console.log("User visits:", metadata.userVisits);
      console.log("Login streak:", metadata.loginStreak);
      console.log("Total visits:", metadata.totalVisits);
    }

    onMemberstackReady(initTracking);
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #144 v0.1 💙 - TRACK A USERS LOGIN HISTORY & ACTIVE USERS -->
<script>
  (function(){
    const memberstack = window.$memberstackDom;

    // Helper: Execute callback when Memberstack is ready
    function onMemberstackReady(cb) {
      if (window.$memberstackReady) {
        cb();
      } else {
        document.addEventListener("memberstack.ready", cb);
      }
    }

    async function initTracking() {
      // Check if a member is logged in (via localStorage)
      const currentUser = JSON.parse(localStorage.getItem("_ms-mem") || "null");
      if (!currentUser) {
        console.warn("No logged-in member found. Tracking not applied.");
        return;
      }

      // Retrieve member metadata
      const memberJson = await memberstack.getMemberJSON();
      let metadata = memberJson.data || {};

      // Ensure userVisits exists as an array
      metadata.userVisits = Array.isArray(metadata.userVisits) ? metadata.userVisits : [];

      // Use ISO date (YYYY-MM-DD) to record one visit per day
      const today = new Date().toISOString().split("T")[0];
      if (!metadata.userVisits.includes(today)) {
        metadata.userVisits.push(today);
      }

      // Helper: Compute consecutive login streak from userVisits
      function computeStreak(visits) {
        if (!visits.length) return 0;
        // Ensure dates are unique and sorted ascending
        const uniqueVisits = [...new Set(visits)].sort();
        let streak = 1;
        let currentDate = new Date(uniqueVisits[uniqueVisits.length - 1]);
        for (let i = uniqueVisits.length - 2; i >= 0; i--) {
          const prevDate = new Date(uniqueVisits[i]);
          const diffDays = Math.floor((currentDate - prevDate) / (1000 * 60 * 60 * 24));
          if (diffDays === 1) {
            streak++;
            currentDate = prevDate;
          } else {
            break;
          }
        }
        return streak;
      }

      // Calculate the login streak and total visits
      metadata.loginStreak = computeStreak(metadata.userVisits);
      metadata.totalVisits = metadata.userVisits.length;

      // Update Memberstack metadata
      await memberstack.updateMemberJSON({ json: metadata });

      console.log("User visits:", metadata.userVisits);
      console.log("Login streak:", metadata.loginStreak);
      console.log("Total visits:", metadata.totalVisits);
    }

    onMemberstackReady(initTracking);
  })();
</script>
Ver Memberscript
UX
Marketing

#143 - Initial Based Profile Avatar

Generate a custom avatar with initials when a member has no profile picture.


<!-- 💙 MEMBERSCRIPT #143 v0.1 💙 - GENERATE INITIALS BASED AVATAR -->
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const checkMemberstack = setInterval(() => {
      if (window.$memberstackDom) {
        clearInterval(checkMemberstack);

        window.$memberstackDom.getCurrentMember().then(({ data }) => {
          if (!data) return console.log("No member data (logged out)");

          const profileImage = document.querySelector('[data-ms-member="profile-image"]');
          const avatarWrapper = document.querySelector('[data-ms-code="avatar"]');
          const initialsDiv = avatarWrapper?.querySelector('.ms-avatar-initial');

          if (data.profileImage) {
            profileImage?.style.setProperty("display", "block");
            avatarWrapper?.style.setProperty("display", "none");
          } else {
            profileImage?.style.setProperty("display", "none");
            avatarWrapper?.style.setProperty("display", "flex");

            // Get initials from available fields
            const first = data.customFields["first-name"]?.trim().charAt(0).toUpperCase() || "";
            const last = data.customFields["last-name"]?.trim().charAt(0).toUpperCase() || "";
            let initials = first + last;

            if (!initials) {
              const fullName = data.customFields["name"]?.trim().split(" ") || [];
              initials = fullName.length > 1
                ? (fullName[0].charAt(0) + fullName[1].charAt(0)).toUpperCase()
                : fullName[0]?.charAt(0).toUpperCase() || "?";
            }

            if (initialsDiv) {
              initialsDiv.textContent = initials;
            } else {
              avatarWrapper.innerHTML = `
${initials}
`; } } }).catch(console.error); } }, 100); }); </script>
v0.1

<!-- 💙 MEMBERSCRIPT #143 v0.1 💙 - GENERATE INITIALS BASED AVATAR -->
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const checkMemberstack = setInterval(() => {
      if (window.$memberstackDom) {
        clearInterval(checkMemberstack);

        window.$memberstackDom.getCurrentMember().then(({ data }) => {
          if (!data) return console.log("No member data (logged out)");

          const profileImage = document.querySelector('[data-ms-member="profile-image"]');
          const avatarWrapper = document.querySelector('[data-ms-code="avatar"]');
          const initialsDiv = avatarWrapper?.querySelector('.ms-avatar-initial');

          if (data.profileImage) {
            profileImage?.style.setProperty("display", "block");
            avatarWrapper?.style.setProperty("display", "none");
          } else {
            profileImage?.style.setProperty("display", "none");
            avatarWrapper?.style.setProperty("display", "flex");

            // Get initials from available fields
            const first = data.customFields["first-name"]?.trim().charAt(0).toUpperCase() || "";
            const last = data.customFields["last-name"]?.trim().charAt(0).toUpperCase() || "";
            let initials = first + last;

            if (!initials) {
              const fullName = data.customFields["name"]?.trim().split(" ") || [];
              initials = fullName.length > 1
                ? (fullName[0].charAt(0) + fullName[1].charAt(0)).toUpperCase()
                : fullName[0]?.charAt(0).toUpperCase() || "?";
            }

            if (initialsDiv) {
              initialsDiv.textContent = initials;
            } else {
              avatarWrapper.innerHTML = `
${initials}
`; } } }).catch(console.error); } }, 100); }); </script>
Ver Memberscript
UX
Marketing

#142 - Embed PDFs For Webflow

Easily embed a PDF on your Webflow site - for free, without any custom code.


<!-- 💙 MEMBERSCRIPT #142 v0.1 💙 - EMBED PDFS IN WEBFLOW -->
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const pdfElements = document.querySelectorAll('[ms-code-pdf-src]');

    pdfElements.forEach(function (element) {
      const src = element.getAttribute('ms-code-pdf-src');
      const height = element.getAttribute('ms-code-pdf-height') || '500px';

      const iframe = document.createElement('iframe');
      iframe.src = src;
      iframe.style.width = '100%';
      iframe.style.height = height;
      iframe.style.border = 'none';
      // Set the iframe to block to remove any inline element gaps
      iframe.style.display = 'block';
      iframe.setAttribute('scrolling', 'auto');

      element.innerHTML = '';
      element.appendChild(iframe);
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #142 v0.1 💙 - EMBED PDFS IN WEBFLOW -->
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const pdfElements = document.querySelectorAll('[ms-code-pdf-src]');

    pdfElements.forEach(function (element) {
      const src = element.getAttribute('ms-code-pdf-src');
      const height = element.getAttribute('ms-code-pdf-height') || '500px';

      const iframe = document.createElement('iframe');
      iframe.src = src;
      iframe.style.width = '100%';
      iframe.style.height = height;
      iframe.style.border = 'none';
      // Set the iframe to block to remove any inline element gaps
      iframe.style.display = 'block';
      iframe.setAttribute('scrolling', 'auto');

      element.innerHTML = '';
      element.appendChild(iframe);
    });
  });
</script>
Ver Memberscript
UX
Marketing

#141 - Iniciar la inserción en YouTube a una hora específica

Habilita enlaces compartibles y comienza a reproducir vídeos a una hora determinada.


<!-- 💙 MEMBERSCRIPT #141 v0.1 💙 - START YOUTUBE VIDEO AT SPECIFIC TIME -->
<script>
  (function() {
    // Function to get URL parameters
    function getUrlParameter(name) {
      name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
      var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
      var results = regex.exec(location.search);
      return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    // Function to update YouTube embed src within Embedly iframe
    function updateYouTubeEmbed(embedly_iframe, startTime) {
      var embedly_src = embedly_iframe.src;
      var youtube_src_match = embedly_src.match(/src=([^&]+)/);
      if (youtube_src_match) {
        var youtube_src = decodeURIComponent(youtube_src_match[1]);
        var new_youtube_src = youtube_src.replace(/(\?|&)start=\d+/, '');
        new_youtube_src += (new_youtube_src.includes('?') ? '&' : '?') + 'start=' + startTime;
        var new_embedly_src = embedly_src.replace(/src=([^&]+)/, 'src=' + encodeURIComponent(new_youtube_src));
        embedly_iframe.src = new_embedly_src;
      }
    }

    // Get all elements with ms-code-yt-start attribute
    var elements = document.querySelectorAll('[ms-code-yt-start]');

    elements.forEach(function(element) {
      var paramName = element.getAttribute('ms-code-yt-start');
      var startTime = getUrlParameter(paramName);
      var defaultStartTime = element.getAttribute('ms-code-yt-start-default');

      // If no URL parameter, use the default start time (if specified)
      if (!startTime && defaultStartTime) {
        startTime = defaultStartTime;
      }

      // If we have a start time (either from URL or default), update the embed
      if (startTime) {
        var iframe = element.querySelector('iframe.embedly-embed');
        if (iframe) {
          updateYouTubeEmbed(iframe, startTime);
        }
      }
    });
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #141 v0.1 💙 - START YOUTUBE VIDEO AT SPECIFIC TIME -->
<script>
  (function() {
    // Function to get URL parameters
    function getUrlParameter(name) {
      name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
      var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
      var results = regex.exec(location.search);
      return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    // Function to update YouTube embed src within Embedly iframe
    function updateYouTubeEmbed(embedly_iframe, startTime) {
      var embedly_src = embedly_iframe.src;
      var youtube_src_match = embedly_src.match(/src=([^&]+)/);
      if (youtube_src_match) {
        var youtube_src = decodeURIComponent(youtube_src_match[1]);
        var new_youtube_src = youtube_src.replace(/(\?|&)start=\d+/, '');
        new_youtube_src += (new_youtube_src.includes('?') ? '&' : '?') + 'start=' + startTime;
        var new_embedly_src = embedly_src.replace(/src=([^&]+)/, 'src=' + encodeURIComponent(new_youtube_src));
        embedly_iframe.src = new_embedly_src;
      }
    }

    // Get all elements with ms-code-yt-start attribute
    var elements = document.querySelectorAll('[ms-code-yt-start]');

    elements.forEach(function(element) {
      var paramName = element.getAttribute('ms-code-yt-start');
      var startTime = getUrlParameter(paramName);
      var defaultStartTime = element.getAttribute('ms-code-yt-start-default');

      // If no URL parameter, use the default start time (if specified)
      if (!startTime && defaultStartTime) {
        startTime = defaultStartTime;
      }

      // If we have a start time (either from URL or default), update the embed
      if (startTime) {
        var iframe = element.querySelector('iframe.embedly-embed');
        if (iframe) {
          updateYouTubeEmbed(iframe, startTime);
        }
      }
    });
  })();
</script>
Ver Memberscript
Flujos personalizados
UX

#140 - Confirmar la coincidencia de entradas

Verificar una entrada antes de permitir su envío: ¡genial para evitar información incorrecta!


<!-- 💙 MEMBERSCRIPT #140 v0.1 💙 - CONFIRM MATCHING INPUTS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form');

    forms.forEach(form => {
      const inputPairs = form.querySelectorAll('[ms-code-conf-input]');
      const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');

      if (!submitButton) {
        console.error('Submit button not found in the form');
        return;
      }

      function validateForm() {
        let fieldsMatch = true;

        inputPairs.forEach(input => {
          const confType = input.getAttribute('ms-code-conf-input');
          const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);
          const errorElement = form.querySelector(`[ms-code-conf-error="${confType}"]`);

          if (confirmInput && errorElement) {
            if (input.value && confirmInput.value) {
              if (input.value !== confirmInput.value) {
                errorElement.style.removeProperty('display');
                fieldsMatch = false;
              } else {
                errorElement.style.display = 'none';
              }
            } else {
              errorElement.style.display = 'none';
            }
          }
        });

        if (fieldsMatch) {
          submitButton.style.removeProperty('pointer-events');
          submitButton.disabled = false;
        } else {
          submitButton.style.pointerEvents = 'none';
          submitButton.disabled = true;
        }
      }

      inputPairs.forEach(input => {
        const confType = input.getAttribute('ms-code-conf-input');
        const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);

        if (confirmInput) {
          input.addEventListener('input', validateForm);
          confirmInput.addEventListener('input', validateForm);
        }
      });

      // Initial validation
      validateForm();

      // Extra precaution: prevent form submission if fields don't match
      form.addEventListener('submit', function(event) {
        if (submitButton.disabled) {
          event.preventDefault();
          console.log('Form submission blocked: Fields do not match');
        }
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #140 v0.1 💙 - CONFIRM MATCHING INPUTS -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form');

    forms.forEach(form => {
      const inputPairs = form.querySelectorAll('[ms-code-conf-input]');
      const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');

      if (!submitButton) {
        console.error('Submit button not found in the form');
        return;
      }

      function validateForm() {
        let fieldsMatch = true;

        inputPairs.forEach(input => {
          const confType = input.getAttribute('ms-code-conf-input');
          const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);
          const errorElement = form.querySelector(`[ms-code-conf-error="${confType}"]`);

          if (confirmInput && errorElement) {
            if (input.value && confirmInput.value) {
              if (input.value !== confirmInput.value) {
                errorElement.style.removeProperty('display');
                fieldsMatch = false;
              } else {
                errorElement.style.display = 'none';
              }
            } else {
              errorElement.style.display = 'none';
            }
          }
        });

        if (fieldsMatch) {
          submitButton.style.removeProperty('pointer-events');
          submitButton.disabled = false;
        } else {
          submitButton.style.pointerEvents = 'none';
          submitButton.disabled = true;
        }
      }

      inputPairs.forEach(input => {
        const confType = input.getAttribute('ms-code-conf-input');
        const confirmInput = form.querySelector(`[ms-code-conf="${confType}"]`);

        if (confirmInput) {
          input.addEventListener('input', validateForm);
          confirmInput.addEventListener('input', validateForm);
        }
      });

      // Initial validation
      validateForm();

      // Extra precaution: prevent form submission if fields don't match
      form.addEventListener('submit', function(event) {
        if (submitButton.disabled) {
          event.preventDefault();
          console.log('Form submission blocked: Fields do not match');
        }
      });
    });
  });
</script>
Ver Memberscript
UX

#139 - Restablecer formulario tras envío

Cree un botón en el estado de éxito del formulario que permita enviarlo de nuevo.


<!-- 💙 MEMBERSCRIPT #139 v0.1 💙 - RESET FORM BUTTON -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Find all "Add another" buttons
    const resetButtons = document.querySelectorAll('[ms-code-reset-form]');

    // Add click event listener to each button
    resetButtons.forEach(function(resetButton) {
      resetButton.addEventListener('click', function(e) {
        e.preventDefault(); // Prevent default link behavior

        // Find the closest form and success message elements
        const formWrapper = this.closest('.w-form');
        const form = formWrapper.querySelector('form');
        const successMessage = formWrapper.querySelector('.w-form-done');

        // Reset the form
        form.reset();

        // Hide the success message
        successMessage.style.display = 'none';

        // Show the form
        form.style.display = 'block';
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #139 v0.1 💙 - RESET FORM BUTTON -->
<script>
  // Wait for the DOM to be fully loaded
  document.addEventListener('DOMContentLoaded', function() {
    // Find all "Add another" buttons
    const resetButtons = document.querySelectorAll('[ms-code-reset-form]');

    // Add click event listener to each button
    resetButtons.forEach(function(resetButton) {
      resetButton.addEventListener('click', function(e) {
        e.preventDefault(); // Prevent default link behavior

        // Find the closest form and success message elements
        const formWrapper = this.closest('.w-form');
        const form = formWrapper.querySelector('form');
        const successMessage = formWrapper.querySelector('.w-form-done');

        // Reset the form
        form.reset();

        // Hide the success message
        successMessage.style.display = 'none';

        // Show the form
        form.style.display = 'block';
      });
    });
  });
</script>
Ver Memberscript
UX

#138 - Desplazamiento del enlace de anclaje

Solucionar el problema con los enlaces de anclaje y las barras de navegación pegajosas/fijas en Webflow.


<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
  // Disable Webflow's built-in smooth scrolling
  var Webflow = Webflow || [];
  Webflow.push(function() {
    $(function() { 
      $(document).off('click.wf-scroll');
    });
  });

  // Smooth scroll implementation with customizable settings
  (function() {
    // Customizable settings
    const SCROLL_SETTINGS = {
      duration: 1000, // in milliseconds
      easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
    };

    const EASING_FUNCTIONS = {
      linear: t => t,
      easeInQuad: t => t * t,
      easeOutQuad: t => t * (2 - t),
      easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
      easeInCubic: t => t * t * t,
      easeOutCubic: t => (--t) * t * t + 1,
      easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
    };

    function getOffset() {
      const navbar = document.querySelector('[ms-code-scroll-offset]');
      if (!navbar) return 0;
      const navbarHeight = navbar.offsetHeight;
      const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
      return navbarHeight + customOffset;
    }

    function smoothScroll(target) {
      const startPosition = window.pageYOffset;
      const offset = getOffset();
      const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
      const distance = targetPosition - startPosition;
      let startTime = null;

      function animation(currentTime) {
        if (startTime === null) startTime = currentTime;
        const timeElapsed = currentTime - startTime;
        const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
        const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
        window.scrollTo(0, startPosition + distance * easeProgress);
        if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
      }

      requestAnimationFrame(animation);
    }

    function handleClick(e) {
      const href = e.currentTarget.getAttribute('href');
      if (href.startsWith('#')) {
        e.preventDefault();
        const target = document.getElementById(href.slice(1));
        if (target) smoothScroll(target);
      }
    }

    function handleHashChange() {
      if (window.location.hash) {
        const target = document.getElementById(window.location.hash.slice(1));
        if (target) {
          setTimeout(() => smoothScroll(target), 0);
        }
      }
    }

    function init() {
      document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', handleClick);
      });
      window.addEventListener('hashchange', handleHashChange);
      handleHashChange(); // Handle initial hash on page load
    }

    document.addEventListener('DOMContentLoaded', init);
    window.Webflow && window.Webflow.push(init);
  })();
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #138 v0.1 💙 - ANCHOR LINK SCROLL OFFSET -->
<script>
  // Disable Webflow's built-in smooth scrolling
  var Webflow = Webflow || [];
  Webflow.push(function() {
    $(function() { 
      $(document).off('click.wf-scroll');
    });
  });

  // Smooth scroll implementation with customizable settings
  (function() {
    // Customizable settings
    const SCROLL_SETTINGS = {
      duration: 1000, // in milliseconds
      easing: 'easeInOutCubic' // 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
    };

    const EASING_FUNCTIONS = {
      linear: t => t,
      easeInQuad: t => t * t,
      easeOutQuad: t => t * (2 - t),
      easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
      easeInCubic: t => t * t * t,
      easeOutCubic: t => (--t) * t * t + 1,
      easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
    };

    function getOffset() {
      const navbar = document.querySelector('[ms-code-scroll-offset]');
      if (!navbar) return 0;
      const navbarHeight = navbar.offsetHeight;
      const customOffset = parseInt(navbar.getAttribute('ms-code-scroll-offset') || '0', 10);
      return navbarHeight + customOffset;
    }

    function smoothScroll(target) {
      const startPosition = window.pageYOffset;
      const offset = getOffset();
      const targetPosition = target.getBoundingClientRect().top + startPosition - offset;
      const distance = targetPosition - startPosition;
      let startTime = null;

      function animation(currentTime) {
        if (startTime === null) startTime = currentTime;
        const timeElapsed = currentTime - startTime;
        const progress = Math.min(timeElapsed / SCROLL_SETTINGS.duration, 1);
        const easeProgress = EASING_FUNCTIONS[SCROLL_SETTINGS.easing](progress);
        window.scrollTo(0, startPosition + distance * easeProgress);
        if (timeElapsed < SCROLL_SETTINGS.duration) requestAnimationFrame(animation);
      }

      requestAnimationFrame(animation);
    }

    function handleClick(e) {
      const href = e.currentTarget.getAttribute('href');
      if (href.startsWith('#')) {
        e.preventDefault();
        const target = document.getElementById(href.slice(1));
        if (target) smoothScroll(target);
      }
    }

    function handleHashChange() {
      if (window.location.hash) {
        const target = document.getElementById(window.location.hash.slice(1));
        if (target) {
          setTimeout(() => smoothScroll(target), 0);
        }
      }
    }

    function init() {
      document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', handleClick);
      });
      window.addEventListener('hashchange', handleHashChange);
      handleHashChange(); // Handle initial hash on page load
    }

    document.addEventListener('DOMContentLoaded', init);
    window.Webflow && window.Webflow.push(init);
  })();
</script>
Ver Memberscript
UX

#137 - Mostrar el nombre del país del visitante

Sustituye el texto por el país en el que se encuentra un usuario en función de su dirección IP.


<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        if (data.country_name) {
          const countryElements = document.querySelectorAll('[ms-code-display-country]');
          countryElements.forEach(element => {
            element.textContent = data.country_name;
          });
        }
      })
      .catch(error => {
        console.error('Error fetching country:', error);
      });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #137 v0.1 💙 - DISPLAY COUNTRY NAME -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    fetch('https://ipapi.co/json/')
      .then(response => response.json())
      .then(data => {
        if (data.country_name) {
          const countryElements = document.querySelectorAll('[ms-code-display-country]');
          countryElements.forEach(element => {
            element.textContent = data.country_name;
          });
        }
      })
      .catch(error => {
        console.error('Error fetching country:', error);
      });
  });
</script>
Ver Memberscript
UX

#136 - Eliminar la ruta de la sección de la URL

Cuando se navegue a una sección, se eliminará la ruta del enlace de anclaje.


<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Check if there's a hash in the URL
    if (window.location.hash) {
        // Get the target element
        const targetId = window.location.hash.substring(1);
        const targetElement = document.getElementById(targetId);

        if (targetElement) {
            // Scroll to the target element
            targetElement.scrollIntoView({behavior: 'smooth'});

            // Remove the hash after a short delay (to allow scrolling to complete)
            setTimeout(function() {
                history.pushState("", document.title, window.location.pathname + window.location.search);
            }, 100);
        }
    }

    // Add click event listeners to all internal links
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function(e) {
            e.preventDefault();

            const targetId = this.getAttribute('href').substring(1);
            const targetElement = document.getElementById(targetId);

            if (targetElement) {
                targetElement.scrollIntoView({behavior: 'smooth'});

                // Remove the hash after a short delay (to allow scrolling to complete)
                setTimeout(function() {
                    history.pushState("", document.title, window.location.pathname + window.location.search);
                }, 100);
            }
        });
    });
});
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #136 💙 REMOVE SECTION PATH FROM URL -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Check if there's a hash in the URL
    if (window.location.hash) {
        // Get the target element
        const targetId = window.location.hash.substring(1);
        const targetElement = document.getElementById(targetId);

        if (targetElement) {
            // Scroll to the target element
            targetElement.scrollIntoView({behavior: 'smooth'});

            // Remove the hash after a short delay (to allow scrolling to complete)
            setTimeout(function() {
                history.pushState("", document.title, window.location.pathname + window.location.search);
            }, 100);
        }
    }

    // Add click event listeners to all internal links
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function(e) {
            e.preventDefault();

            const targetId = this.getAttribute('href').substring(1);
            const targetElement = document.getElementById(targetId);

            if (targetElement) {
                targetElement.scrollIntoView({behavior: 'smooth'});

                // Remove the hash after a short delay (to allow scrolling to complete)
                setTimeout(function() {
                    history.pushState("", document.title, window.location.pathname + window.location.search);
                }, 100);
            }
        });
    });
});
</script>
Ver Memberscript
Flujos personalizados

#135 - Redirección basada en el valor seleccionado

Establezca dinámicamente la redirección del formulario en función de la selección de los usuarios.


<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
  (function() {
    'use strict';

    function initDropdownRedirect() {
      const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
      if (!dropdown) return;

      const form = dropdown.closest('form');
      if (!form) return;

      function updateRedirect() {
        const selectedValue = dropdown.value;
        form.setAttribute('redirect', selectedValue);
        form.setAttribute('data-redirect', selectedValue);
      }

      function handleSubmit(event) {
        event.preventDefault();
        const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
        if (redirectUrl) {
          setTimeout(() => {
            window.location.href = redirectUrl;
          }, 500); // Delay redirect by 500 milliseconds
        } else {
          form.submit(); // Fall back to normal form submission if no redirect is set
        }
      }


      dropdown.addEventListener('change', updateRedirect);
      form.addEventListener('submit', handleSubmit);

      // Initialize redirect on page load
      updateRedirect();
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initDropdownRedirect);
    } else {
      initDropdownRedirect();
    }
  })();
</script>
v0.2

<!-- 💙 MEMBERSCRIPT #135 v0.2 💙 - REDIRECT FORM FROM SELECT VALUE -->
<script>
  (function() {
    'use strict';

    function initDropdownRedirect() {
      const dropdown = document.querySelector('select[ms-code-dropdown-redirect]');
      if (!dropdown) return;

      const form = dropdown.closest('form');
      if (!form) return;

      function updateRedirect() {
        const selectedValue = dropdown.value;
        form.setAttribute('redirect', selectedValue);
        form.setAttribute('data-redirect', selectedValue);
      }

      function handleSubmit(event) {
        event.preventDefault();
        const redirectUrl = form.getAttribute('data-redirect') || form.getAttribute('redirect');
        if (redirectUrl) {
          setTimeout(() => {
            window.location.href = redirectUrl;
          }, 500); // Delay redirect by 500 milliseconds
        } else {
          form.submit(); // Fall back to normal form submission if no redirect is set
        }
      }


      dropdown.addEventListener('change', updateRedirect);
      form.addEventListener('submit', handleSubmit);

      // Initialize redirect on page load
      updateRedirect();
    }

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initDropdownRedirect);
    } else {
      initDropdownRedirect();
    }
  })();
</script>
Ver Memberscript
UX

#134 - Desplazarse al principio al cambiar de pestaña

Al cambiar de pestaña, la página se desplazará a la parte superior de la sección de pestañas.


<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all tab containers with the ms-code-tab-scroll-top attribute
    const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');

    tabContainers.forEach(container => {
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');

      tabLinks.forEach(link => {
        link.addEventListener('click', function(e) {
          // Small delay to ensure the new tab content is rendered
          setTimeout(() => {
            // Find the active tab pane within this container
            const activePane = container.querySelector('.w-tab-pane.w--tab-active');

            if (activePane) {
              // Calculate the new scroll position
              const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;

              // Scroll to the new position
              window.scrollTo({
                top: newScrollPosition,
                behavior: 'smooth'
              });
            }
          }, 50); // 50ms delay, adjust if needed
        });
      });
    });
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #134 💙 - SCROLL TO TOP OF TABS ON CHANGE -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Select all tab containers with the ms-code-tab-scroll-top attribute
    const tabContainers = document.querySelectorAll('.w-tabs[ms-code-tab-scroll-top]');

    tabContainers.forEach(container => {
      const tabLinks = container.querySelectorAll('.w-tab-link');
      const scrollTopValue = parseInt(container.getAttribute('ms-code-tab-scroll-top') || '0');

      tabLinks.forEach(link => {
        link.addEventListener('click', function(e) {
          // Small delay to ensure the new tab content is rendered
          setTimeout(() => {
            // Find the active tab pane within this container
            const activePane = container.querySelector('.w-tab-pane.w--tab-active');

            if (activePane) {
              // Calculate the new scroll position
              const newScrollPosition = container.getBoundingClientRect().top + window.pageYOffset + scrollTopValue;

              // Scroll to the new position
              window.scrollTo({
                top: newScrollPosition,
                behavior: 'smooth'
              });
            }
          }, 50); // 50ms delay, adjust if needed
        });
      });
    });
  });
</script>
Ver Memberscript
Seguridad

#133 - Imágenes con marca de agua automática

Añada fácilmente una marca de agua a las imágenes de su sitio Webflow.


<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
  function addWatermarkToImages() {
    const images = document.querySelectorAll('img[ms-code-watermark]');

    images.forEach(img => {
      img.crossOrigin = "Anonymous";  // This allows us to work with images from other domains
      img.onload = function() {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        // Set canvas size to match the image
        canvas.width = img.width;
        canvas.height = img.height;

        // Draw the original image onto the canvas
        ctx.drawImage(img, 0, 0, img.width, img.height);

        // Get watermark text from attribute
        const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';

        // Add watermark
        ctx.font = `${img.width / 20}px Arial`;  // Adjust font size based on image width
        ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // Rotate and draw the watermark text
        ctx.save();
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(-Math.PI / 4);  // Rotate 45 degrees
        ctx.fillText(watermarkText, 0, 0);
        ctx.restore();

        // Preserve the original image's classes and other attributes
        canvas.className = img.className;
        for (let i = 0; i < img.attributes.length; i++) {
          const attr = img.attributes[i];
          if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
            canvas.setAttribute(attr.name, attr.value);
          }
        }

        // Replace the original image with the watermarked canvas
        img.parentNode.replaceChild(canvas, img);
      };

      // Trigger onload event (in case the image is already loaded)
      if (img.complete) {
        img.onload();
      }
    });
  }

  // Run the function when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', addWatermarkToImages);
</script>
v0.1

<!-- 💙 MEMBERSCRIPT #133 v0.1 💙 - AUTO IMAGE WATERMARK -->
<script>
  function addWatermarkToImages() {
    const images = document.querySelectorAll('img[ms-code-watermark]');

    images.forEach(img => {
      img.crossOrigin = "Anonymous";  // This allows us to work with images from other domains
      img.onload = function() {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        // Set canvas size to match the image
        canvas.width = img.width;
        canvas.height = img.height;

        // Draw the original image onto the canvas
        ctx.drawImage(img, 0, 0, img.width, img.height);

        // Get watermark text from attribute
        const watermarkText = img.getAttribute('ms-code-watermark') || 'Watermark';

        // Add watermark
        ctx.font = `${img.width / 20}px Arial`;  // Adjust font size based on image width
        ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // Rotate and draw the watermark text
        ctx.save();
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(-Math.PI / 4);  // Rotate 45 degrees
        ctx.fillText(watermarkText, 0, 0);
        ctx.restore();

        // Preserve the original image's classes and other attributes
        canvas.className = img.className;
        for (let i = 0; i < img.attributes.length; i++) {
          const attr = img.attributes[i];
          if (attr.name !== 'src' && attr.name !== 'ms-code-watermark') {
            canvas.setAttribute(attr.name, attr.value);
          }
        }

        // Replace the original image with the watermarked canvas
        img.parentNode.replaceChild(canvas, img);
      };

      // Trigger onload event (in case the image is already loaded)
      if (img.complete) {
        img.onload();
      }
    });
  }

  // Run the function when the DOM is fully loaded
  document.addEventListener('DOMContentLoaded', addWatermarkToImages);
</script>
Ver Memberscript
Accesibilidad
Modales

#132 - Ocultar elementos con la tecla Escape

Añade un atributo y cuando se haga clic en la tecla esc, el elemento se establecerá en mostrar ninguno.


<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
  document.addEventListener('keydown', function(event) {
    // Check if the pressed key is ESC (key code 27)
    if (event.key === 'Escape' || event.keyCode === 27) {
      // Find all elements with the attribute ms-code-close-esc
      const elements = document.querySelectorAll('[ms-code-close-esc]');

      // Loop through the elements and set their display to 'none'
      elements.forEach(function(element) {
        element.style.display = 'none';
      });
    }
  });
</script>
v0.1

<!-- 💙 MEMBERSCRIPT 💙 - HIDE ELEMENTS WITH ESC KEY -->
<script>
  document.addEventListener('keydown', function(event) {
    // Check if the pressed key is ESC (key code 27)
    if (event.key === 'Escape' || event.keyCode === 27) {
      // Find all elements with the attribute ms-code-close-esc
      const elements = document.querySelectorAll('[ms-code-close-esc]');

      // Loop through the elements and set their display to 'none'
      elements.forEach(function(element) {
        element.style.display = 'none';
      });
    }
  });
</script>
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.