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?
This question is related to
javascript
iframe
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');
};
The URL approach will only work for small HTML fragements. The more solid approach is to generate an object URL from a blob and use it as a source of the dynamic 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);
I know this is an old question but I thought I would provide an example using the srcdoc
attribute as this is now widely supported and this is question is viewed often.
Using the srcdoc
attribute, you can provide inline HTML to embed. It overrides the src
attribute if supported. The browser will fall back to the src
attribute if unsupported.
I would also recommend using the sandbox
attribute to apply extra restrictions to the content in the frame. This is especially important if the HTML is not your own.
const iframe = document.createElement('iframe');_x000D_
const html = '<body>Foo</body>';_x000D_
iframe.srcdoc = html;_x000D_
iframe.sandbox = '';_x000D_
document.body.appendChild(iframe);
_x000D_
If you need to support older browsers, you can check for srcdoc
support and fallback to one of the other methods from other answers.
function setIframeHTML(iframe, html) {_x000D_
if (typeof iframe.srcdoc !== 'undefined') {_x000D_
iframe.srcdoc = html;_x000D_
} else {_x000D_
iframe.sandbox = 'allow-same-origin';_x000D_
iframe.contentWindow.document.open();_x000D_
iframe.contentWindow.document.write(html);_x000D_
iframe.contentWindow.document.close();_x000D_
}_x000D_
}_x000D_
_x000D_
var iframe = document.createElement('iframe');_x000D_
iframe.sandbox = '';_x000D_
var html = '<body>Foo</body>';_x000D_
_x000D_
document.body.appendChild(iframe);_x000D_
setIframeHTML(iframe, html);
_x000D_
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.
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;
}
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.
Source: Stackoverflow.com