chunk-026-popup.js

I’ll build this as a strong standalone popup chunk for RX Theme: reusable popup manager, triggers, accessibility, cookies/localStorage, exit intent, scroll/time triggers, newsletter/content modal support, and safe WordPress-friendly initialization.

Below is an advanced chunk-026-popup.js file for:

assets/static-js/static-chunks-js/chunk-026-popup.js

It is safe for RX Theme, plain JavaScript, no jQuery, WordPress-friendly, accessibility-focused, and supports many popup systems: time popup, scroll popup, exit intent, click trigger, cookie/localStorage limit, focus trap, ESC close, overlay close, mobile-safe behavior, analytics hooks, and custom events.

/*!
 * RX Theme Popup Chunk
 * File: assets/static-js/static-chunks-js/chunk-026-popup.js
 * Purpose: Advanced popup/modal system for RX Theme
 * Author: RX Theme
 * Version: 1.0.0
 */

(function () {
  "use strict";

  /**
   * RX Popup Manager
   *
   * Supported features:
   * - Time delayed popup
   * - Scroll depth popup
   * - Exit intent popup
   * - Click trigger popup
   * - Auto open popup
   * - Cookie/localStorage frequency control
   * - Accessibility support
   * - Focus trap
   * - ESC key close
   * - Overlay click close
   * - Body scroll lock
   * - Newsletter popup
   * - Content popup
   * - Confirm popup
   * - Custom events
   * - WordPress-friendly hooks
   */

  var RXPopup = {
    version: "1.0.0",

    defaults: {
      selector: "[data-rx-popup]",
      triggerSelector: "[data-rx-popup-trigger]",
      closeSelector: "[data-rx-popup-close]",
      overlayClass: "rx-popup-overlay",
      activeClass: "is-active",
      visibleClass: "is-visible",
      bodyOpenClass: "rx-popup-open",
      animationInClass: "rx-popup-animate-in",
      animationOutClass: "rx-popup-animate-out",
      storagePrefix: "rx_popup_",
      storageType: "localStorage", // localStorage, sessionStorage, cookie
      cookieDays: 7,
      focusableSelectors:
        'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]',
      closeOnOverlay: true,
      closeOnEsc: true,
      lockBodyScroll: true,
      restoreFocus: true,
      debug: false
    },

    state: {
      initialized: false,
      activePopup: null,
      lastFocusedElement: null,
      scrollTriggersFired: {},
      timeTriggersFired: {},
      exitIntentFired: false,
      resizeTimer: null,
      scrollTimer: null
    },

    /**
     * Init popup system
     */
    init: function (options) {
      if (this.state.initialized) {
        return;
      }

      this.settings = this.extend({}, this.defaults, options || {});
      this.cacheElements();
      this.bindEvents();
      this.preparePopups();
      this.initAutoTriggers();

      this.state.initialized = true;

      this.dispatch("rxPopupReady", {
        version: this.version
      });

      this.log("RX Popup initialized");
    },

    /**
     * Cache DOM elements
     */
    cacheElements: function () {
      this.popups = Array.prototype.slice.call(
        document.querySelectorAll(this.settings.selector)
      );

      this.triggers = Array.prototype.slice.call(
        document.querySelectorAll(this.settings.triggerSelector)
      );

      this.closeButtons = Array.prototype.slice.call(
        document.querySelectorAll(this.settings.closeSelector)
      );
    },

    /**
     * Prepare all popup elements
     */
    preparePopups: function () {
      var self = this;

      this.popups.forEach(function (popup, index) {
        var id = popup.getAttribute("id");

        if (!id) {
          id = "rx-popup-" + (index + 1);
          popup.setAttribute("id", id);
        }

        popup.setAttribute("role", popup.getAttribute("role") || "dialog");
        popup.setAttribute("aria-modal", "true");
        popup.setAttribute("aria-hidden", "true");

        if (!popup.hasAttribute("tabindex")) {
          popup.setAttribute("tabindex", "-1");
        }

        var labelledBy = popup.getAttribute("data-rx-popup-labelledby");
        if (labelledBy) {
          popup.setAttribute("aria-labelledby", labelledBy);
        }

        var describedBy = popup.getAttribute("data-rx-popup-describedby");
        if (describedBy) {
          popup.setAttribute("aria-describedby", describedBy);
        }

        self.createOverlayIfNeeded(popup);
      });
    },

    /**
     * Create overlay if popup needs it
     */
    createOverlayIfNeeded: function (popup) {
      var hasOverlay = popup.getAttribute("data-rx-popup-overlay");

      if (hasOverlay === "false") {
        return;
      }

      var popupId = popup.getAttribute("id");
      var existingOverlay = document.querySelector(
        '[data-rx-popup-overlay-for="' + popupId + '"]'
      );

      if (existingOverlay) {
        return;
      }

      var overlay = document.createElement("div");
      overlay.className = this.settings.overlayClass;
      overlay.setAttribute("data-rx-popup-overlay-for", popupId);
      overlay.setAttribute("aria-hidden", "true");

      popup.parentNode.insertBefore(overlay, popup);
    },

    /**
     * Bind global and element events
     */
    bindEvents: function () {
      var self = this;

      document.addEventListener("click", function (event) {
        self.handleDocumentClick(event);
      });

      document.addEventListener("keydown", function (event) {
        self.handleKeydown(event);
      });

      window.addEventListener(
        "scroll",
        function () {
          self.throttleScroll();
        },
        { passive: true }
      );

      window.addEventListener("resize", function () {
        clearTimeout(self.state.resizeTimer);
        self.state.resizeTimer = setTimeout(function () {
          self.handleResize();
        }, 150);
      });

      document.addEventListener("mouseleave", function (event) {
        self.handleExitIntent(event);
      });

      document.addEventListener("visibilitychange", function () {
        self.handleVisibilityChange();
      });
    },

    /**
     * Handle all document click events
     */
    handleDocumentClick: function (event) {
      var trigger = event.target.closest(this.settings.triggerSelector);
      var closeBtn = event.target.closest(this.settings.closeSelector);
      var overlay = event.target.closest("." + this.settings.overlayClass);

      if (trigger) {
        event.preventDefault();

        var targetId = trigger.getAttribute("data-rx-popup-trigger");
        var popup = this.getPopupById(targetId);

        if (popup) {
          this.open(popup, {
            source: "click",
            trigger: trigger
          });
        }

        return;
      }

      if (closeBtn) {
        event.preventDefault();

        var popupToClose = closeBtn.closest(this.settings.selector);

        if (popupToClose) {
          this.close(popupToClose, {
            source: "close-button"
          });
        } else {
          this.closeActive({
            source: "close-button"
          });
        }

        return;
      }

      if (
        overlay &&
        this.settings.closeOnOverlay &&
        this.state.activePopup
      ) {
        var overlayFor = overlay.getAttribute("data-rx-popup-overlay-for");

        if (
          overlayFor &&
          this.state.activePopup.getAttribute("id") === overlayFor
        ) {
          this.close(this.state.activePopup, {
            source: "overlay"
          });
        }
      }
    },

    /**
     * Handle keyboard controls
     */
    handleKeydown: function (event) {
      if (!this.state.activePopup) {
        return;
      }

      if (event.key === "Escape" && this.settings.closeOnEsc) {
        event.preventDefault();
        this.close(this.state.activePopup, {
          source: "escape"
        });
        return;
      }

      if (event.key === "Tab") {
        this.trapFocus(event, this.state.activePopup);
      }
    },

    /**
     * Open popup
     */
    open: function (popup, meta) {
      if (!popup) {
        return;
      }

      if (typeof popup === "string") {
        popup = this.getPopupById(popup);
      }

      if (!popup) {
        return;
      }

      var popupId = popup.getAttribute("id");

      if (!this.canShow(popup)) {
        this.log("Popup blocked by frequency control:", popupId);
        return;
      }

      if (this.state.activePopup && this.state.activePopup !== popup) {
        this.close(this.state.activePopup, {
          source: "switch"
        });
      }

      this.state.lastFocusedElement = document.activeElement;
      this.state.activePopup = popup;

      popup.classList.remove(this.settings.animationOutClass);
      popup.classList.add(
        this.settings.activeClass,
        this.settings.visibleClass,
        this.settings.animationInClass
      );

      popup.setAttribute("aria-hidden", "false");

      var overlay = this.getOverlay(popup);
      if (overlay) {
        overlay.classList.add(this.settings.activeClass, this.settings.visibleClass);
      }

      if (this.settings.lockBodyScroll) {
        this.lockBodyScroll();
      }

      this.focusPopup(popup);
      this.markShown(popup);

      this.dispatch("rxPopupOpen", {
        popup: popup,
        id: popupId,
        meta: meta || {}
      });

      this.log("Popup opened:", popupId);
    },

    /**
     * Close popup
     */
    close: function (popup, meta) {
      if (!popup) {
        return;
      }

      if (typeof popup === "string") {
        popup = this.getPopupById(popup);
      }

      if (!popup) {
        return;
      }

      var self = this;
      var popupId = popup.getAttribute("id");
      var animationDuration = this.getAnimationDuration(popup);

      popup.classList.remove(this.settings.animationInClass);
      popup.classList.add(this.settings.animationOutClass);
      popup.setAttribute("aria-hidden", "true");

      var overlay = this.getOverlay(popup);
      if (overlay) {
        overlay.classList.remove(this.settings.visibleClass);
      }

      setTimeout(function () {
        popup.classList.remove(
          self.settings.activeClass,
          self.settings.visibleClass,
          self.settings.animationOutClass
        );

        if (overlay) {
          overlay.classList.remove(self.settings.activeClass);
        }

        if (self.state.activePopup === popup) {
          self.state.activePopup = null;
        }

        if (!self.state.activePopup && self.settings.lockBodyScroll) {
          self.unlockBodyScroll();
        }

        if (self.settings.restoreFocus && self.state.lastFocusedElement) {
          try {
            self.state.lastFocusedElement.focus();
          } catch (error) {
            self.log("Focus restore failed", error);
          }
        }

        self.dispatch("rxPopupClose", {
          popup: popup,
          id: popupId,
          meta: meta || {}
        });

        self.log("Popup closed:", popupId);
      }, animationDuration);
    },

    /**
     * Close active popup
     */
    closeActive: function (meta) {
      if (this.state.activePopup) {
        this.close(this.state.activePopup, meta || {});
      }
    },

    /**
     * Toggle popup
     */
    toggle: function (popup, meta) {
      if (typeof popup === "string") {
        popup = this.getPopupById(popup);
      }

      if (!popup) {
        return;
      }

      if (popup.classList.contains(this.settings.activeClass)) {
        this.close(popup, meta || {});
      } else {
        this.open(popup, meta || {});
      }
    },

    /**
     * Auto trigger systems
     */
    initAutoTriggers: function () {
      var self = this;

      this.popups.forEach(function (popup) {
        self.initTimeTrigger(popup);
        self.initAutoOpenTrigger(popup);
      });
    },

    /**
     * Time delay popup
     *
     * HTML:
     * <div data-rx-popup data-rx-popup-delay="3000"></div>
     */
    initTimeTrigger: function (popup) {
      var delay = parseInt(popup.getAttribute("data-rx-popup-delay"), 10);

      if (!delay || delay < 1) {
        return;
      }

      var popupId = popup.getAttribute("id");

      if (this.state.timeTriggersFired[popupId]) {
        return;
      }

      var self = this;

      setTimeout(function () {
        if (!self.state.timeTriggersFired[popupId]) {
          self.state.timeTriggersFired[popupId] = true;

          self.open(popup, {
            source: "time",
            delay: delay
          });
        }
      }, delay);
    },

    /**
     * Auto open popup
     *
     * HTML:
     * <div data-rx-popup data-rx-popup-auto="true"></div>
     */
    initAutoOpenTrigger: function (popup) {
      var auto = popup.getAttribute("data-rx-popup-auto");

      if (auto !== "true") {
        return;
      }

      var self = this;

      requestAnimationFrame(function () {
        self.open(popup, {
          source: "auto"
        });
      });
    },

    /**
     * Scroll trigger throttling
     */
    throttleScroll: function () {
      var self = this;

      if (this.state.scrollTimer) {
        return;
      }

      this.state.scrollTimer = setTimeout(function () {
        self.state.scrollTimer = null;
        self.handleScrollTriggers();
      }, 120);
    },

    /**
     * Scroll depth popup
     *
     * HTML:
     * <div data-rx-popup data-rx-popup-scroll="60"></div>
     */
    handleScrollTriggers: function () {
      var self = this;
      var scrollPercent = this.getScrollPercent();

      this.popups.forEach(function (popup) {
        var targetPercent = parseInt(
          popup.getAttribute("data-rx-popup-scroll"),
          10
        );

        if (!targetPercent || targetPercent < 1) {
          return;
        }

        var popupId = popup.getAttribute("id");

        if (self.state.scrollTriggersFired[popupId]) {
          return;
        }

        if (scrollPercent >= targetPercent) {
          self.state.scrollTriggersFired[popupId] = true;

          self.open(popup, {
            source: "scroll",
            scrollPercent: scrollPercent,
            targetPercent: targetPercent
          });
        }
      });
    },

    /**
     * Exit intent popup
     *
     * HTML:
     * <div data-rx-popup data-rx-popup-exit="true"></div>
     */
    handleExitIntent: function (event) {
      if (this.state.exitIntentFired) {
        return;
      }

      if (event.clientY > 10) {
        return;
      }

      var popup = this.popups.find(function (item) {
        return item.getAttribute("data-rx-popup-exit") === "true";
      });

      if (!popup) {
        return;
      }

      this.state.exitIntentFired = true;

      this.open(popup, {
        source: "exit-intent"
      });
    },

    /**
     * Browser tab visibility event
     */
    handleVisibilityChange: function () {
      if (document.hidden) {
        this.dispatch("rxPopupPageHidden", {});
      } else {
        this.dispatch("rxPopupPageVisible", {});
      }
    },

    /**
     * Resize handler
     */
    handleResize: function () {
      if (this.state.activePopup) {
        this.dispatch("rxPopupResize", {
          popup: this.state.activePopup
        });
      }
    },

    /**
     * Frequency control
     */
    canShow: function (popup) {
      var id = popup.getAttribute("id");
      var once = popup.getAttribute("data-rx-popup-once");
      var frequency = popup.getAttribute("data-rx-popup-frequency");
      var storageKey = this.settings.storagePrefix + id;
      var stored = this.getStorage(storageKey);

      if (once === "true" && stored) {
        return false;
      }

      if (frequency) {
        var hours = parseFloat(frequency);

        if (!isNaN(hours) && stored) {
          var lastShown = parseInt(stored, 10);
          var now = Date.now();
          var limit = hours * 60 * 60 * 1000;

          if (now - lastShown < limit) {
            return false;
          }
        }
      }

      return true;
    },

    /**
     * Mark popup as shown
     */
    markShown: function (popup) {
      var id = popup.getAttribute("id");
      var once = popup.getAttribute("data-rx-popup-once");
      var frequency = popup.getAttribute("data-rx-popup-frequency");

      if (once === "true" || frequency) {
        this.setStorage(this.settings.storagePrefix + id, String(Date.now()));
      }
    },

    /**
     * Storage get
     */
    getStorage: function (key) {
      try {
        if (this.settings.storageType === "sessionStorage") {
          return window.sessionStorage.getItem(key);
        }

        if (this.settings.storageType === "cookie") {
          return this.getCookie(key);
        }

        return window.localStorage.getItem(key);
      } catch (error) {
        this.log("Storage get failed:", error);
        return null;
      }
    },

    /**
     * Storage set
     */
    setStorage: function (key, value) {
      try {
        if (this.settings.storageType === "sessionStorage") {
          window.sessionStorage.setItem(key, value);
          return;
        }

        if (this.settings.storageType === "cookie") {
          this.setCookie(key, value, this.settings.cookieDays);
          return;
        }

        window.localStorage.setItem(key, value);
      } catch (error) {
        this.log("Storage set failed:", error);
      }
    },

    /**
     * Cookie set
     */
    setCookie: function (name, value, days) {
      var expires = "";

      if (days) {
        var date = new Date();
        date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
        expires = "; expires=" + date.toUTCString();
      }

      document.cookie =
        encodeURIComponent(name) +
        "=" +
        encodeURIComponent(value || "") +
        expires +
        "; path=/; SameSite=Lax";
    },

    /**
     * Cookie get
     */
    getCookie: function (name) {
      var nameEQ = encodeURIComponent(name) + "=";
      var cookies = document.cookie.split(";");

      for (var i = 0; i < cookies.length; i++) {
        var cookie = cookies[i];

        while (cookie.charAt(0) === " ") {
          cookie = cookie.substring(1, cookie.length);
        }

        if (cookie.indexOf(nameEQ) === 0) {
          return decodeURIComponent(cookie.substring(nameEQ.length));
        }
      }

      return null;
    },

    /**
     * Focus popup
     */
    focusPopup: function (popup) {
      var autofocus = popup.querySelector("[autofocus]");
      var focusable = this.getFocusableElements(popup);

      if (autofocus) {
        autofocus.focus();
        return;
      }

      if (focusable.length) {
        focusable[0].focus();
        return;
      }

      popup.focus();
    },

    /**
     * Get focusable elements
     */
    getFocusableElements: function (container) {
      return Array.prototype.slice
        .call(container.querySelectorAll(this.settings.focusableSelectors))
        .filter(function (element) {
          return (
            element.offsetWidth > 0 ||
            element.offsetHeight > 0 ||
            element === document.activeElement
          );
        });
    },

    /**
     * Trap focus inside popup
     */
    trapFocus: function (event, popup) {
      var focusable = this.getFocusableElements(popup);

      if (!focusable.length) {
        event.preventDefault();
        popup.focus();
        return;
      }

      var first = focusable[0];
      var last = focusable[focusable.length - 1];

      if (event.shiftKey && document.activeElement === first) {
        event.preventDefault();
        last.focus();
      } else if (!event.shiftKey && document.activeElement === last) {
        event.preventDefault();
        first.focus();
      }
    },

    /**
     * Body scroll lock
     */
    lockBodyScroll: function () {
      var scrollY = window.scrollY || window.pageYOffset;

      document.body.dataset.rxPopupScrollY = String(scrollY);
      document.body.style.position = "fixed";
      document.body.style.top = "-" + scrollY + "px";
      document.body.style.left = "0";
      document.body.style.right = "0";
      document.body.style.width = "100%";
      document.body.classList.add(this.settings.bodyOpenClass);
    },

    /**
     * Body scroll unlock
     */
    unlockBodyScroll: function () {
      var scrollY = document.body.dataset.rxPopupScrollY;

      document.body.style.position = "";
      document.body.style.top = "";
      document.body.style.left = "";
      document.body.style.right = "";
      document.body.style.width = "";
      document.body.classList.remove(this.settings.bodyOpenClass);

      if (scrollY) {
        window.scrollTo(0, parseInt(scrollY, 10));
      }

      delete document.body.dataset.rxPopupScrollY;
    },

    /**
     * Get popup by ID
     */
    getPopupById: function (id) {
      if (!id) {
        return null;
      }

      id = id.replace("#", "");

      return document.getElementById(id);
    },

    /**
     * Get overlay for popup
     */
    getOverlay: function (popup) {
      var id = popup.getAttribute("id");

      if (!id) {
        return null;
      }

      return document.querySelector(
        '[data-rx-popup-overlay-for="' + id + '"]'
      );
    },

    /**
     * Get page scroll percentage
     */
    getScrollPercent: function () {
      var scrollTop = window.scrollY || window.pageYOffset;
      var docHeight = document.documentElement.scrollHeight;
      var winHeight = window.innerHeight;
      var scrollable = docHeight - winHeight;

      if (scrollable <= 0) {
        return 100;
      }

      return Math.round((scrollTop / scrollable) * 100);
    },

    /**
     * Get CSS animation duration
     */
    getAnimationDuration: function (element) {
      var styles = window.getComputedStyle(element);
      var duration = styles.animationDuration || styles.transitionDuration;

      if (!duration) {
        return 250;
      }

      var durations = duration.split(",").map(function (value) {
        value = value.trim();

        if (value.indexOf("ms") > -1) {
          return parseFloat(value);
        }

        if (value.indexOf("s") > -1) {
          return parseFloat(value) * 1000;
        }

        return 250;
      });

      return Math.max.apply(null, durations) || 250;
    },

    /**
     * Public method: open by ID
     */
    openById: function (id) {
      this.open(id, {
        source: "api"
      });
    },

    /**
     * Public method: close by ID
     */
    closeById: function (id) {
      this.close(id, {
        source: "api"
      });
    },

    /**
     * Public method: destroy popup system
     */
    destroy: function () {
      this.closeActive({
        source: "destroy"
      });

      this.state.initialized = false;
      this.state.activePopup = null;
      this.state.lastFocusedElement = null;

      this.dispatch("rxPopupDestroy", {});
    },

    /**
     * Dispatch custom event
     */
    dispatch: function (name, detail) {
      var event;

      try {
        event = new CustomEvent(name, {
          detail: detail || {}
        });
      } catch (error) {
        event = document.createEvent("CustomEvent");
        event.initCustomEvent(name, true, true, detail || {});
      }

      document.dispatchEvent(event);
    },

    /**
     * Extend object helper
     */
    extend: function () {
      var output = arguments[0] || {};

      for (var i = 1; i < arguments.length; i++) {
        if (!arguments[i]) {
          continue;
        }

        for (var key in arguments[i]) {
          if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
            output[key] = arguments[i][key];
          }
        }
      }

      return output;
    },

    /**
     * Debug logger
     */
    log: function () {
      if (!this.settings || !this.settings.debug) {
        return;
      }

      if (window.console && typeof window.console.log === "function") {
        window.console.log.apply(window.console, arguments);
      }
    }
  };

  /**
   * RX Popup HTML Builder
   *
   * Allows dynamic popup creation by JavaScript.
   */
  var RXPopupBuilder = {
    create: function (options) {
      var settings = this.extend(
        {
          id: "rx-dynamic-popup-" + Date.now(),
          title: "",
          content: "",
          closeText: "Close",
          size: "medium",
          type: "content",
          showClose: true,
          extraClass: ""
        },
        options || {}
      );

      var popup = document.createElement("div");
      popup.className =
        "rx-popup rx-popup--" +
        settings.size +
        " rx-popup--" +
        settings.type +
        " " +
        settings.extraClass;

      popup.setAttribute("id", settings.id);
      popup.setAttribute("data-rx-popup", "");
      popup.setAttribute("role", "dialog");
      popup.setAttribute("aria-modal", "true");
      popup.setAttribute("aria-hidden", "true");

      var html = "";

      if (settings.showClose) {
        html +=
          '<button type="button" class="rx-popup__close" data-rx-popup-close aria-label="' +
          this.escape(settings.closeText) +
          '">×</button>';
      }

      html += '<div class="rx-popup__inner">';

      if (settings.title) {
        html +=
          '<h2 class="rx-popup__title">' +
          this.escape(settings.title) +
          "</h2>";
      }

      html += '<div class="rx-popup__content">';
      html += settings.content;
      html += "</div>";
      html += "</div>";

      popup.innerHTML = html;
      document.body.appendChild(popup);

      if (window.RXPopup && window.RXPopup.state.initialized) {
        window.RXPopup.popups.push(popup);
        window.RXPopup.preparePopups();
      }

      return popup;
    },

    alert: function (message, title) {
      var popup = this.create({
        title: title || "Notice",
        content:
          '<p class="rx-popup__message">' + this.escape(message) + "</p>",
        type: "alert",
        size: "small"
      });

      window.RXPopup.open(popup, {
        source: "builder-alert"
      });

      return popup;
    },

    confirm: function (options) {
      var settings = this.extend(
        {
          title: "Are you sure?",
          message: "",
          confirmText: "Confirm",
          cancelText: "Cancel",
          onConfirm: null,
          onCancel: null
        },
        options || {}
      );

      var content =
        '<p class="rx-popup__message">' +
        this.escape(settings.message) +
        "</p>" +
        '<div class="rx-popup__actions">' +
        '<button type="button" class="rx-popup__button rx-popup__button--confirm" data-rx-confirm-action="confirm">' +
        this.escape(settings.confirmText) +
        "</button>" +
        '<button type="button" class="rx-popup__button rx-popup__button--cancel" data-rx-confirm-action="cancel">' +
        this.escape(settings.cancelText) +
        "</button>" +
        "</div>";

      var popup = this.create({
        title: settings.title,
        content: content,
        type: "confirm",
        size: "small"
      });

      popup.addEventListener("click", function (event) {
        var action = event.target.getAttribute("data-rx-confirm-action");

        if (action === "confirm") {
          if (typeof settings.onConfirm === "function") {
            settings.onConfirm();
          }
          window.RXPopup.close(popup, {
            source: "confirm"
          });
        }

        if (action === "cancel") {
          if (typeof settings.onCancel === "function") {
            settings.onCancel();
          }
          window.RXPopup.close(popup, {
            source: "cancel"
          });
        }
      });

      window.RXPopup.open(popup, {
        source: "builder-confirm"
      });

      return popup;
    },

    escape: function (value) {
      var div = document.createElement("div");
      div.textContent = value;
      return div.innerHTML;
    },

    extend: function () {
      var output = arguments[0] || {};

      for (var i = 1; i < arguments.length; i++) {
        if (!arguments[i]) {
          continue;
        }

        for (var key in arguments[i]) {
          if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
            output[key] = arguments[i][key];
          }
        }
      }

      return output;
    }
  };

  /**
   * Newsletter form helper
   */
  var RXPopupNewsletter = {
    init: function () {
      document.addEventListener("submit", function (event) {
        var form = event.target.closest("[data-rx-popup-newsletter]");

        if (!form) {
          return;
        }

        RXPopupNewsletter.handleSubmit(event, form);
      });
    },

    handleSubmit: function (event, form) {
      var ajax = form.getAttribute("data-rx-popup-newsletter-ajax");

      if (ajax !== "true") {
        return;
      }

      event.preventDefault();

      var action = form.getAttribute("action");
      var method = form.getAttribute("method") || "POST";
      var messageBox = form.querySelector("[data-rx-popup-message]");
      var submitButton = form.querySelector('[type="submit"]');

      if (!action) {
        this.showMessage(messageBox, "Form action is missing.", "error");
        return;
      }

      var formData = new FormData(form);

      if (submitButton) {
        submitButton.disabled = true;
        submitButton.classList.add("is-loading");
      }

      fetch(action, {
        method: method,
        body: formData,
        credentials: "same-origin"
      })
        .then(function (response) {
          if (!response.ok) {
            throw new Error("Network response was not ok");
          }

          return response.text();
        })
        .then(function () {
          RXPopupNewsletter.showMessage(
            messageBox,
            "Thank you. Your request has been submitted.",
            "success"
          );

          form.reset();

          document.dispatchEvent(
            new CustomEvent("rxPopupNewsletterSuccess", {
              detail: {
                form: form
              }
            })
          );
        })
        .catch(function () {
          RXPopupNewsletter.showMessage(
            messageBox,
            "Something went wrong. Please try again.",
            "error"
          );

          document.dispatchEvent(
            new CustomEvent("rxPopupNewsletterError", {
              detail: {
                form: form
              }
            })
          );
        })
        .finally(function () {
          if (submitButton) {
            submitButton.disabled = false;
            submitButton.classList.remove("is-loading");
          }
        });
    },

    showMessage: function (box, message, type) {
      if (!box) {
        return;
      }

      box.textContent = message;
      box.className = "rx-popup-message rx-popup-message--" + type;
      box.setAttribute("role", type === "error" ? "alert" : "status");
    }
  };

  /**
   * Auto initialize
   */
  function bootRXPopup() {
    window.RXPopup = RXPopup;
    window.RXPopupBuilder = RXPopupBuilder;

    RXPopup.init(window.rxPopupSettings || {});
    RXPopupNewsletter.init();
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", bootRXPopup);
  } else {
    bootRXPopup();
  }
})();

Example HTML popup

<div
  id="rx-newsletter-popup"
  class="rx-popup"
  data-rx-popup
  data-rx-popup-delay="4000"
  data-rx-popup-once="true"
  data-rx-popup-frequency="168"
>
  <button type="button" class="rx-popup__close" data-rx-popup-close aria-label="Close popup">
    ×
  </button>

  <div class="rx-popup__inner">
    <h2 class="rx-popup__title">Join RX Health Updates</h2>
    <p class="rx-popup__text">
      Get simple medical education, health articles, and RX Theme updates.
    </p>

    <form data-rx-popup-newsletter data-rx-popup-newsletter-ajax="false">
      <input type="email" name="email" placeholder="Your email address" required>
      <button type="submit">Subscribe</button>
      <div data-rx-popup-message></div>
    </form>
  </div>
</div>

Example trigger button

<button type="button" data-rx-popup-trigger="rx-newsletter-popup">
  Open Popup
</button>

Example CSS starter

.rx-popup-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0.25s ease;
}

.rx-popup-overlay.is-active,
.rx-popup-overlay.is-visible {
  opacity: 1;
  visibility: visible;
}

.rx-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 9999;
  width: min(92vw, 560px);
  max-height: 90vh;
  overflow: auto;
  background: #fff;
  color: #111;
  border-radius: 16px;
  box-shadow: 0 20px 80px rgba(0, 0, 0, 0.25);
  transform: translate(-50%, -48%) scale(0.96);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s ease;
}

.rx-popup.is-active,
.rx-popup.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1);
}

.rx-popup__inner {
  padding: 32px;
}

.rx-popup__close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
}

.rx-popup-open {
  overflow: hidden;
}

Enqueue in functions.php

function rx_theme_enqueue_popup_chunk() {
    wp_enqueue_script(
        'rx-popup-chunk',
        get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-026-popup.js',
        array(),
        '1.0.0',
        true
    );
}
add_action( 'wp_enqueue_scripts', 'rx_theme_enqueue_popup_chunk' );

This file is good for the advanced stage of RX Theme because it is modular, reusable, accessibility-aware, and can work with many future popup types without rewriting the whole system.

Leave a Reply

Your email address will not be published. Required fields are marked *