Import JavaScript Using iFrames

There are a variety of ways to dynamically import JavaScript files on demand, the most elegant solution is to add additional script tags into the header. Todays article introduces a different technique, a hack if you will, leveraging the fact that an iFrame can load and run its own JavaScript, entirely independent of the parent context. So for each JavaScript file to include, beyond the main library, an iframe must be appended to the end of the body tag, which will call another simple HTML page where the JavaScript file we need is included. There are also other, maybe better uses of this technique, such as using JavaScript to cut HTML fragments from a page, but I havent found the killer application yet. Here is an example of how the JavaScript will be imported:

Example 1: The Import Function

var _createFrameWindow = function(url) {
	var oIFrame = document.createElement(iframe);
	oIFrame.setAttribute(height, 0);
	oIFrame.setAttribute(width, 0);
	oIFrame.className = displayNone;
	YAHOO.util.Dom.get(project).appendChild(oIFrame);
	oIFrame.src = url;
	return oIFrame.contentWindow;
};

var _importJavaScript = function(fileName, callback) {
	var iWindow = _createFrameWindow(fileName),
		intervalId = setInterval(function() {
		if (iWindow.init) {
			clearInterval(intervalId);
			iWindow.init(window, document);
			callback();
		}
	}, 500);
};

_importJavaScript(http://localhost/core/test_iframeImportee.html, function() {
	YAHOO.util.testObject.test();
});

The _createFrameWindow function appends an undisplayed iframe to the end of body, with the src set to the provided url. The _importJavaScript function first calls _createFrameWindow to create the iframe, then waits until it is loaded (when the init function is available), before calling the init function. The init function is a special function that must wrap any imported JavaScript. The init function requires 2 parameters to be passed, the window and document of the parent context, so that the JavaScript executing therein has access to the parent window. This means that any imported JavaScript must use the window and document variables provided by the init function, instead of referencing window and document directly as globals. This is because the included iFrame will have a different window and document than the parent window containing the iFrame, and any JavaScript running inside the iFrame must be made aware of the parent window context. Otherwise, if you included something like yahoo-dom-event in the parent window, and reference it ("YAHOO.util.Dom") in the JavaScript of imported window, then the JavaScript will fail. It must instead be written like ("contextWindow.YAHOO.util.Dom"), where contextWindow is the window passed into the init function.

The last few lines of Example 1 execute the _importJavaScript function, providing the URL to call and a callback function to execute after the init function of the imported JavaScript. In this case the test_iframeImportee.html file imports a script that creates a new object YAHOO.util.testObject. Then the callback executes a test function attached to testObject which, just prints out an alert, proving that the import worked. Here is an example of what the testObject JavaScript file looks like:

Example 2: The TestObject

var init = function(contextWindow, contextDocument) {
	contextWindow.YAHOO.util.testObject = {
		test: function() {
			alert(This is now attached to the main window.);
		}
	};
};

Example 2 shows how to write JavaScript imported by this technique. All the JavaScript code of the import file is wrapped inside of an init function. Inside the init function the JavaScript is written normally and any dependencies (such as YAHOO) from the parent window can be assumed to be available, as long as they are properly referenced from the context parameters. In this case, the object testObject is added to "YAHOO.util" and a function test is exposed, showing the inner workings of the test function called by Example 1.

These examples are also my proof of concept, which I ran in the following browsers: IE6, IE7, IE8, FF2, FF3, Safari 3, Opera 9, and Chrome. All were run on a WinXp machine, and Safari 3 and FF3 were also run on Mac OSX. In some previous browsers versions there have been issues with referencing the contextWindow of an iFrame, but I believe these are limited Opera <=7 and IE <=5. And as these browsers are now used by less than 1% of 1% of the people using the internet, you can safely assume that contextWindow is available and that this technique will work for your JavaScript enabled users.

The only other drawback to this technique is that JavaScript files from another domain cannot be imported. All browsers have built in security preventing cross-site scripting, which will throw exceptions if you try to import JavaScript from another domain.