英文:
Dynamically write contents to iframe
问题
我使用这段代码来预览一封电子邮件。当我动态将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,未发现错误。
英文:
I use this to preview an e-mail
When I dynamically write HTML to an iframe somehow the <body>
tag is omitted? The body tag holds the font-family
etc. but now the whole tag is gone and the HTML document is not shown correctly
const iframe_content = $('#'+iframe_id).contents().find('html');
iframe_content.html(data.html);
contents of 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>
After the HTML is written to the iframe, the source of the iframe looks like this (the body tag is gone!)
<!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>
I have tried to validate the HTML via https://validator.w3.org/ and no errors
答案1
得分: 8
"原因是jQuery的.html(value)
方法不是用于设置带有顶层<html>
标记的内容的。我们可以在jQuery源代码中看到这一点。在调用iframe_content.html(data.html);
时,jQuery库内部将进行以下调用:html()
调用access()
,access()
通过回调调用append()
,append()
调用domManip()
,domManip()
调用buildFragment()
,buildFragment()
执行以下代码,其中elem
是你传递的data.html
,而fragment
是一个documentFragment:
tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
// 反序列化标准表示
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;
现在它将工作。"
英文:
The reason is that jQuery's .html(value)
method is not intended to be used for setting content with a (top-level) <html>
tag. We can see this in the jQuery source code. When calling iframe_content.html(data.html);
, the following calls are made inside the jQuery library:
html()
calls access()
, which calls (via callback) append()
, which calls domManip()
, which calls buildFragment()
, which executes the following code, where elem
is the data.html
you passed as argument, and fragment
is a 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 ];
So tmp
is a <div>
element (as appendChild
returns the argument). tag
is set to "html", and wrap
to [0, "", ""]
, and jQuery.htmlPrefilter(elem)
just returns elem
. So in short, this is executed:
tmp.innerHTML = elem;
The problem is HTML does not allow a <div>
element to have an <html>
or <head>
or <body>
element as child, so the DOM (not jQuery) will not create those DOM elements as intended.
This is not the end of the jQuery processing, but already at this point we can see that information has been lost.
Work around
As jQuery's .html(value)
method cannot do the job, bypass jQuery and set innerHTML
directly. Replace this:
iframe_content.html(data.html);
with this:
iframe_content.get(0).innerHTML = data.html;
And now it will work.
答案2
得分: 0
你尝试过使用Shadow DOM吗?
所有样式都被封装,就像iframe一样,但你还可以获得自然扩展内容(与iframe的固定高度相比)和更快的渲染时间。
示例用法:
const shadow = someParentElement.attachShadow({
mode: 'closed'
});
shadow.innerHTML = '<whatever>';
英文:
Have you tried using a Shadow DOM?
All styles are encapsulated just like an iframe, but you also get the added benefits of naturally expanding content (compared to the fixed height of an iframe) and faster render times.
Example usage:
const shadow = someParentElement.attachShadow({
mode: 'closed'
});
shadow.innerHTML = `<whatever>`;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论