Skip to content

Instantly share code, notes, and snippets.

@mrosati84
Last active August 29, 2015 14:06
Show Gist options
  • Save mrosati84/b4ab536365d05cb9cf56 to your computer and use it in GitHub Desktop.
Save mrosati84/b4ab536365d05cb9cf56 to your computer and use it in GitHub Desktop.
A super-simple jQuery plugin that handles drag-to-upload files
(function(window, document, $) {
'use strict';
if ( ! String.hasOwnProperty('hashCode') ) {
/**
* returns a hash code from a base string
* @return {string}
*/
String.prototype.hashCode = function() {
var hash = 0, i, chr, len;
if (this.length === 0) return hash;
for (i = 0, len = this.length; i < len; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
}
$.fn.upload = function(options) {
/* =========================
* Plugin internal variables
======================== */
var _el = this, // the object on which you called the plugin
_rawEl = this.get(0), // the raw HTML node
_settings = $.extend({
'form': $('#form'), // the form
'trigger': $('#trigger-file'), // button to trigger file selection
'fallback': $('#fallback'), // the fallback input files
'preview': $('#preview'), // default id for the preview container
'maxUpload': 5,
'maxSize': '200000',
'mimeTypes': ['image/jpeg', 'image/png']
}, options);
/**
* bind the drag events and form handle
* @return {void}
*/
function _bindDrag() {
_el.on('dragover', function(e) {
// add 'hover' class when hovering files
_el.addClass('hover');
e.preventDefault();
return false;
});
_el.on('dragend', function() {
// remove 'hover' class when drag ends
_el.removeClass('hover');
});
_el.on('dragleave', function() {
// remove 'hover' class when drag ends
_el.removeClass('hover');
});
_el.on('drop', function(e) {
_handleFileSelection(e);
e.preventDefault();
return false;
});
// click to remove images
_settings.preview.on('click', '.preview-img', function(e) {
$(this).remove();
});
// handle form submission
_settings.form.on('submit', function(e) {
var form = $(this);
// convert images in base64 hidden uploads
$('.preview-img').each(function(index, element) {
var input = $('<input>')
.attr('type', 'hidden')
.attr('name', 'image-' + index)
.val($(element).attr('src'));
form.append(input);
});
// remove file input that triggers manual selection
_settings.trigger.remove();
});
_rawEl.ondrop = function(e) {
_el.removeClass('hover');
e.preventDefault();
return false;
};
// handle "manual" file selection
_settings.trigger.on('change', function(e) {
_handleFileSelection(e);
});
}
function _handleFileSelection(e) {
var files = (e.type === 'drop') ? e.originalEvent.dataTransfer.files : e.originalEvent.target.files,
filesCount = files.length;
// create previews
for ( var i = 0; i < filesCount; i++ ) {
var reader = new FileReader();
reader.onload = _onFileLoad;
if ( _validateMime(files[i].type ) && _validateSize(files[i].size ) ) {
reader.readAsDataURL(files[i]);
}
}
}
function _onFileLoad(e) {
// get a hash code for this image
var previewHashCode = e.target.result.hashCode();
// check number of dragged elements
if ( _settings.preview.find('img').length >= _settings.maxUpload ) {
return false;
}
// check if this image has already been dragged
if ( $('.preview-img[data-hash="'+previewHashCode+'"]').length > 0 ) {
return false;
}
// append preview images
var preview = $('<img data-hash="' + previewHashCode + '" class="preview-img">');
preview.attr('src', e.target.result);
_settings.preview.append(preview);
}
/**
* check the plugin compatibility
* @return {boolean}
*/
function _checkCompatibility() {
if ( window.hasOwnProperty('File') &&
window.hasOwnProperty('FileReader') &&
window.hasOwnProperty('FileList') ) {
return true;
}
}
/**
* validate file size
* @param {integer} size the size of the file
* @return {boolean}
*/
function _validateSize(size) {
return size <= _settings.maxSize;
}
/**
* validate file mime type
* @param {string} mime the mime type of the file
* @return {boolean}
*/
function _validateMime(mime) {
for ( var i = 0; i < _settings.mimeTypes.length; i++ ) {
if ( mime === _settings.mimeTypes[i] ) {
return true;
}
}
return false;
}
/* ==============
* Public methods
============= */
this.api = {
getRawEl: function() {
return _rawEl;
}
};
/**
* initialize the plugin
* @return {void}
*/
function _init() {
if ( _checkCompatibility() ) {
_settings.fallback.hide(); // hide legacy file input
_bindDrag(); // bind the filedrag events
}
}
// fire plugin
_init();
// preserve jQuery method chaining
return this;
};
}(window, document, jQuery));
@mrosati84
Copy link
Author

Super-simple jQuery drag to upload

Usage

This is the basic HTML code you need to work with this plugin

<!-- drop files here -->
<div id="drop"></div>

<!-- preview will appear here -->
<div id="preview"></div>

<form id="form" action="http://localhost/~mrosati/upload.php" method="post" enctype="multipart/form-data">
    <!-- files will be injected: -->
    <!-- <input type="hidden" name="file-<n>" value="data:..."> -->

    <!-- trigger file chose -->
    <label for="trigger-file">Chose files</label>
    <input id="trigger-file" type="file" name="trigger-file[]" multiple="true">

    <!-- fallback inputs for legacy browsers -->
    <div id="fallback">
        <input type="file" name="file-0">
        <input type="file" name="file-1">
        <input type="file" name="file-2">
    </div>

    <input type="submit" value="Send">
</form>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="drag_to_upload/drag_to_upload.js"></script>
<script>
    window.upload = $('#drop').upload({
        form: $('#form'), // the form
        trigger: $('#trigger-file'), // manually trigger file selection
        preview: $('#preview'), // the preview container
        maxUpload: 5, // max files
        maxSize: 500000, // size limit, in bytes
        mimeTypes: ['image/jpeg', 'image/png'] // list of supported mime types
    });
</script>

Call the plugin like this $('#drop').upload() and pass in your options, or leave it blank to use the default ones. The selector #drop points to the HTML node you will use to drop your files in. This node and the actual HTML form do not need to be related in any way, they can be completely separated.

Files can be dragged or selected in the classical way. Just set-up the trigger option and select a valid <input type="file"> to manually open the file selection dialog window.

Please note that the resulting POST request will generate n hidden inputs, where n is the number of dropped files. For each of these files the input will be populated with the base64 encoded content of the file.

Note about compatibility

This plugin works starting from Internet Explorer v. 10. For earlier versions, a fallback input will be provided. You can see the fallback legacy input in the <div id="fallback"> container.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment