PAYATHON 2026

如何为第三方支付弹窗的 .ls-checkout 添加样式?

支付小周

结论

如果 .ls-checkout 位于 Lemon Squeezy 创建的跨域 iframe 中,当前网站就无法通过 jQuery、MutationObserver、addClass() 或父页面 CSS 修改它。原因不在监听时机,而是浏览器的同源策略。

通常可以自定义的内容包括:

  • 当前页面中的 checkout 按钮;
  • 第三方脚本插入当前文档的遮罩层、容器或 iframe 元素;
  • Lemon Squeezy 官方配置或后台支持调整的结账页外观。

至于 iframe 内的按钮、表单、字体和间距,只能通过支付服务商提供的主题配置或 API 修改。

为什么现有监听没有生效

第三方 checkout 通常会按下面的结构加载:

<body>
  <!-- 当前网站的 DOM -->
  <div class="checkout-overlay">
    <iframe src="https://checkout.example.com/..."></iframe>
  </div>
</body>

iframe 内是另一份独立文档:

<!-- 第三方域名中的文档 -->
<div class="ls-checkout">
  ...
</div>

父页面的选择器只能查询当前文档:

document.querySelector('.ls-checkout');

即使屏幕上已经显示了 .ls-checkout,只要它位于跨域 iframe 中,这段查询仍会返回 null。MutationObserver 同样只能观察当前文档的 DOM 变化,不能穿透 iframe 监听内部节点。

父页面中的这段 CSS 也不会作用于 iframe 内部:

.ls-checkout {
  background: red;
}

直接读取 iframe 文档则会受到安全限制:

const iframe = document.querySelector('iframe');

iframe.contentDocument.querySelector('.ls-checkout');
// 跨域 iframe 通常会返回 null 或抛出 SecurityError

只有当父页面与 iframe 页面同源,或者第三方页面主动提供受支持的通信和定制机制时,才能操作其中的内容。

可以修改外层容器或 iframe

如果只是想调整弹窗的尺寸、位置、层级或外部留白,可以监听第三方脚本插入当前文档的元素,再修改这个元素。

const observer = new MutationObserver(() => {
  const iframe = document.querySelector(
    'iframe[src*="lemonsqueezy"], iframe[src*="checkout"]'
  );

  if (!iframe || iframe.dataset.customized === 'true') {
    return;
  }

  iframe.dataset.customized = 'true';
  iframe.classList.add('custom-checkout-frame');

  // 已找到目标时,如果后续不需要继续监听,可以停止观察。
  observer.disconnect();
});

observer.observe(document.body, {
  childList: true,
  subtree: true
});

对应的样式如下:

.custom-checkout-frame {
  width: min(100%, 960px) !important;
  border: 0;
  border-radius: 16px;
}

这段样式只能改变 iframe 这个矩形元素,无法修改其中的 .ls-checkout。

如果第三方脚本还插入了父页面可以访问的外层节点,也可以直接监听该容器:

function customizeCheckoutContainer() {
  const container = document.querySelector('.checkout-overlay');

  if (!container || container.classList.contains('checkout-overlay--custom')) {
    return false;
  }

  container.classList.add('checkout-overlay--custom');
  return true;
}

if (!customizeCheckoutContainer()) {
  const observer = new MutationObserver(() => {
    if (customizeCheckoutContainer()) {
      observer.disconnect();
    }
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
}

具体类名要以浏览器开发者工具中父页面实际出现的元素为准,不能认定第三方脚本一定会使用 .checkout-overlay。

如果 .ls-checkout 确实属于当前文档

弹窗打开后,可以先在浏览器控制台中运行:

document.querySelector('.ls-checkout');

如果返回的是元素而非 null,说明它不在跨域 iframe 中。这时可以使用 MutationObserver:

function applyCheckoutStyle(root = document) {
  const elements = root.querySelectorAll('.ls-checkout:not(.checkout-customized)');

  for (const element of elements) {
    element.classList.add('checkout-customized');
  }

  return elements.length > 0;
}

applyCheckoutStyle();

const observer = new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    for (const node of mutation.addedNodes) {
      if (!(node instanceof Element)) {
        continue;
      }

      if (node.matches('.ls-checkout')) {
        node.classList.add('checkout-customized');
      }

      applyCheckoutStyle(node);
    }
  }
});

observer.observe(document.body, {
  childList: true,
  subtree: true
});
.ls-checkout.checkout-customized {
  border-radius: 16px;
  box-shadow: 0 20px 60px rgb(0 0 0 / 25%);
}

事件委托形式的 .on() 不能用于监听元素创建:

$(document).on('click', '.ls-checkout', handler);

它监听的是 .ls-checkout 上发生的 click 事件,不会在元素插入 DOM 时执行。要监听节点创建,应使用 MutationObserver。

同源 iframe 的特殊情况

如果 iframe 与父页面同源,可以等待它加载完成,再访问其中的文档:

const iframe = document.querySelector('iframe');

iframe.addEventListener('load', () => {
  const checkout = iframe.contentDocument?.querySelector('.ls-checkout');
  checkout?.classList.add('checkout-customized');
});

不过,支付页面通常来自支付服务商的域名,一般不满足同源条件。设置 document.domain、修改 CORS 响应头或使用 jQuery 都无法绕过这项限制。CORS 也不会赋予父页面操作跨域 iframe DOM 的权限。

建议的处理顺序

  1. 打开浏览器开发者工具,确认 .ls-checkout 位于父页面还是 iframe 中。
  2. 如果它在父页面中,使用 MutationObserver 监听 document.body,并启用 subtree: true。
  3. 如果它在跨域 iframe 中,不再尝试直接操作内部 DOM。
  4. 检查 Lemon Squeezy 提供的 checkout 外观、品牌或主题配置。
  5. 如果官方没有开放所需样式,只调整父页面中的遮罩层和 iframe 外框,同时避免依赖第三方未公开的内部类名。

处理这类问题时,首先要确认 .ls-checkout 属于哪一份文档,而不是纠结使用 jQuery 还是原生 JavaScript。宿主页面无法覆盖跨域 iframe 内的样式。

备注:内容仅供参考。