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. Today's 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 haven't 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.