Update: This doesn't work in the Windows version of Safari.
However, while that may be the case, let's examine a few facts:
So, to recap, before you write in to piss and moan about Safari on Windows, consider the following... 1. I'm not a Windows user. 2. Most people that use Safari aren't Windows users. 3. Safari 5 functionality, while true, is the focus of this article, isn't the whole point of this article.
I honestly don't care one way or another what browser or OS you happen to like, again, that's not even the point.
After doing a bit of research to see where IE9 stands with regards to drag and drop file support, I found a stunningly large amount of people seem to think that drag and drop upload does not work in Safari 5. Well, this post is here to set the record straight, Safari 5 absolutely supports the new drag and drop file upload standard. Turns out IE9 dropped the ball on this, I'm afraid, and we'll have to wait for the next IE to maybe get drag and drop file uploads.
Prior to the release of Safari 5, this feature was supported in both Chrome and Firefox 3.6. However, the variant of the spec supported in Chrome and Firefox is a more complicated implementation, and with Safari 5, a simpler version of drag and drop file uploading was introduced that has since been implemented in both Firefox 4 and in Chrome.
The differences pan out to be that in Firefox and in Chrome, both browsers required you to essentially build an HTTP POST request from scratch, and the Safari implementation was streamlined to allow a much simpler API. I'm given to understand that Firefox 4 and newer versions of Chrome now support both methods, and Safari 6 will add support for the more complicated method.
This is what a working implementation looks like:
<html>
<head>
<title>Drag and Drop File Upload Demo</title>
<script type='text/javascript' src='/Library/jQuery/jQuery.js?hFileLastModified=1364984544' charset='utf-8'></script>
<script type='text/javascript'>
var dnd = {
ready : function()
{
$('div#uploadTarget')
.bind(
'dragenter',
function(e) {
e.preventDefault();
e.stopPropagation();
}
)
.bind(
'dragover',
function(e) {
e.preventDefault();
e.stopPropagation();
}
)
.bind(
'drop',
function(e) {
if (e.originalEvent.dataTransfer.files.length) {
e.preventDefault();
e.stopPropagation();
dnd.createFileQueue(e.originalEvent.dataTransfer.files);
dnd.upload(e.originalEvent.dataTransfer.files);
}
}
);
},
createFileQueue : function(files)
{
$('tbody#uploadQueue').find('tr').not('.uploadTemplate').remove();
$(files).each(
function(key, file) {
var tr = $('tr.uploadTemplate').clone();
tr.removeClass('uploadTemplate');
tr.attr('id', 'file-' + key);
if (file.type.match(new RegExp('/image.*/')) && FileReader) {
// Displaying a thumbnail doesn't seem to work in Safari
var img = document.createElement('img');
img.file = value;
img.classList.add('thumbnail');
tr.find('tr').eq(0).html(img);
var reader = new FileReader();
reader.onload = (function(img) {
return function(e) {
img.src = e.target.result;
};
})(img);
reader.readAsDataURL(value);
}
tr.find('td').eq(1).html(file.name);
tr.find('td').eq(2).html(dnd.getFileSize(file.size));
$('tbody#uploadQueue').append(tr);
}
);
},
upload : function(files)
{
// This is a work-around for Safari occaisonally hanging when doing a
// file upload. For some reason, an additional HTTP request for a blank
// page prior to sending the form will force Safari to work correctly.
$.get('/file/blank.html');
var http = new XMLHttpRequest();
$('div#uploadProgress > div > div').css('width', 0);
$('div#uploadComplete').fadeOut(
'normal',
function() {
$('div#uploadProgress').fadeIn();
}
);
if (http.upload && http.upload.addEventListener) {
http.upload.addEventListener(
'progress',
function(e) {
if (e.lengthComputable) {
var progress = Math.round((e.loaded * 100) / e.total);
var progressDiv = $('div#uploadProgress > div > div');
progressDiv.css('width', progress + '%');
}
},
false
);
http.upload.addEventListener(
'load',
function(e) {
$('div#uploadProgress > div > div')
.css('width', '100%');
$('div#uploadProgress').fadeOut(
'normal',
function() {
$('div#uploadComplete').fadeIn();
}
);
}
);
}
if (typeof(FormData) != 'undefined') {
var form = new FormData();
form.append('path', '/');
for (var i = 0; i < files.length; i++) {
form.append('file[]', files[i]);
}
http.open('POST', '/file/upload.php');
http.send(form);
} else {
alert('Your browser does not support standard HTML5 Drag and Drop');
}
},
getFileSize : function(bytes)
{
switch (true) {
case (bytes < Math.pow(2,10)): {
return bytes + ' Bytes';
};
case (bytes >= Math.pow(2,10) && bytes < Math.pow(2,20)): {
return Math.round(bytes / Math.pow(2,10)) +' KB';
};
case (bytes >= Math.pow(2,20) && bytes < Math.pow(2,30)): {
return Math.round((bytes / Math.pow(2,20)) * 10) / 10 + ' MB';
};
case (bytes > Math.pow(2,30)): {
return Math.round((bytes / Math.pow(2,30)) * 100) / 100 + ' GB';
};
}
}
};
$(document).ready(dnd.ready);
</script>
<style type="text/css">
body {
font: 12px Helvetica, Arial, sans-serif;
}
div#uploadTarget {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 25px;
}
table {
width: 500px;
margin: 0 auto;
border-collapse: collapse;
border: 1px solid rgb(200, 200, 200);
background: #fff;
box-shadow: 0 7px 20px rgba(0, 0, 0, 0.6);
-webkit-box-shadow: 0 7px 20px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0 7px 20px rgba(0, 0, 0, 0.6);
}
th {
background: rgb(222, 222, 222);
border: 1px solid rgb(200, 200, 200);
text-align: left;
padding: 5px;
}
td {
padding: 5px;
border-top: 1px solid rgb(222, 222, 222);
}
tbody tr:nth-child(even) {
background: rgb(244, 244, 244);
}
tr.uploadTemplate {
display: none;
}
h1 {
font-size: 14px;
width: 500px;
margin: auto;
}
p {
margin: 4px auto 50px auto;
width: 500px;
}
div#uploadProgress {
margin: 5px auto 50px auto;
width: 500px;
}
div#uploadComplete {
margin: 5px auto 50px auto;
width: 500px;
display: none;
}
div#uploadProgress > div {
display: inline-block;
width: 393px;
height: 16px;
border: 1px solid rgb(128, 128, 128);
position: relative;
top: 5px;
left: 5px;
}
div#uploadProgress > div > div {
background: rgb(130, 171, 196);
height: 16px;
width: 0;
}
</style>
</head>
<body>
<div id="uploadTarget">
<h1>Drag and Drop File Upload Demo</h1>
<p>
Drag some files from your desktop to the browser window, if your
browser supports the updated drag and drop standard, you'll see
each file listed in the table below.
</p>
<div id='uploadProgress'>
Upload Progress: <div><div></div></div>
</div>
<div id='uploadComplete'>
Upload Complete!
</div>
<table>
<colgroup>
<col style="width: 100px;" />
<col />
<col style="width: 100px;" />
</colgroup>
<thead>
<tr>
<th>Icon</th>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody id="uploadQueue">
<tr class="uploadTemplate">
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Both implementations feature the addition of the files object upon the dataTransfer object. The simplified implementation adds a new object called FormData, which then allows you to construct a POST request without having to create the raw HTTP headers yourself.
The form.append() method allows you to define arbitrary POST variables.
Safari's implementation does allow for multiple files to be uploaded in a drag and drop, contrary to some information I've read that suggests only a single file can be dropped. And as you can see in this demo, any element can become the target for the drop, eliminating the need for a <input type="file" /> element.
Safari's implementation has some strange bugs, however. Occasionally, I've noticed in my own use of this feature that your first drop might stall and not go through. Adding a simple asynchronous request to a blank page prior to completing the upload seems to defeat that bug.
Additionally, Safari's implementation is not as complete as Chrome's or Firefox's, in that it does not yet seem to provide a way to output the progress of the upload of each file as the upload is occurring. At least my testing has yet to yield a successful implementation of a JavaScript-only progress meter. UPDATE: So it turns out Safari DOES support implementing a JavaScript-only file upload progress meter. As it happens, I wasn't doing it correctly. The demo code has been updated to include a working progress meter, which I have also tested in Firefox and Chrome, and this progress meter works in those browsers as well.
Then, yet another drawback seems to be that displaying thumbnails of images being uploaded also doesn't seem to work in Safari. Both of these are no doubt desirable features, but I, for one, am extremely pleased to be able to upload multiple files at once through drag and drop without resorting to hacks that involve either Flash or Java.
A demo of the above code is located here.
There are no comments posted at this time.
* All comments are moderated and are subject to approval.
Your comment will appear once it has been approved.
Posting multiple times will not expedite the approval process.