Skip to content

Instantly share code, notes, and snippets.

@jotaelesalinas
Created November 14, 2018 08:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jotaelesalinas/d0f86aa1692e362f349417b48ccd34ea to your computer and use it in GitHub Desktop.
Save jotaelesalinas/d0f86aa1692e362f349417b48ccd34ea to your computer and use it in GitHub Desktop.
Javascript script for easy drag and drop on an HTML element
/*
* Configures an element to receive files via drag'n'drop.
* - el: drop element, either id string or HTMLElement object.
* - cb_ok: callback, accepts: contents or data returned by preprocessor, file and index; called once per successfully preprocessed file.
* - cb_fail: callback, accepts error, file and index; called once per failed file.
* - preprocessor: function that accepts the contents of the file and can return any data; accepts: contents, file, index; called once per dropped file.
* - cb_start: callback, called before preprocessing all files, accepts the files, can throw, e.g. if wrong filetypes
* - cb_finished: callback, called after handling all files, no matter is failed or not
* - css: CSS styles to be applied to the drop element
*/
var dragndrop = function (el, cb_ok, cb_fail, options) {
var defaults = {
preprocessor: function (file, content) { return content; },
cb_start: function () {},
cb_finished: function () {},
css: null
};
var options = Object.assign({}, defaults, options);
var prev_styles = false;
/* What to do when dragging over the element -- change the cursor
*/
var handleDragOver = function (evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
};
/* What to do when the files are dropped
*/
var handleFileSelect = function (evt) {
evt.stopPropagation();
evt.preventDefault();
startProcessingFiles(evt.dataTransfer.files);
};
/* Processes the list of dropped files.
* - creates a list of pending files
* - runs the handler for each file
*/
var startProcessingFiles = function (filelist) {
files = Array.from(filelist);
pending_files = {};
try {
options.cb_start(files);
} catch (err) {
console.log('ERROR in cb_start():', err);
throw err;
}
files.forEach(f => pending_files[f.name] = true);
files.forEach((f, idx) => processFile(f, idx, options.preprocessor, cb_ok, cb_fail, whenFinished));
};
/* Runs cb_finished after the last file is handled.
* This function is called after each file is habdled.
*/
var whenFinished = function (file) {
pending_files[file.name] = false;
for ( var idx in pending_files ) {
if ( pending_files[idx] ) {
// there are still pending files
return;
}
}
files = null;
pending_files = null;
options.cb_finished();
};
/* Reads a file using the borwser's File API.
* file: File from File API
* cb_ok: callback, accepts file and content
* cb_fail: callback, accepts file and event
*/
var readFile = function (file, cb_ok, cb_fail) {
var fr = new window.FileReader();
fr.onload = function (evt) {
cb_ok(file, evt.target.result);
};
fr.onerror = function (evt) {
cb_fail(file, evt);
};
//fr.readAsText(file);
fr.readAsBinaryString(file);
};
/* Process a file using a provided handling function
* - file: File from File API
* - handler: function that accepts the binary content of the file and returns anything
* - cb_ok: callback, accepts file and content
* - cb_fail: callback, accepts file and event
* - cb_always: callback, called after handling, no matter is failed or not
*/
var processFile = function (file, idx, handler, cb_ok, cb_fail, cb_always) {
readFile(file, function (file, contents) {
var text;
try {
text = handler(contents, file, idx);
cb_ok(text, file, idx);
} catch (err) {
cb_fail(err, file, idx);
}
cb_always(file, idx);
}, function (file) {
cb_always(file, idx);
});
};
/* Setup the listeners for the drag'n'drop
*/
var drop_element = typeof el === 'string' ? document.getElementById(el) : el;
drop_element.addEventListener('dragover', handleDragOver, false);
drop_element.addEventListener('drop', handleFileSelect, false);
if ( options.css ) {
for ( var idx in options.css ) {
drop_element.style[idx] = options.css[idx];
}
}
};
<!doctype html>
<html>
<head>
<title>dragndrop test</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
font-family: 'segoe ui', calibri, arial, sans-serif;
font-size: 15px;
}
body {
margin: 2em;
}
#drop-zone {
margin: 1em;
margin-bottom: 0;
padding: 2em;
text-align: center;
font-size: 200%;
font-weight: bold;
font-family: serif;
}
.use-mozilla {
color: crimson;
}
@media print {
#drop-zone {
display:none;
}
}
#output {
margin: 2em;
padding: 2em;
background-color: darkslategray;
color: whitesmoke;
}
.test {
margin: 2em;
margin-bottom: 0;
padding: 0;
border: 2px solid CornflowerBlue;
}
.test .name {
margin: 0;
background-color: CornflowerBlue;
color: white;
padding: 15px;
font-weight: normal;
}
.test .qas {
margin: 0;
padding: 1em 2em;
}
.test .qa {
margin: 0;
margin-bottom: 1em;
}
.test .qa.noanswer {
display: none;
}
.test .q {
margin: 0;
}
.test .a {
margin: 0;
}
.test .a:before {
content: " \2192\00a0";
}
</style>
</head>
<body>
<h1>dragndrop test</h1>
<div id="drop-zone">
Drop one or more files here.
<br>
Use Mozilla Firefox or Google Chrome. It does NOT work with Internet Explorer.
</div>
<pre id="output"></pre>
<script src='dragndrop.js'></script>
<script>
var console2 = console;
var console = {
out: document.getElementById('output'),
log: function () {
console2.log(...arguments);
this.out.innerHTML += [...arguments].join(", ") + "\n";
}
};
var fileOk = function (data, file, index) {
console.log('fileOk()');
console.log('data', data);
console.log('file name', file.name);
console.log('index', index);
};
var fileErr = function (err, file, index) {
console.log('fileErr()');
console.log('err', err);
console.log('file name', file.name);
console.log('index', index);
};
var onStart = function (files) {
console.log('onStart()');
console.log('files', files);
};
var preprocess = function (contents, file, index) {
console.log('preprocess()');
console.log('contents.length', contents.length);
console.log('file name', file.name);
console.log('index', index);
if ( index == 1 ) {
throw "Super critical error!";
}
return file.name + ' (' + contents.length + ')';
}
var onFinished = function () {
console.log('onFinished()');
console.log('Done!');
};
var css = {
"border": 'thick dashed',
"border-radius": '.25em',
"border-color": 'orangered'
};
dragndrop('drop-zone', fileOk, fileErr, {
preprocessor: preprocess,
cb_start: onStart,
cb_finished: onFinished,
css});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment