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.
var newIframe = document.createElement('iframe'); newIframe.width = '200';newIframe.height = '200'; newIframe.src = 'about:blank'; document.body.appendChild(newIframe);
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
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.
Here’s the code for the iframe:
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.
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.