But first, we need to know our limitations:
- XHR cannot be used to upload files.
- Even if it can be (which I'm 99% sure it can't), our code will look like shit.
- Don't want to use Java Applets.
- Don't want to use Flash.
- Simple so that it works with FireFox, Safari and IE.
- Although desirable, simple means there will be no progress bar. However, the file will get uploaded without the page refreshed.
So, here's my attempt using jQuery.
Typically, file uploads are done with a form and a file input. When the form is submitted, the file gets uploaded and the page refreshes with the results. The iframe hack uses that. Except, there is another component. The iframe, of course.
With the iframe hack, you will have your normal form, but the HTML returned by its submission will be 'sent' to the iframe. The iframe is invisible. So users will not see the page refreshed.
So, let's start with an example:
The form
<form id="topsecretuploadform" method="POST" action="/path/to/file/upload/handler" enctype="multipart/form-data">
<input type="file" name="topsecret">
<input type="submit" value="Let the world see it">
<iframe id="topsecretiframe" name="youcannnotseeme" src="" style="display: none;"></iframe>
</form>
You will need some JavaScript to set the target of the form.
You need to set the target to the name of the iframe. If you do that, the HTML generated by the server after it has processed the upload will be sent to the invisible iframe.
jQuery(function($) {
var iframe = $("#topsecretiframe");
$("#topsecretuploadform").submit(function() {
this.target = iframe.attr("name");
iframe.get(0).processContent = true; // The use of this flag will be explained
});
})
You will need some JavaScript to tell the user that the upload is complete
jQuery(function($) {
$("#topsecretiframe").load(function() {
if (!this.processContent)
return;
var iframeDocument = this.contentWindow || this.contentDocument;
iframeDocument = iframeDocument.document ? iframeDocument.document : iframeDocument;
var iframeBodyElement = iframeDocument.body;
// The 3 lines above is a really easy and cross browser way of getting the body of the document object of the iframe
// Assuming that we know certain elements will be present in the returned HTML after a successful upload,
// the code below shows how you can know from the script.
var someHtml = $(iframeBodyElement);
if (iframeBodyElement.find("div.successfulupload").length > 0)
alert('Upload done successfully.');
else
alert('Oh shit. Upload went fubar. Look at your logs.');
});
});Caveats
The reason for the processContent flag.
Some browsers will call the iframe onload function the first time it loads (with empty content). Some don't. If that flag didn't exists, using our example, some users will be alerted of something that haven't even started doing.Iframe names must be set statically.
Don't do this:jQuery("iframe.thosehackyiframes").each(function(index) {
this.name = "wahooga_" + index;
});From my testing, that does not work. Set them in a static way.So
So, you get dynamically uploaded files without XHR. In between the form submission and the iframe load function call, you can insert all the bells and whistles. You may actually want some sort of animated waiting gif. But that's something I'm not going to talk about.Hope this helps.