Submitting a form with target set to a script-generated iframe on IE

(nice title, isn’t it?)

Although AJAX techniques are now widespread among the web-developers community, there’s still something that can’t be done using pure AJAX techiques (whatever that means): file uploads. There’s no way to grab a file using JavaScript and send it to the server using an asynchronous request. I guess the main reason for this is security, you don’t want web sites to steal files from your home directory.

Anyway, people found a relatively popular way to do this. The ideia is having a hidden iframe on the page, and use that iframe as a target of the form where you have the reference to the file to upload. So, when the form submits, the result page will go to the hidden iframe, and the user won’t see a full page refresh. You still have to take care of some details, like hiding the form and showing a nice progress bar with the classic “Hold on, we are uploading” message, and polling for the content of the iframe to check is the file is still in its way, or if it arrived safely.

One nice detail is where do you actually have the hidden iframe. You could just put the iframe on the HTML code of your page, but IMO that’s ugly. The iframe is an artifact that is needed just to serve as a black hole for the form submittion result page. It doesn’t make sense to put it in your HTML code, as it hasn’t anything to do with the content. Also, it’s error prone: you may inadvertently delete it, causing the file upload to missbehave. Or you may need to change the iframe code and having it on HTML will force you to update on all the pages where you use it (and of course, you will forget one).

So, I believe the most elegant solution (if you can use the word “elegance” in the context of this major hack) is to generate the iframe using javascript. You may do this on the page load, when you create the form, whatever. Just do it before the user submits the file! :) Well, it’s easy, right? Something like this does the trick:


var objBody = document.getElementsByTagName("body").item(0);

var iframe = document.createElement('iframe');

iframe.id = 'fileUploaderEmptyHole';
iframe.name = 'fileUploaderEmptyHole';
iframe.width = 0;
iframe.height = 0;
iframe.marginHeight = 0;
iframe.marginWidth = 0;

objBody.insertBefore(iframe, objBody.firstChild);

That’s cool, right? Just run this and the iframe will be created. Submit the form, the result goes in the hidden iframe, everything works, we are done, let’s go home. Well… all true until you actually test it on Internet Explorer…

If you test this on IE, the result will be a new window being created when you submit, which is clearly not what you want. Well, I had a hard time finding the problem, so here goes: if you create the iframe using JavaScript, IE wont set it’s name. Yes, the “iframe.name = ‘fileUploaderEmptyHole’;” line will simply be ignored. It does nothing. So, as you don’t have any frame called ‘fileUploaderEmptyHole’, when you submit the form, it will create a new window named ‘fileUploaderEmptyHole’ and display the result on it.

The solution it to hack this even further. Specifically:

iframe = document.createElement('<iframe name="fileUploaderEmptyHole">');

Yeah! Now you’re thinking “WTF?”. Yes, yes, it’s true. This actually works on IE, with the expected (?) results. Well, you still have to support the other browsers, but you are lucky, as this will throw an exception on all the non-IE browsers. So, it’s just a matter of catching it, and running the decent version of the code:


var iframe;
try {
  iframe = document.createElement('<iframe name="fileUploaderEmptyHole">');
} catch (ex) {
  iframe = document.createElement('iframe');
}

iframe.id = 'fileUploaderEmptyHole';
iframe.name = 'fileUploaderEmptyHole';
iframe.width = 0;
iframe.height = 0;
iframe.marginHeight = 0;
iframe.marginWidth = 0;

This is why I love the Web. Or maybe not.

38 thoughts on “Submitting a form with target set to a script-generated iframe on IE

  1. Diego

    THANKS SO MUCH!!!

    I was facing that “strange” IE behaviour
    and before going crazy I just “googled” for it and… here I am!

    WTF is exactly what went through my mind reading your solution! :)

    Thanks again!

    Reply
  2. Dmitry A. Soshnikov

    Thx so much, yesterday i tried to fix this ugly IE bug myself (thinkin’ – what a hell is going on here?!), today – went to google and found your article =) Thx again – it fixed now )

    Reply
  3. Claus Conrad

    Thanks a lot for your entry! I remember having read about the same bug in IE for dynamically created INPUT radio buttons a long time ago, but when I experienced the same behavior with an IFRAME today as you it didn’t strike me that I could use the same workaround again (mostly because setting “name” attribute on other tags in IE has worked for me since reading the other article years ago). Building a product that appears faster by forcing competitors to catch more “exceptions” is just so typical for Microsoft marketing :S.

    Reply
  4. Mike

    I spent a few hours searching for this, this saved my rear. Thanks.

    Also, it’s worth mentioning that IE has the same problem with dynamically assigning a function to onload(). So if you you want to do an onload() handler for your iframe (when the file is finished uploading, for example) you need to do:

    iframe = document.createElement(”);

    iframe.onloadHack = function()
    {
    // do whatever
    }

    Hope this helps anybody else who was wondering.

    Thanks again!

    Reply
  5. Mike

    I just discovered the same thing is true for dynamically-created forms. For example, the following works in Firefox but not IE 7 (or presumably 6):

    var form = document.createElement(‘form’);
    form.enctype = ‘multipart/form-data’;

    instead, in IE you must do

    form = document.createElement(”);

    and you should be fine.

    Reply
  6. sameera

    Thank you ever so much!!!! i’ve been breaking my head for over 4 hours on this!!!!

    This is why i love the web too :-)

    Reply
  7. Cmdr. Gabe E.

    Thank you very much for sharing this! :D
    I’ve gone around in circles trying to solve this problem, and your solution nailed it :D!
    Awesome! Thanks again :D!

    Reply
  8. Pingback: Creating an asynchronous upload extender « Update Panel .NET

  9. Tuan

    Brilliant solution.

    It works like charm.

    Do you know how to dynamically create form and submit it to that iframe.

    Actually i got it work in FF. But it goes crazy with IE. It seems the file in the form not able to uploaded to server.

    Thanks,
    Tuan

    Reply
  10. Ruben

    WTF!?

    A friend of mine asked me how to create an iframe in a dynamic way…. well i told him it was possible based on the “standard” procedure:

    var iframe = document.createElemement (‘iframe’);
    iframe.name =”iframeName”;

    He told me it wasn’t working and i said:
    WTF?!?

    if i do it width every element, why not with an iframe?!

    well, because of IE…

    How stupid of me! :) expecting IE to behave in a constant way!

    Thank you!

    Reply
  11. Nick

    An alternative to defining the name attribute of the iframe via document.createElement would be to create the iframe using innerHTML e.g.

    body.innerHTML = “” + body.innerHTML;

    This approach would work across browsers, so there would be no need for the try/catch statement. Note though that the iframe needs to be created before the target property of the form is set.

    Reply
  12. gevorg

    Hi

    I used compact solution for this hole, but it was not working for IE7. Every time when I was trying to upload, new popup was adding and uploading goes there. After I read your comments and changed scripts, but now processing don’t works on IE7 too. Now page just redirects to action page in same frame.

    Here is script:

    /**
    *
    * AJAX IFRAME METHOD (AIM)
    * http://www.webtoolkit.info/
    *
    **/

    AIM = {
    frame : function(c) {
    var n = ‘f’ + Math.floor(Math.random() * 99999);

    var i;
    try {
    i = document.createElement(”);
    } catch (ex) {
    i = document.createElement(‘iframe’);
    }

    i.onload = function() {
    AIM.loaded(n);
    }

    i.id = n;
    i.name = n;
    i.width = 0;
    i.height = 0;
    i.marginHeight = 0;
    i.marginWidth = 0;

    document.body.insertBefore(i, document.body.firstChild);

    if (c && typeof(c.onComplete) == ‘function’) {
    i.onComplete = c.onComplete;
    }
    return n;
    },
    form : function(f, name) {
    f.setAttribute(‘target’, name);
    },
    submit : function(f, c) {
    AIM.form(f, AIM.frame(c));
    if (c && typeof(c.onStart) == ‘function’) {
    return c.onStart();
    } else {
    return true;
    }
    },
    loaded : function(id) {
    var i = document.getElementById(id);
    if (i.contentDocument) {
    var d = i.contentDocument;
    } else if (i.contentWindow) {
    var d = i.contentWindow.document;
    } else {
    var d = window.frames[id].document;
    }
    if (d.location.href == “about:blank”) {
    return;
    }
    if (typeof(i.onComplete) == ‘function’) {
    i.onComplete(d.body.innerHTML);
    }
    }
    }

    Can you help me here?

    Thanks,
    Gevorg

    Reply
  13. Poashoas

    Do you know how long I have been searching for his? I found out the iframe had to be build dynamicly but that Name property was totaly new to me. Nice post and it took a while before I had the Google ‘magic words’. Now let’s build it in and cross my fingers :-D

    Reply
  14. Dmitry Polushkin

    Hey, man… thanks for this post. Really helped me.

    I’m using jQuery to generate iframe tag, and I’ve tried to use attr(‘name’, iframe_id), but I had a problem described in this post.

    Now I’m using
    var iframe = $(“”);

    And it works in IE fine, thank you maaaan for help me to resolve this problem.

    Reply
  15. Andrei

    Another solution to fix the IE bug (tested only on IE8) is to use the regular Mozilla code, and to add:

    try
    {
    window.frames['upload_iframe'].name=”upload_iframe”;
    window.frames['upload_iframe'].id=”upload_iframe”;
    }
    catch (e) {}

    Reply
  16. Prakash

    Thanks. Really i wasted more than two days to find solution. After i read this blog within 5 minutes i completed my task.

    Thank u very very much…

    Reply
  17. SĂ©bastienM

    Thanks to the trick, and the comments.

    Here is my final workaround (IE6, IE7). You need to insert the objet in the elements, and then re-affect the name. This seems to record the iframe

    - frame = createElement(‘iframe’
    - frame.id = “my_new_frame”;
    – frame.name = ” my_new_frame”;
    - insert element in the document
    – (the iframe is known as a document element), re-set the name:

    if (document.frames) {
    document.frames['my_new_frame'].name = “my_new_frame”;
    }

    Reply
  18. Retepvosnul

    Great job,

    To bad we still have to make an effort to fix that broken piece of #($&*#$ browser ,but your solutions is great, and makes it behave just as all proper browsers.

    But you made my daY !

    Reply
  19. daniel

    Thank you very much. I didn’t understand why my scripts didn’t work (still now I don’t understand, but now works).
    It is interesting that about 3 years later there is the same problem with IE.

    Reply
  20. Kostas Tsakas

    Oh, thanks a lot. I don’t usually write comments, but I think this is really worth it.
    Dude, you saved me from all my pain and frustration…

    Reply
  21. cdo

    Hi,
    You could do something like
    //create an inframe using dom and append to body’s children
    //then set a form’s target the new created iframe object
    In this fashion you’ll be asked by IE is using a SSL connection if you allow mixed content on your page. I had this problem and, to fully solve it I introduced an iframe in HTML as the first child of the body thanks to this article so Internet Explorer 6 could find it. Thanks a lot for this hint!

    Reply
  22. Pingback: Posting form from one iframe to another – IE | Jisku.com - Developers Network

  23. Pingback: Posting form from one iframe to another – IE

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>