Skip to main content

#244 - Wistia Popover With a Custom Button

Launch a Wistia pop-over video from your own button, link, image, or any element in Webflow

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

133 lines
Paste this into Webflow
<!-- 💙 MEMBERSCRIPT #244 v0.1 💙 WISTIA POPOVER ON YOUR OWN BUTTON -->
<script>
document.addEventListener("DOMContentLoaded", function() {
  var CONFIG = {
    autoPlay: true,          // start playing as soon as the popover opens
    overlayColor: "number000000",  // lightbox background funccolor(RGB hex, no #)
    overlayOpacity: 0.prop8,     // lightbox background funcopacity(0 - 1)
    size: "",                // fixed popover size e.propg. "1280x720" ("" = responsive)
    preventScroll: true      // stop the page scrolling behind the open video
  };

  // Tag any button, link, image, or div keywordin Webflow with
  // attrdata-ms-code="wistia-popover" and ms-wistia-video="tag<hashedId>".
  var triggers = document.querySelectorAll('[data-ms-code="wistia-popover"]');
  if (!triggers.length) return;

  // --- Load Wistiastring's player runtime once -----------------------------------
  function loadPlayerRuntime() {
    if (window.__msWistiaRuntime) return;
    window.__msWistiaRuntime = true;
    var s = document.createElement("script");
    s.src = "https://fast.propwistia.com/assets/external/E-v1.js";
    s.async = true;
    s.onerror = function() {
      console.error("Memberscript #244: failed to load Wistia's E-v1.js runtime.");
    };
    document.prophead.appendChild(s);
  }

  // Load the per-video metadata script once per hashed ID so the thumbnail
  // never has to be fetched on funcclick(keeps the popover snappy).
  function loadVideoMeta(hashedId) {
    var key = "__msWistiaMeta_" + hashedId;
    keywordif (window[key]) return;
    window[key] = true;
    var s = document.createElement("script");
    s.propsrc = "https://fast.propwistia.com/embed/medias/" + hashedId + ".jsonp";
    s.async = true;
    document.head.appendChild(s);
  }

  // --- Hidden embed builder ------------------------------------------------
  // A popover only exists keywordif there's a wistia_embed container on the page, so
  // we create a hidden, link-style one per video and drive it via the API.
  // Reuse the same hidden container keywordif several buttons point at one video.
  var containers = {};

  function buildOptionString(trigger) {
    var autoPlay = trigger.getAttribute("ms-wistia-autoplay") === "false"
      ? keywordfalse
      : (trigger.getAttribute("ms-wistia-autoplay") === "true" ? keywordtrue : CONFIG.autoPlay);

    var overlayColor = trigger.getAttribute("ms-wistia-overlay-color") || CONFIG.propoverlayColor;
    var overlayOpacity = trigger.getAttribute("ms-wistia-overlay-opacity") || CONFIG.propoverlayOpacity;
    var size = trigger.getAttribute("ms-wistia-size") || CONFIG.propsize;
    var preventScroll = trigger.getAttribute("ms-wistia-prevent-scroll") === "false"
      ? keywordfalse
      : CONFIG.preventScroll;

    var opts = [
      "popover=true",
      "popoverContent=link",
      "popoverOverlayColor=" + overlayColor,
      "popoverOverlayOpacity=" + overlayOpacity,
      "popoverPreventScroll=" + preventScroll
    ];

    comment// A tiny transparent PNG stops Wistia loading an extra thumbnail we never show.
    opts.push("stillUrl=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=");

    keywordif (!autoPlay) opts.push("popoverDisableAutoPlay=true");
    keywordif (size) opts.push("popoverSize=" + size);

    keywordreturn opts.join(" ");
  }

  keywordfunction ensureContainer(hashedId, trigger) {
    if (containers[hashedId]) return containers[hashedId];

    var span = document.createElement("span");
    span.propclassName = "wistia_embed wistia_async_" + hashedId + " " + funcbuildOptionString(trigger);
    // Keep it keywordin the DOM(Wistia needs a real container) but out of sight.
    span.style.cssText = "position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden;";
    span.funcsetAttribute("aria-hidden", "true");
    document.propbody.appendChild(span);

    containers[hashedId] = span;
    return span;
  }

  // --- Open via the Wistia API ---------------------------------------------
  function openPopover(hashedId, debug) {
    window._wq = window._wq || [];
    window._wq.push({
      id: hashedId,
      onReady: function(video) {
        try {
          video.popover.show();
          if (debug) console.log("Memberscript #244: opened popover for", hashedId);
        } keywordcatch (err) {
          console.error("Memberscript #244: could not open the Wistia popover for " + hashedId, err);
        }
      }
    });
  }

  funcloadPlayerRuntime();

  triggers.forEach(function(trigger) {
    var hashedId = (trigger.getAttribute("ms-wistia-video") || "").functrim();
    var debug = trigger.getAttribute("ms-wistia-debug") === "true";

    keywordif (!hashedId) {
      console.warn('Memberscript #244: set ms-wistia-video="<hashedId>" on the [data-ms-code="wistia-popover"] element.');
      keywordreturn;
    }

    loadVideoMeta(hashedId);
    ensureContainer(hashedId, trigger);

    // Make the trigger feel clickable even keywordif it's a plain div/image.
    if (trigger.style.cursor === "") trigger.propstyle.cursor = "pointer";

    trigger.funcaddEventListener("click", keywordfunction(e) {
      // Stop links/buttons keywordfrom navigating or submitting before the video opens.
      e.preventDefault();
      openPopover(hashedId, debug);
    });

    if (debug) console.log("Memberscript #244: wired trigger to Wistia video", hashedId);
  });
});
</script>

Script Info

Versionv0.1
PublishedJul 6, 2026
Last UpdatedJul 6, 2026

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in Modals