Creating an iframe with given HTML dynamically

2019-01-02 22:13发布

I'm trying to create an iframe from JavaScript and fill it with arbitrary HTML, like so:

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

I would expect iframe to then contain a valid window and document. However, this isn't the case:

> console.log(iframe.contentWindow);
null

Try it for yourself: http://jsfiddle.net/TrevorBurnham/9k9Pe/

What am I overlooking?

5条回答
成全新的幸福
2楼-- · 2019-01-02 22:28

Thanks for your great question, this has caught me out a few times. When using dataURI HTML source, I find that I have to define a complete HTML document.

See below a modified example.

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

take note of the html content wrapped with <html> tags and the iframe.src string.

The iframe element needs to be added to the DOM tree to be parsed.

document.body.appendChild(iframe);

You will not be able to inspect the iframe.contentDocument unless you disable-web-security on your browser. You'll get a message

DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://localhost:7357" from accessing a cross-origin frame.

查看更多
时光不老,我们不散
3楼-- · 2019-01-02 22:30

There is an alternative for creating an iframe whose contents are a string of HTML: the srcdoc attribute. This is not supported in older browsers (chief among them: Internet Explorer, and possibly Safari?), but there is a polyfill for this behavior, which you could put in conditional comments for IE, or use something like has.js to conditionally lazy load it.

查看更多
The star\"
4楼-- · 2019-01-02 22:31

Do this

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

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow is defined here

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;
}
查看更多
Fickle 薄情
5楼-- · 2019-01-02 22:35

Setting the src of a newly created iframe in javascript does not trigger the HTML parser until the element is inserted into the document. The HTML is then updated and the HTML parser will be invoked and process the attribute as expected.

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);

Also this answer your question it's important to note that this approach has compatibility issues with some browsers, please see the answer of @mschr for a cross-browser solution.

查看更多
我命由我不由天
6楼-- · 2019-01-02 22:49

Allthough your src = encodeURI should work, I would have gone a different way:

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();

As this has no x-domain restraints and is completely done via the iframe handle, you may access and manipulate the contents of the frame later on. All you need to make sure of is, that the contents have been rendered, which will (depending on browser type) start during/after the .write command is issued - but not nescessarily done when close() is called.

A 100% compatible way of doing a callback could be this approach:

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

Iframes has the onload event, however. Here is an approach to access the inner html as DOM (js):

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};
查看更多
登录 后发表回答