How can I upload files asynchronously without jQuery and support IE6-9?

693 views Asked by At

I have a simple form for uploading a file that works well enough synchronously:

<form id="uploadForm" action="/upload" method="post" encType="multipart/form-data">
  <input type="file" id="file" name="file" accept=".csv" />
  <input type="text" name="comment" />
  <button type="submit">Upload</button>
</form>

How can I make this form upload the file asynchronously (without reloading the page?), and be notified when the upload is complete, and if there were any errors?

I know that the primary way to make an asynchronous request is to use an XMLHttpRequest, and I see that something like the following is now possible (I have not tested this code):

function handleLoad() {
  console.log(this.responseText);
}
function handleError(error) {
  console.error(error.stack);
}
function handleSubmit(e) {
  e.preventDefault();
  var form = document.getElementById('uploadForm');
  var request = new XMLHttpRequest();
  request.addEventListener('load', handleLoad);
  request.addEventListener('error', handleError);
  request.open("POST", "/upload");
  request.send(new FormData(form));
}

However, according to MDN, sending FormData, Blobs, Files, or ArrayBuffers is only supported in IE10+, and I want a backwards-compatible solution (which doesn't use jQuery).

There are plenty of answers to How can I upload files asynchronously?, but all of the answers I have read so far use jQuery or use the modern XMLHttpRequest method I described above.

I am using React and have no use for anything else jQuery offers, so I would like to keep my page download size smaller by solving this with vanilla JS.

1

There are 1 answers

0
Andy On

The oldest established way to do this is by making the <form> target a hidden <iframe> element by adding target="<name of iframe>" to the <form> element. When you call form.submit() in JavaScript, it will load the response in the target <iframe>:

<form id="uploadForm" action="/upload" method="post" encType="multipart/form-data" target="asyncFrame">
  <iframe id="asyncFrame" name="asyncFrame" height="0" width="0" frameborder="0"></iframe>
  <input type="file" id="file" name="file" accept=".csv" />
  <input type="text" name="comment" />
  <button type="submit">Upload</button>
</form>

<script>
  var iframe = document.getElementById('asyncFrame');

  function handleLoad() {
    console.log('response:', iframe.contentWindow.document.body.innerText);
  }
  iframe.addEventListener('load', handleLoad);
</script>

The most awkward thing about this approach is there is no way to get the status code of the response, and no standard way (as far I can tell) to determine if the request failed. You will have to inspect the response body (iframe.contentWindow.document.body.innerText) to determine if there was an error (if you are posting to your own server, luckily, you can ensure that the response body contains sufficient information, but if it is a 3rd-party server, you may be out of luck).