Here is the full and almost working version of my XHTML AJAX image uploader. It uses AJAX to upload files and I'm okay with not supporting IE9 and older and Opera 11.6 and older (IE10 and Opera 12 support it). I am however having trouble with Firefox.
The main problem is dynamically attaching the id to target (or even pass along the i which would correlate with the element[i]) to the progress event. It works fine in Chrome and Opera 12 though not Firefox 10. The event listener is on line 100 in the code below.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Image Preview</title>
<script type="application/javascript">
//<![CDATA[
function images_name(s)
{
var a = reverse(s);
var b = a.split('.');
var c = reverse(b[1]);
var d = c.toLowerCase();
var e = d.replace(/^\s+|\s+$/g,'');
var f = e.replace(/^-+|-+$/g,'');
var g = f.replace(/ /g,'-');
var h = g.replace(/[--]+/g,'-');
var i = h.replace(/[^a-zA-Z 0-9-]+/g,'');
return i;
}
function images_preview()
{
if (typeof FileReader=='function')
{
var files = document.getElementById('post_files').files;
for (var i = 0; i < files.length; i++)
{
var fr = new FileReader();
fr.file = files[i];
fr.onloadend = function(e)
{
var ip = document.getElementById('images_preview');
var file = e.target.file;
var d1 = document.createElement('div');
var dn = images_name(file.name);
d1.setAttribute('id',dn);
var t = document.createElement('img');
t.setAttribute('alt','Preview');
t.setAttribute('class','left');
t.setAttribute('src',e.target.result);
d1.appendChild(t);
ip.appendChild(d1);
var d2 = document.createElement('div');
var s2 = document.createElement('span');
var s2t = document.createTextNode(file.name);
s2.appendChild(s2t);
d2.appendChild(s2);
d1.appendChild(d2);
var d3 = document.createElement('div');
var s3 = document.createElement('span');
var s3t = document.createTextNode(file.type);
s3.appendChild(s3t);
d3.appendChild(s3);
d1.appendChild(d3);
var d4 = document.createElement('div');
var s4 = document.createElement('span');
var s4t = document.createTextNode(Math.round(file.size/1024)+' KB');
s4.appendChild(s4t);
d4.appendChild(s4);
d1.appendChild(d4);
var d5 = document.createElement('div');
d5.setAttribute('class','progress');
var d6 = document.createElement('div');
d6.setAttribute('class','status');
d5.appendChild(d6);
d1.appendChild(d5);
}
fr.readAsDataURL(files[i]);
}
}
else {alert('Notice: image preview not supported by your browser.');}
}
function images_upload(e)
{
e.preventDefault();
var n = '';
for (var i = 0; i < document.getElementById('post_files').files.length; i++)
{
var file = document.getElementById('post_files').files[i];
n = images_name(file.name);
//alert('n1 = '+n);
var xhr = new XMLHttpRequest();
if (typeof xhr.upload=='object')
{
var upload = xhr.upload;
upload.addEventListener('progress',function(n)
{
return function(e)
{
//alert('n = '+n);
var loader = document.getElementById(n).getElementsByClassName('status')[0];
var p = Math.round((e.loaded * 100) / e.total);
loader.style.width = p+'%';
//alert('n = ' + n);
};
}(n), false);
xhr.open('POST','upload.php');
xhr.setRequestHeader('Cache-Control','no-cache');
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
xhr.setRequestHeader('X-File-Name',file.name);
xhr.send(file);
}
else {var ns = true;}
}
if (ns) {alert('Error: your browser does not support AJAX file uploads.');}
}
function reverse(s) {return s.split('').reverse().join('');}
window.onload = function()
{
if (window.addEventListener)
{
document.getElementById('form_images').addEventListener('submit',function(e) {images_upload(e);},false);
document.getElementById('post_files').addEventListener('change',function() {images_preview();},false);
}
}
//]]>
</script>
<style type="text/css">
.left {float: left;}
#images_preview > div {border: #000 solid 1px; clear: both; float: right; margin: 8px; overflow: auto; width: 400px;}
#images_preview > div > div {margin-top: 8px;}
#images_preview div.progress, #images_preview div.status {background-color: orange; bottom: 0px; clear: both; height: 2px; position: relative;}
#images_preview div.status {background-color: #0f0; width: 0px;}
#images_preview img {background-color: #ccc; border: #666 solid 1px; margin: 8px; max-height: 100px; max-width: 100px; padding: 4px;}
</style>
</head>
<body>
<form action="" id="form_images" method="post" enctype="multipart/form-data">
<fieldset>
<div><input id="post_files" name="post_files" multiple="multiple" size="128" type="file" /></div>
<div><input name="post_files" type="submit" /></div>
<div id="images_preview"></div>
</fieldset>
</form>
</body>
</html>
Some clarifications: first I only care about having this work in Firefox, Chrome, Opera 12+ and IE 10+, I'll have single file non-AJAX fallback in place. Right now I just want to concentrate on getting this to work however.
I've tested this and it works in Opera 12+, IE 10, Chrome (whatever) though not even Firefox 14 (current nightly build version). I would highly prefer a reasonable version number for Firefox to work like Firefox 4 in example.
No frameworks, I try to do quality and figuring problems out like this helps me learn real JavaScript coded correctly.
When I post JavaScript questions people start picking things out that clearly should be left in, example this is an AJAX file upload so why anyone would remove preventDefault is beyond me.