Communicating with the server

Navigation

Skip navigation.

Site search

Site navigation

More information

Warning

This type of script has an inherent problem. If you are using JavaScript to pass data back and forward between a client and server, then you will almost certainly be compromising the accessibility of your site. As a result, you should only use this in a situation where you can be certain that lack of accessibility will not cause a problem.

Loading external JavaScript data after the page has loaded

The problem

JavaScript is incapable of streaming data, or importing script files after the page has loaded. Even many DOM capable browsers will not run the contents of a script element that is is created using DOM methods after the page has loaded. The only way that JavaScript will reliably run a script is if the script is embedded in the page directly or via an external script file, while the page is loading. Data can also be loaded into the script via form inputs, or other controls, but this requires the viewer to enter the data themselves. So in order to load data, it must be included in the contents of the page as it loads, and this is not always desirable. Sometimes, a script author may need to load data after the page has loaded, so in order to do this, they would need to stop the script, load a new page to collect new data, and then continue once the new page has loaded.

I find this situation unacceptable, as the running script may be of significant importance, and terminating it often produces an unsightly effect, and it may require some effort to pass the information from the old page to the new page, and to recreate the setup of the old page. Would it not be easier if JavaScript were able to simply load some data? Microsoft have come up with a way using ActiveXObject, and the Mozilla have decided on a different way, which has since been copied by Opera, Safari, and Konqueror (more on that later). Neither are standardised (although the latter is being included in an upcoming proposal), and fail in older browsers. I also find this situation totally unacceptable, and so I offer a further alternative. I note that, like myself, other people have also used this technique before, and found it to be very successful.

Of course, JavaScript can use cookies to retain data from one page to another, and it can also read data from an encoded URL, but neither of these can cope with even as much as 4 KB of data, and I am more interested in being able to load data files from a server. Of course, you could also open a popup window containing the data, and this file could then use the opener property to pass the data to the main window. In order to get a reliable cross browser effect when using opener, you need to use window.open, not target="_blank", but this will usually be blocked by most popup blockers, so I would never recommend this technique.

Demonstration

Try loading data with this script. It should work in a minimum of Internet Explorer 4+, Netscape 4+, Mozilla, Opera 5+, Safari/Konqueror 3+, ICEbrowser and iCab. For some unknown reason, OmniWeb 4.5-5.0 completely screws up iframes, so although it can handle this technique sometimes, on this particular page, it fails completely. Escape and OmniWeb 4.2- do not correctly implement layer.load. Clue browser and WebTV/MSNTV refuse to run this script properly due to bugs in both browsers.

You can also see the data loading page. See below for details.

I have also written a script for importing XML data files. Although this uses the ActiveXObject and W3C DOM techniques in browsers that support them, it also uses a similar technique to this script in browsers that do not.

Using iframes

Yes, I turn to my good friend; the iframe. As I have said, the only way to load more data is while a page loads, so we must load another page. But I do not want to replace the current page, so I must use some form of frames to allow one page to load data while another remains as the page that the user interacts with. Using frames causes numerous complications, so I restrict myself to the reliable iframe.

Firstly, the main page must contain a function that will receive the data when the data loads:

function doSomethingWithTheData( theData ) {
  //put your own code here that uses theData
}

Next, the page must contain the iframe into which the data will be loaded. Of course, just placing an iframe on the page will probably mess up the layout and will not exactly match the page theme, so it will need to be hidden and removed from the usual flow of the page. The easiest way is to hide it in an absolutely positioned hidden div (some browsers do not like to position the iframe itself):

<div style="position:absolute;left:0px;top:0px;visibility:hidden;" id="datadiv">
  <iframe src="about:blank" height="0" width="0" name="dataframe"></iframe>
</div>

This is best put at the immediate end of the page, just before the </body> tag. Note that it requires you to use HTML transitional.

Now to load the data (restriction; you can only load data from the same domain as the current page):

window.frames['dataframe'].window.location.replace('moreData.html');

Note: If you want to submit form data to construct the external data URL, I have demonstrated how in a previous email.

And finally for the new page to pass the data to the main page. It can pass any data in any format you choose, provided that data is contained in a JavaScript variable. It could even be an array of arrays representing a database table:

var someData = 'blah';
var someData = ['one',2,new Date()];
var someData = [ ['one',2,new Date()], ['some','more','data'], ['and','yet','more'] ];

And once the data is ready, it is passed to the function we created in the main 'parent' page:

parent.doSomethingWithTheData( someData );

You can choose to run the function as the iframe page is still loading or after it has loaded (using the onload event).

And layers browsers

**sigh** Oh well, I hate to do it, but I would only feel guilty if I didn't tell you. Despite the fact that layers browsers like Netscape 4 do not support iframes, they can use a variation of this script to import data. You will note that the positioned element created for the iframe (yes, a layer) was also given an ID. Well, that is not just for decoration. It means that the layer is available to layers browsers as document.layers['datadiv'] (I could also use a numerical index, but this is just easier).

Layers offer a method 'load' which can load new content from an external page into the layer. It is called passing two parameters; the URL to load and the new width for the layer. We might as well make it 0 pixels wide, it does not matter as we do not care what it looks like, it is hidden.

document.layers['datadiv'].load('moreData.html',0);

In order to use both techniques, all we need to do is to check which version to use:

if( document.layers && document.layers['datadiv'].load ) {
  document.layers['datadiv'].load('moreData.html',0);
} else if( window.frames && window.frames.length ) {
  window.frames['dataframe'].window.location.replace('moreData.html');
} else {
  alert( 'Doesn\'t work' );
}

Once the page has loaded, and any onload scripts have completed running, it becomes a part of the main page itself (where did you come up with that idea Netscape?!), but until then, it runs in exactly the same way as if it were in an iframe, so we do not need to make any changes to the data page.

Other restrictions

The frame or layer can only load one page at a time. You can in fact call the receiver function (or functions - your choice) multiple times from the data page, so you could in fact load multiple datasets in the same page. If, however, you need to load multiple pages of data (for example, if each page needs to be generated by a separate process on the server), you will either need to wait for each page to load and move on to the next page, or you will need multiple iframes/layers.

Opera and iCab users can disable iframes. If they do then it may be possible to open a popup window and load the data through that. However, a user that has blocked iframes will almost certainly have also blocked popups, so there is no real point in trying. As the script can detect if iframes are blocked (see the else statement in the section on layers), you would be better off to use that to produce a message politely asking them to enable iframes.

A W3C DOM alternative?

Yes, there is an alternative provided by the W3C DOM, but it is just an alternative way to do the same thing as my iframe/layer script. After the page has loaded, embed a script tag into the page head, and give it a source SRC. The script will be run as soon as it loads, in the context of the current page. This has the limitation that it only works in Internet Explorer 5+ on Windows, Mozilla/Netscape 6+ and Opera 7.5+, as well as newer versions of Safari/Chrome, Konqueror and OmniWeb.

Older releases of Safari, Konqueror, OmniWeb 4.5+, as well as Internet Explorer on Mac, Opera 7.23-, ICEbrowser, and 4th generation browsers like Internet Explorer 4-, Netscape 4, Opera 6- and iCab do not support this technique, even though they all support the iframe/ilayer technique (most of them produce errors if you try to use it).

if( document.createElement && document.childNodes ) {
  var scriptElem = document.createElement('script');
  scriptElem.setAttribute('src','someNewUrl.js');
  scriptElem.setAttribute('type','text/javascript');
  document.getElementsByTagName('head')[0].appendChild(scriptElem);
}

Test it here: embed a script tag into the document.

Sending data back to the server

Using popups

If you resort to this, then you are asking for trouble. Many popup blockers will block this, but you could always load the data into a form and submit it into target "_blank" - Most popup blockers will block this, and some browsers will not allow you to automatically close the popup afterwards, leaving your viewers with an irritating popup window. Not the best way to make friends.

This technique has the slight advantage that it may still work when when triggered by the onunload event.

Using cookies

Yuck! ... Well ... OK. you can save the data as a cookie using JavaScript, then request a new page. This would pass the data back to the server. However, as you would then need to ensure that you deleted the cookie etc, and many users will block cookies, this is not a good method.

Using the iframe/layer

The easiest way to send data back to a server is to load the data into a form, and submit the form. Of course, this is subject to the same limitations as before; it requires that the current script is terminated. You could submit the form into the iframe (ignoring layers browsers). In fact, if you want to send more than 4 KB, this is the only way to do it, using the "post" method. Submitting the form adds a history entry, and that may be undesirable.

Instead, you could URL encode / escape the data (just like using the "get" method on a form) and request new data using the script above. This would not only enable the script to work in layers browsers as well, but it would also allow you to pass something back to the current page, to say that the server had completed work on the data. Assuming that you do not want to send more than 4KB of data, this is by far my favourite method.

This technique may fail if you attempt to use it when triggered by the onunload event.

Note: It is actually possible to use XMLHttpRequest and Microsoft.XMLHTTP to POST data to the server without adding history entries but that restricts you to modern browsers such as Internet Explorer 5+ on Windows, Mozilla/Netscape 6+, Safari 1.2+, Konqueror 3.3+ and Opera 7.6+, and does not work with file:// URLs.

Using images

This seems like a strange way to pass data, but in fact it is quite effective, although I do prefer to URL encode the data and pass it back using the technique described above. However, for some purposes, this may be preferred. I do not recommend this technique, but I am writing it here to let you know you can use it if you want to. Be warned that if the user has disabled images, this technique will fail to work in most browsers. Assuming images are supported, this works in just as many browsers as the iframe/layer technique.

URL encode (escape) the data and pass it back in a request to cache an image. You can then wait for the image to load and use the onload and onerror events to detect if the data was successfully received and used (you cannot reliably get any more information than that). This has the disadvantage that it builds up a large cache of images so it can use large amounts of memory. However, it can still be fairly useful.

The JavaScript:

var i = new Image();
i.onload = function () { /* server is happy */ };
i.onerror = function () { /* server is not happy */ };
i.src = 'myPage.php?data='+escape(theData);

The PHP (you could use any server side language you choose):

$happy = do_something_with($data);
if( $happy ) {
  header( 'Content-type:image/gif' );
  fpassthru( fopen( 'myPcture.gif', 'rb' ) );
} else {
  print 'Some text to make the image load fail';
}

This technique may fail if you attempt to use it when triggered by the onunload event.

This site was created by Mark "Tarquin" Wilton-Jones.
Don't click this link unless you want to be banned from our site.