动态创建具有给定HTML的iframe


148

我正在尝试从JavaScript创建iframe,并用任意HTML填充它,如下所示:

var html = '<body>Foo</body>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

我希望iframe然后包含一个有效的窗口和文档。但是,事实并非如此:

> console.log(iframe.contentWindow);
空值

自己尝试一下:http : //jsfiddle.net/TrevorBurnham/9k9Pe/

我在俯视什么?


9
请注意,HTML5引入了一个自动执行此操作的新参数:w3schools.com/tags/att_iframe_srcdoc.asp 唯一的问题是浏览器兼容性...
Vincent Audebert 2013年

Answers:


121

设置src新创建的iframe在将元素插入文档中之前, javascript中不会触发HTML解析器。然后更新HTML,将调用HTML解析器并按预期处理属性。

http://jsfiddle.net/9k9Pe/2/

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);
document.body.appendChild(iframe);
console.log('iframe.contentWindow =', iframe.contentWindow);

同样,这个问题也回答了您,请务必注意,这种方法与某些浏览器存在兼容性问题,请参阅@mschr的跨浏览器解决方案。


3
甚至在IE10中也不起作用。当然,@ mschr的答案可以在IE7 +中运行,甚至可以使用旧版本。
James M. Greene

6
他的问题是“我忽略了什么?” 这就是iframe没有附加到文档中的事实。我从来没有声称这是跨浏览器,而且直到我回答有人真正抱怨才一年多。不,它不是跨浏览器。但是说实话,如果您想提高代码质量,那么比起先使用iframe来说,解决方案可能更干净:)
GillesC 2013年

1
请注意,encodeURI(...)它不会对所有字符进行编码,因此,如果您的HTML包含特殊字符,例如#,则会中断。encodeURIComponent(...)编码这些字符。看到这个答案
Ryan Morlok

237

尽管您src = encodeURI应该工作,但我会采取另一种方式:

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();

由于这没有x域限制,并且完全是通过iframe手柄完成的,因此您稍后可以访问和操作框架的内容。您需要确定的是,内容已呈现,这将在发出.write命令期间/之后(取决于浏览器类型)开始- close()调用时不必完成。

100%兼容的回调方法可以是这种方法:

<html><body onload="parent.myCallbackFunc(this.window)"></body></html>

但是,iframe具有onload事件。这是一种以DOM(js)形式访问内部html的方法:

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};

有趣; 我以前没看过这种技术。我知道URI编码/解码会增加性能,但是我也看到它被描述为不支持该srcdoc属性的环境中的唯一替代方法。这种document.write方法有不利之处吗?
特雷弗·伯纳姆

1
@mschr此方法是否支持完整的HTML页面代码,其中还包含了include和样式表?参见stackoverflow.com/questions/19871886
1.21吉瓦(

2
基本上,是的,我认为应该,对此有不良评论。该技术基本上会打开一个inputtextstream,您可以通过document.write对其进行写入。反过来,正常的加载网页会通过websocket流对其进行检索。
mschr

2
这在Internet Explorer中有效!这很方便,因为在任何版本的IE中,数据URI都不能用作iframe源。 caniuse.com/#feat=datauri
Jesse Hallett

2
有趣的document.body.appendChild(iframe)是,此功能是必需的。
保罗

14

感谢您提出的重大问题,这使我不知所措。使用dataURI HTML源时,我发现必须定义一个完整的HTML文档。

参见下面的修改示例。

var html = '<html><head></head><body>Foo</body></html>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

注意用<html>标签和iframe.src字符串包装的html内容。

需要将iframe元素添加到要解析的DOM树中。

document.body.appendChild(iframe);

iframe.contentDocument除非您disable-web-security在浏览器上,否则您将无法检查。您会收到一条消息

DOMException:无法从“ HTMLIFrameElement”读取“ contentDocument”属性:阻止了起源为“ http:// localhost:7357 ”的框架访问跨域框架。


12

还有一种用于创建内容为HTML字符串的iframe的替代方法:srcdoc属性。较旧的浏览器不支持此功能(其中包括:Internet Explorer,可能还包括Safari?),但此行为有一个polyfill,您可以在IE中添加条件注释,或使用has.js之类的条件来懒惰加载它。


2
现在对此的支持非常主流(保存IE)。而且绝对比直接访问contentDocument更可取-特别是因为如果与sandbox属性结合使用,则无法访问contentDocument。
CambridgeMike

1

我知道这是一个古老的问题,但我想我将提供一个使用srcdoc属性的示例,因为该属性现已得到广泛支持,并且该问题经常被人们关注。

使用该srcdoc属性,可以提供要嵌入的内联HTML。src如果支持,它将覆盖属性。src如果不支持,浏览器将退回到该属性。

我还建议使用该sandbox属性对框架中的内容施加额外的限制。如果HTML不是您自己的,这尤其重要。

const iframe = document.createElement('iframe');
const html = '<body>Foo</body>';
iframe.srcdoc = html;
iframe.sandbox = '';
document.body.appendChild(iframe);

如果需要支持较旧的浏览器,则可以srcdoc从其他答案中查看是否支持并回退到其他方法之一。

function setIframeHTML(iframe, html) {
  if (typeof iframe.srcdoc !== 'undefined') {
    iframe.srcdoc = html;
  } else {
    iframe.sandbox = 'allow-same-origin';
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write(html);
    iframe.contentWindow.document.close();
  }
}

var iframe = document.createElement('iframe');
iframe.sandbox = '';
var html = '<body>Foo</body>';

document.body.appendChild(iframe);
setIframeHTML(iframe, html);


1

URL方法仅适用于小型HTML片段。更可靠的方法是从Blob生成对象URL,并将其用作动态iframe的源。

const html = '<html>...</html>';
const iframe = document.createElement('iframe');
const blob = new Blob([html], {type: 'text/html'});
iframe.src = window.URL.createObjectURL(blob);
document.body.appendChild(iframe);

0

做这个

...
var el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow在这里定义

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.