Inject Content into a new IFrame

Author : lovecicy

转自:http://sparecycles.wordpress.com/2012/03/08/inject-content-into-a-new-iframe/

文中的例子都失效了,返回404错误。

In on-line ad delivery we utilize the <iframe> tag quite a lot. There are good reasons for serving ads in iframes. There are restrictions in communication between an iframe and its parent window when the two documents are served from different domains. This helps protect users as well as site publishers from malicious activity.

Iframes also provide advantages in the speed of delivery of an overall page. The parsing and display of an iframe’s content happens asynchronously to the rest of the parent window’s resources. This means that when an iframe is encountered during an initial page load it does not prevent the rest of the page from loading while the content of the iframe is loaded.

At AppNexus we’ve been developing some improved ad delivery mechanisms which leverage the advantages of iframes, in particular we’ve been leveraging the dynamic creation of iframes to serve ad content after some user interaction is applied. However, this has been a bit of an adventure as idiosyncrasies in how browsers handle the population of content in iframes and the rendering of their  documents.

Let me illustrate.

Here’s how we would use JavaScript to create a new iframe, give it some properties, set its initial source, and add it to the existing document.

var newIframe = document.createElement('iframe'); newIframe.width = '200';newIframe.height = '200'; newIframe.src = 'about:blank';  document.body.appendChild(newIframe);

I’ve set the src attribute to ‘about:blank’. This is generally a good practice because IE can behave strangely when an iframe is appended to the document without a source. Setting the src to about:blank makes this a “friendly” iframe which means that javascript code running inside the iframe can interact with the DOM and javascript defined in its parent window and vice versa. In other words, there are no restrictions on the communication and code execution between the iframe window and the parent window.

There are several different ways we can add content to our new iframe:

  • Change the src attribute to an external document URL
  • Use the DOM’s open(), write(), close() API to inject content
  • Use the javascript: URI scheme.

Here are examples of each approach.

I can set the src attribute to an external file:

newIframe.src = 'myIframeContent.html';

If this was my intent all along, then there’s no need to start with the ‘about:blank’ src. I can set this as the src directly.

This is not convenient if we don’t have access to the site’s initial domain, and we want to maintain the “friendly” nature of the iframe. In such a case we can use the DOM API to open the iframe’s document and dynamically write content into it.

var myContent = '<!DOCTYPE html>' + '<html><head><title>My dynamic	document</head>' + '<body><p>Hello world</p></body></html>'; newIframe.contentWindow.document.open('text/html', 'replace'); newIframe.contentWindow.document.write(myContent); newIframe.contentWindow.document.close();

First, I’d like to call out that the contentWindow isn’t created until we’ve added the iframe to the parent window’s DOM (which we did in the first code block above).

This approach is adequate to dynamically inject content into an iframe in most cases. However, if the content contains resources to external <script> resources you can run into problems in IE.

Look at this example in Chrome, and you will see an iframe populated with content using the approach above, and it includes a reference to an external script which provides content for the iframe.

However, if you look at the same page in IE, you will see a JavaScript error and no content is displayed in the iframe.

Here’s the code for the iframe:

var iframe = document.createElement('iframe'); 
var ctnr = document.getElementById('ctnr'); 
var content = '<!DOCTYPE html>' + '<head><title>Dynamic iframe</title>' + '<body>	<div id="innerCtnr"></div>' + '<script type="text/javascript"	src="external.js"><\/script>' + '<script type="text/javascript">' +	'document.getElementById("innerCtnr").innerHTML = externalVar;' +	'<\/script>' + '</body></html>'; ctnr.appendChild(iframe); iframe.contentWindow.document.open('text/html', 'replace'); iframe.contentWindow.document.write(content); iframe.contentWindow.document.close();

In external.js there is a variable defined which is referenced by the code which is being added in our document.write call. external.js is loaded asynchronous to the parsing of document.write(content), therefore at the time that we invoke this:

document.getElementById("innerCtnr").innerHTML = externalVar;

externalVar has not yet been defined and causes a ReferenceError.

To workaround this problem in IE we can make use of the javascript: URI scheme which makes bookmarklets and scriplets possible in all browsers. Instead of using document.open/write/close we use the following approach:

iframe.contentWindow.contents = content; 
iframe.src = 'javascript:window["contents"]';

First, we assign the dynamic content to a variable on the iframe’s window object. Then we invoke it via the javascript: scheme. This not only renders the HTML properly, but loads and executes the scripts in the desired order.

Here’s an example using the javascript URI scheme.

Summary

Dynamic iframes are resourceful ways to add new content into a web page, but care and attention has to be paid as to how those iframes are injected with content to avoid errors caused by browser differences in loading and executing scripts added to those iframes.

standard

Have your say