如何将 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>
不幸的是,这种方法有两个主要问题:
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
}
问题是什么?编写 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>
`)
}
这几乎适用于所有浏览器,但也存在自身的问题:
- 从 CSS/JS 转义到 HTML 仍然是一个问题
- 整个源代码都通过 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>
`)
}
现在,如果脚本或样式包含可怕的 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
试着在控制台中运行上面的代码,亲眼看看效果!它会记录一个 URL。如果你把这个 URL 粘贴到新标签页(包括blob:
开头的部分),它就会加载一个包含 HTML 的页面。
注意到'text/html'
传递给 了getBlobURL
吗?我们也可以修改它。生成 CSS 或 JS blob 很简单:只需分别传递text/css
或text/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
哦,浏览器对 Blob URL 的支持比 srcdoc 好得多。;)
寓意?
我想,不要与语言作斗争。
我知道我想做什么:从 URL 加载数据。只是我从来没想过要找一个不靠谱的方法来做这件事!
鏂囩珷鏉ユ簮锛�https://dev.to/pulljosh/how-to-load-html-css-and-js-code-into-an-iframe-2blc