Upload Multiple Files With Slim Image Cropper
Slim Image Cropper is a nifty way to let users upload cropped images to your server. but it doesn’t offer an out of the box solution to upload multiple images at once. In this article we’ll setup a script that works together with Slim to setup basic multiple file upload.
If you’re not familiar with Slim you can read more about it on the Slim Image Cropper Product Page.
Alright, let’s get started.
First things first, we’ll need a place to drop multiple images. We’ll create a simple <div>
element for that. Inside the div we will add a text so the user knows where to drop the images. For this text we can use a <label>
element. If we then bind this label element to a <input>
of type file (using the for
attribute with the input id
as value), we’re good to go! Oh, if you add the multiple
attribute to the input the user can select multiple files.
This label / input combo will help us out in a couple of situations. The first advantage is this, if our JavaScript fails, we’ll still have our classic file upload.
<div class="file-drop-area">
<label for="files">Drop your files here</label>
<input name="files[]" id="files" type="file" multiple />
</div>
Alright, with the HTML setup we can now move on to add a some style. We style the label to look like the drop area. We also give it a cursor that looks like a pointer, when the user hovers over the label the browser reveals that it’s clickable.
Now for the second advantage of our label / input combo. Because we’ve bound or <label>
to our <input>
our eventual solution will work both when the user drops images on the drop area and when the user clicks the label.
.file-drop-area label {
display: block;
padding: 2em;
background: #eee;
text-align: center;
cursor: pointer;
}
On to JavaScript!
- We’ll have to handle the file
drag
anddrop
events.drag
needs to be defaultPrevented (I don’t know if that’s even a word) fordrop
to work. Also, we’ll bind thechange
event on the file input so manual file selection also works. - For each dropped or selected item we’ll have figure out if it’s an actual file and if it matches our expectations.
- Create an Image Cropper for each of the dropped files that met our expectations.
- If all our script ran successfully we can hide the file input element.
// 1. Handling the various events
// - get references to different elements we need
// - listen to drag, drop and change events
// - handle dropped and selected files
// get a reference to the file drop area and the file input
var fileDropArea = document.querySelector('.file-drop-area');
var fileInput = fileDropArea.querySelector('input');
var fileInputName = fileInput.name;
// listen to events for dragging and dropping
fileDropArea.addEventListener('dragover', handleDragOver);
fileDropArea.addEventListener('drop', handleDrop);
fileInput.addEventListener('change', handleFileSelect);
// need to block dragover to allow drop
function handleDragOver(e) {
e.preventDefault();
}
// deal with dropped items,
function handleDrop(e) {
e.preventDefault();
handleFileItems(e.dataTransfer.items || e.dataTransfer.files);
}
// handle manual selection of files using the file input
function handleFileSelect(e) {
handleFileItems(e.target.files);
}
// 2. Handle the dropped items
// - test if the item is a File or a DataTransferItem
// - do some expectation matching
// loops over a list of items and passes
// them to the next function for handling
function handleFileItems(items) {
var l = items.length;
for (var i = 0; i < l; i++) {
handleItem(items[i]);
}
}
// handles the dropped item, could be a DataTransferItem
// so we turn all items into files for easier handling
function handleItem(item) {
// get file from item
var file = item;
if (item.getAsFile && item.kind == 'file') {
file = item.getAsFile();
}
handleFile(file);
}
// now we're sure each item is a file
// the function below can test if the files match
// our expectations
function handleFile(file) {
/*
// you can check if the file fits all requirements here
// for example:
// if file is bigger then 1 MB don't load
if (file.size > 1000000) {
return;
}
*/
// if it does, create a cropper
createCropper(file);
}
// 3. Create the Image Cropper
// - create an element for the cropper to bind to
// - add the element after the drop area
// - creat the cropper and bind the remove button so it
// removes the entire cropper when clicked.
// create an Image Cropper for each passed file
function createCropper(file) {
// create container element for cropper
var cropper = document.createElement('div');
// insert this element after the file drop area
fileDropArea.parentNode.insertBefore(cropper, fileDropArea.nextSibling);
// create a Slim Cropper
Slim.create(cropper, {
ratio: '16:9',
defaultInputName: fileInputName,
didInit: function () {
// load the file to our slim cropper
this.load(file);
},
didRemove: function () {
// detach from DOM
cropper.parentNode.removeChild(cropper);
// destroy the slim cropper
this.destroy();
},
});
}
// 4. Disable the file input element
// hide file input, we can now upload with JavaScript
fileInput.style.display = 'none';
// remove file input name so it's value is
// not posted to the server
fileInput.removeAttribute('name');
To run the script either:
- Paste the script after the file drop area or just before the closing body tag;
- Run the script in a DOMContentLoaded callback;
- Run the script in a jQuery
ready()
handler;
For the above script to function you’ll have to embed Slim Image Cropper on your web page. Of course with some tweaks to the createCropper
function the above script can be used with other croppers as well.
In this example I use the global version of the script, but the createCropper
function can easily be rewritten to work with jQuery as you can see below.
Literally replace Slim.create(cropper,
with $(cropper).slim(
and you’re good to go.