如何将 HTML、CSS 和 JS 代码加载到 iFrame 中?

2025-06-10

如何将 HTML、CSS 和 JS 代码加载到 iFrame 中

寓意?

如果您来这里只是为了寻找答案,而不是为了听故事,那么解决方案就在底部

如果您曾经使用过 JSFiddle、Codepen 或其他程序,那么您会对这个问题很熟悉:目标是获取一些 HTML、CSS 和 JS(存储为字符串)并创建一个加载了代码的 iframe。

这个问题应该很简单,但其实不然。至少……直到我找到我一直在等待的那张金票之前,它一直都很简单。

稍后再详细讨论。我们先从那些失败的地方说起,这样更有趣。

尝试 #1:使用 srcdoc

经过一番研究后,我很高兴地发现可以srcdoc向 iframe 添加属性。

如果您传入 HTML 字符串,iframe 将加载其中的 HTML 内容:



<iframe srcdoc="<p>This text will appear in the iframe!</p>"></iframe>


Enter fullscreen mode Exit fullscreen mode

不幸的是,这种方法有两个主要问题:

1. 浏览器对 srcdoc 的支持不够好


如果我们想要支持 IE 或 Edge,我们需要一种不同的方法(或 polyfill)。

2. 可以“逃离” CSS/JS

以下是我使用 srcdoc 实现的大致情况:



function setIframeContent(iframe, { html, css, js }) {
  const source = `
    <html>
      <head><style>${css}</style></head>
      <body>
        ${html}
        <script>${js}</script>
      </body>
    </html>
  `
  iframe.srcdoc = source
}


Enter fullscreen mode Exit fullscreen mode

问题是什么?编写 CSS 或 JS 时,只需在代码中包含</style>或,就有可能“跳出”到 HTML 领域。</script>

这个错误实际上很常见;JSFiddle 和 Codepen 都受到影响:

尝试 2:无服务器回旋镖

为了解决浏览器支持问题,我们将其替换srcdoc为常规src属性。为此,我们需要传递一个真实的 URL,而不仅仅是代码。

也许我们可以建立一个页面,它接受 HTML、CSS 和 JS“GET”参数并输出与以前相同类型的页面,但这次是从实际的 URL 加载的。

现在是使用无服务器架构的绝佳时机,因为我们只需要一个端点来做一件事。以下是我的尝试:



module.exports = (req, res) => {
  // Code comes from GET params in URL
  const { html = '', css = '', js = '' } = req.query

  // Generate and send HTML page
  return res.send(`
    <html>
      <head><style>${css}</style></head>
      <body>
        ${html}
        <script>${js}</script>
      </body>
    </html>
  `)
}



Enter fullscreen mode Exit fullscreen mode

这几乎适用于所有浏览器,但也存在自身的问题:

  1. 从 CSS/JS 转义到 HTML 仍然是一个问题
  2. 整个源代码都通过 URL 传递,这并不理想

尝试 #3:无服务器 Boomerang(redux)

我们的第一个回旋镖解决了浏览器支持问题,但仍然有“转义”问题需要处理。

幸运的是,由于我们传入代码的方式,这个问题实际上可以解决。我们不用在服务器上将 CSS 和 JS 插入到页面中,而是可以在客户端执行!这是因为客户端计算机仍然可以访问 URL GET 参数。

这里的源代码有点长,但确实有效:



module.exports = (req, res) => {
  return res.send(`
    <html>
      <head>
        <script type="text/javascript">
          window.addEventListener('load', function() {
            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, ' '));
            };

            // Load JS from GET params (on client)
            var js = getUrlParameter('js');
            if (js) {
              var script = document.createElement('script');
              script.type = 'text/javascript';
              script.text = js;
              document.body.appendChild(script);
            }

            // Load CSS from GET params (on client)
            var css = getUrlParameter('css');
            if (css) {
              var style = document.createElement('style');
              style.type = 'text/css';
              if (style.styleSheet) {
                style.styleSheet.cssText = css;
              } else {
                style.appendChild(document.createTextNode(css));
              }
              document.head.appendChild(style);
            }

            // Remove the currently running script tag
            document.currentScript.parentNode.removeChild(document.currentScript);
          });
        </script>
      </head>
      <body>
        ${req.query.html || ''}
      </body>
    </html>
  `)
}



Enter fullscreen mode Exit fullscreen mode

现在,如果脚本或样式包含可怕的 HTML 字符,则浏览器在将该脚本/样式插入文档时会为我们处理这些字符。

这个解决方案……还不错。从技术上来说,它确实有效。但我们仍然需要考虑 URL 的软长度限制。另外,我们现在处理的是服务器端的事情,但感觉应该在客户端处理。

一定有更好的方法。

解决方案:Blob URL

在整个过程中,我们一直在尝试模拟从 URL 加载数据:

  • 首先,我们使用 srcdoc 加载数据,而不是从 URL 加载
  • 然后我们使用回旋镖从 URL 加载代码
  • 接下来,我们更新了回旋镖,尝试模拟“从外部 URL 加载 CSS/JS”行为,尽管每个资源都来自一个 URL。

事实证明,Javascript 有一个功能可以做到这一点:Blob URL

斑点

我们可以使用Blob构造函数创建一个伪文件。它不是从磁盘或 URL 加载的真实文件——它只是存储在内存中。但在很多方面,它的功能与真实加载的文件一样。

然后,我们可以使用它URL.createObjectURL(blob)来创建一个可用于加载 blob 内容的 URL。

实际情况如下:



const getBlobURL = (code, type) => {
  const blob = new Blob([code], { type })
  return URL.createObjectURL(blob)
}

console.log(getBlobURL('<p>My webpage</p>', 'text/html'))
// blob:https://dev.to/9ca05e31-05ea-48f8-838d-cc1ad0949ec8


Enter fullscreen mode Exit fullscreen mode

试着在控制台中运行上面的代码,亲眼看看效果!它会记录一个 URL。如果你把这个 URL 粘贴到新标签页(包括blob:开头的部分),它就会加载一个包含 HTML 的页面。

注意到'text/html'传递给 了getBlobURL吗?我们也可以修改它。生成 CSS 或 JS blob 很简单:只需分别传递text/csstext/javascript即可。

Blob URL 的另一个好处是它们会一直存在,并且可以像访问常规 URL 一样以任何方式访问。这意味着我们实际上可以从单独的 URL 加载 CSS 和 JS 文件,因此“转义”技巧不再是问题。

以下是实际操作中最简单的实现:



const getGeneratedPageURL = ({ html, css, js }) => {
  const getBlobURL = (code, type) => {
    const blob = new Blob([code], { type })
    return URL.createObjectURL(blob)
  }

  const cssURL = getBlobURL(css, 'text/css')
  const jsURL = getBlobURL(js, 'text/javascript')

  const source = `
    <html>
      <head>
        ${css && `<link rel="stylesheet" type="text/css" href="${cssURL}" />`}
        ${js && `<script src="${jsURL}"></script>`}
      </head>
      <body>
        ${html || ''}
      </body>
    </html>
  `

  return getBlobURL(source, 'text/html')
}

const url = getGeneratedPageURL({
  html: '<p>Hello, world!</p>',
  css: 'p { color: blue; }',
  js: 'console.log("hi")'
})

const iframe = document.querySelector('#iframe')
iframe.src = url


Enter fullscreen mode Exit fullscreen mode

哦,浏览器对 Blob URL 的支持比 srcdoc 好得多。;)

寓意?

我想,不要与语言作斗争。

我知道我想做什么:从 URL 加载数据。只是我从来没想过要找一个不靠谱的方法来做这件事!

鏂囩珷鏉ユ簮锛�https://dev.to/pulljosh/how-to-load-html-css-and-js-code-into-an-iframe-2blc
PREV
只在工作时编写代码的程序员
NEXT
开源 Bhagavad Gita API v3.1 Bhagavad Gita API