You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React.js中iframe内嵌HTML脚本失效及跳转回调监听实现

React中iframe加载HTML字符串并执行脚本、监听跳转的实现方案

问题场景

对接支付系统时,调用API返回的htmlContent内容需要渲染到iframe中,实际运行时iframe内部脚本无法执行,无法触发自动表单提交、跳转逻辑,也无法捕获支付成功的回调地址。
原有实现代码如下:

自定义IFrame组件

const IFrame = ({ children, ...props }: any) => {
  const [contentRef, setContentRef] = useState<any>(null);
  const mountNode = contentRef?.contentWindow?.document?.body;

  return (
    // eslint-disable-next-line jsx-a11y/iframe-has-title
    <iframe
      {...props}
      ref={setContentRef}
      style={{ border: 0 }}
      onLoad={(e: any) => {
        console.log('page loaded', e.target);
      }}
    >
      {mountNode && createPortal(children, mountNode)}
    </iframe>
  );
};

页面调用逻辑

const html = "<html>Hello</html><script>alert('world');</script>";
return (
        <IFrame>
          <div dangerouslySetInnerHTML={{ __html: html }} />
        </IFrame>
       );

运行上述代码时,iframe内没有触发预期的alert弹窗,无法满足支付页自动跳转、监听回调地址的需求。

问题根因

脚本无法执行的核心原因有两点:

  • 浏览器内置XSS防护规则限制:通过innerHTML(即React的dangerouslySetInnerHTML)插入的<script>标签默认不会被解析执行,该规则对iframe内外的DOM插入都生效
  • 实现逻辑缺陷:通过createPortal将内容挂载到iframe已有文档的body节点,没有完整写入HTML文档上下文,支付系统返回的自动提交表单、跳转类脚本依赖完整文档环境,无法正常触发

可行实现方案

优先使用iframe原生srcdoc属性加载完整HTML字符串,该方式会让浏览器正常解析并执行文档内的所有脚本,兼容性覆盖所有现代浏览器。同时通过iframe的load事件监听页面跳转,捕获回调地址。

可直接复用的支付IFrame组件

import { useRef, useState, useEffect } from 'react';

const PaymentIFrame = ({ htmlContent, onUrlChange, ...props }) => {
  const iframeRef = useRef(null);
  const [currentUrl, setCurrentUrl] = useState('');

  // 兼容不支持srcdoc的老旧浏览器,可按需启用
  // useEffect(() => {
  //   const iframe = iframeRef.current;
  //   if (!iframe || !htmlContent) return;
  //   const doc = iframe.contentDocument || iframe.contentWindow.document;
  //   doc.open();
  //   doc.write(htmlContent);
  //   doc.close();
  // }, [htmlContent]);

  const handleIframeLoad = () => {
    try {
      // 同域场景下可直接读取iframe当前地址
      const newUrl = iframeRef.current.contentWindow.location.href;
      if (newUrl !== currentUrl) {
        setCurrentUrl(newUrl);
        onUrlChange?.(newUrl);
        // 匹配到支付成功回调路径时执行业务逻辑
        if (newUrl.includes('/pay/callback/success')) {
          console.log('捕获到支付成功跳转', newUrl);
        }
      }
    } catch (err) {
      // 跨域场景下无法直接读取location,可通过postMessage接收支付方推送的结果
    }
  };

  return (
    <iframe
      {...props}
      ref={iframeRef}
      srcDoc={htmlContent}
      onLoad={handleIframeLoad}
      style={{ border: 0, width: '100%', height: '100%' }}
      title="支付页面"
    />
  );
};

组件调用方式

// 直接传入支付接口返回的完整HTML字符串即可,无需额外处理
const payHtml = `<html>
<body>
  支付跳转中...
  <script>
    alert('world');
    // 此处为支付系统自带的自动提交表单、跳转逻辑,会正常执行
  </script>
</body>
</html>`;

return (
  <PaymentIFrame
    htmlContent={payHtml}
    onUrlChange={(url) => console.log('检测到iframe地址变化:', url)}
  />
);

注意事项

  • 不要使用dangerouslySetInnerHTML向iframe内插入脚本内容,浏览器默认不会执行这类脚本,必须通过srcdoc或者document.write的方式写入完整HTML文档
  • 跨域场景下无法直接读取iframe的location.href,两种兜底方案:
    • 配置支付系统的跳转回调地址为自身站点的同域地址,跳转回同域后即可正常读取URL判断支付结果
    • 若支付渠道支持postMessage通信,可监听window的message事件接收支付结果通知
  • 传入的HTML内容必须完全来自可信的支付接口返回,不要拼接用户可控参数,避免XSS风险

内容的提问来源于stack exchange,提问作者Ahmad Echem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 21:18:56