我用它来预览电子邮件
当我动态地将 HTML 写入 iframe 时,不知何故省略了<body>
标记?body 标签包含font-family
等,但现在整个标签都消失了,HTML 文档无法正确显示
const iframe_content = $('#'+iframe_id).contents().find('html');
iframe_content.html(data.html);
data.html
的内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>@media (prefers-color-scheme: dark){
.body_inner{background:#000 !important}
.content{border:0}
.content_inner{background:#000 !important;color:#fff !important}
}</style>
</head>
<body style="margin:0;padding:0;font-family:arial,helvetica,garuda">
<div>first element</div>
</body>
</html>
将 HTML 写入 iframe 后,iframe 的源代码如下所示(body 标记不见了!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>@media (prefers-color-scheme: dark){
.body_inner{background:#000 !important}
.content{border:0}
.content_inner{background:#000 !important;color:#fff !important}
}</style>
</head>
<div>first element</div>
</html>
我尝试通过 https://validator.w3.org/验证 HTML,没有错误
原因是 jQuery 的.html(value)
方法不用于设置带有(顶级)<html>
标签的内容。我们可以在jQuery源代码中看到这一点。当调用iframe_content.html(data.html);
时,在jQuery库内进行以下调用:
html()
调用access()
,它调用(通过回调)append()
,它调用domManip()
,它调用buildFragment()
,执行以下代码,其中elem
是作为参数传递的data.html
,fragment
是 documentFragment:
tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
// Deserialize a standard representation
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
所以tmp
是一个<div>
元素(因为appendChild
返回参数)。tag
设置为 "html",wrap
设置为[0, "", ""]
,jQuery.htmlPrefilter(elem)
只返回elem
。简而言之,这是执行的:
tmp.innerHTML = elem;
问题是HTML不允许<div>
元素具有<html>
或<head>
或<body>
元素作为子元素,因此DOM(不是jQuery)不会按预期创建这些DOM元素。
这不是jQuery处理的结束,但是在这一点上我们已经可以看到信息已经丢失。
解决方法
由于 jQuery 的.html(value)
方法无法完成这项工作,请绕过 jQuery 并直接设置innerHTML
。替换此内容:
iframe_content.html(data.html);
有了这个:
iframe_content.get(0).innerHTML = data.html;
现在它将起作用。
你试过使用 Shadow DOM 吗?
所有样式都像 iframe 一样封装,但您还可以获得自然扩展内容(与 iframe 的固定高度相比)和更快的渲染时间的额外好处。
用法示例:
const shadow = someParentElement.attachShadow({
mode: 'closed'
});
shadow.innerHTML = `<whatever>`;