Is it possible to simulate/fake the drop event using javascript only? How to test this type of event?
Take for example this dnd upload sample page , is it possible to trigger the "drop" event with a file without actually dropping a file there? Let's say clicking on a button?
I have started writing a Sukuli script that can control the mouse and do the trick but I was looking for a better solution.
EDIT
#kol answer is a good way to get rid of the drag and drop event but I still have to manually select a file from my computer. This is the bit I am interested in simulating. Is there a way to create a file variable programatically?
var fileInput = document.getElementById('fileInput'),
file = fileInput.files[0];
1. Dropping image selected by the user
I've made a jsfiddle. It's a stripped-down version of the html5demos.com page you've referred to, but:
I added an <input type="file"> tag which can be used to select an image file from the local computer, and
I also added an <input type="button"> tag with an onclick handler, which simulates the "drop file" event by directly calling the ondrop event handler of the DND-target div tag.
The ondrop handler looks like this:
holder.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files);
}
That is, we have to pass an argument to ondrop, which
has a dataTransfer field with a files array subfield, which contains the selected File, and
has a preventDefault method (a function with no body will do).
So the onclick handler of the "Simulate drop" button is the following:
function simulateDrop() {
var fileInput = document.getElementById('fileInput'),
file = fileInput.files[0];
holder.ondrop({
dataTransfer: { files: [ file ] },
preventDefault: function () {}
});
}
Test
Select an image file (png, jpeg, or gif)
Click on the "Simulate drop" button
Result
2. Dropping autogenerated test files without user interaction (GOOGLE CHROME ONLY!!!)
I've made another jsfiddle. When the page is loaded, a function gets called, which:
creates a text file into the temporary file system, and
loads and drops this text file into the target <div>; then
creates an image file into the temporary file system, and
loads and drops this image file into the target <div>.
The code of this drop-simulator function call is the following:
(function () {
var fileErrorHandler = function (e) {
var msg = "";
switch (e.code) {
case FileError.QUOTA_EXCEEDED_ERR:
msg = "QUOTA_EXCEEDED_ERR";
break;
case FileError.NOT_FOUND_ERR:
msg = "NOT_FOUND_ERR";
break;
case FileError.SECURITY_ERR:
msg = "SECURITY_ERR";
break;
case FileError.INVALID_MODIFICATION_ERR:
msg = "INVALID_MODIFICATION_ERR";
break;
case FileError.INVALID_STATE_ERR:
msg = "INVALID_STATE_ERR";
break;
default:
msg = "Unknown Error";
break;
};
console.log("Error: " + msg);
},
requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem,
dropFile = function (file) {
holder.ondrop({
dataTransfer: { files: [ file ] },
preventDefault: function () {}
});
};
if (!requestFileSystem) {
console.log("FileSystem API is not supported");
return;
}
requestFileSystem(
window.TEMPORARY,
1024 * 1024,
function (fileSystem) {
var textFile = {
name: "test.txt",
content: "hello, world",
contentType: "text/plain"
},
imageFile = {
name: "test.png",
content: "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
contentType: "image/png",
contentBytes: function () {
var byteCharacters = atob(this.content),
byteArrays = [], offset, sliceSize = 512, slice, byteNumbers, i, byteArray;
for (offset = 0; offset < byteCharacters.length; offset += sliceSize) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
for (i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return byteArrays;
}
};
// Create and drop text file
fileSystem.root.getFile(
textFile.name,
{ create: true },
function (fileEntry) {
fileEntry.createWriter(
function (fileWriter) {
fileWriter.onwriteend = function(e) {
console.log("Write completed (" + textFile.name + ")");
fileSystem.root.getFile(
textFile.name,
{},
function (fileEntry) {
fileEntry.file(
function (file) {
dropFile(file);
},
fileErrorHandler
);
},
fileErrorHandler
);
};
fileWriter.onerror = function(e) {
console.log("Write failed (" + textFile.name + "): " + e.toString());
};
fileWriter.write(new Blob([ textFile.content ], { type: textFile.contentType }));
},
fileErrorHandler
);
},
fileErrorHandler
);
// Create and drop image file
fileSystem.root.getFile(
imageFile.name,
{ create: true },
function (fileEntry) {
fileEntry.createWriter(
function (fileWriter) {
fileWriter.onwriteend = function(e) {
console.log("Write completed (" + imageFile.name + ")");
fileSystem.root.getFile(
imageFile.name,
{},
function (fileEntry) {
fileEntry.file(
function (file) {
dropFile(file);
},
fileErrorHandler
);
},
fileErrorHandler
);
};
fileWriter.onerror = function(e) {
console.log("Write failed (" + imageFile.name + "): " + e.toString());
};
fileWriter.write(new Blob(imageFile.contentBytes(), { type: imageFile.contentType }));
},
fileErrorHandler
);
},
fileErrorHandler
);
},
fileErrorHandler
);
})();
The content of the auto-generated text file is given as a string, and the content of the image file is given as a base64-encoded string. These are easy to change. For example, the test text file can contain not just plain text, but HTML too. In this case, don't forget to change the textFile.contentType field from text/plain to text/html, and to add this content type to the acceptedTypes array and to the previewfile function. The test image can also be changed easily, you just need an image-to-base64 converter.
I had to extend the drop handler code to handle text files in addition to images:
acceptedTypes = {
'text/plain': true, // <-- I added this
'image/png': true,
'image/jpeg': true,
'image/gif': true
},
...
function previewfile(file) {
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
if (file.type === 'text/plain') { // <-- I added this branch
reader.onload = function (event) {
var p = document.createElement("p");
p.innerText = event.target.result;
holder.appendChild(p);
}
reader.readAsText(file);
} else {
reader.onload = function (event) {
var image = new Image();
image.src = event.target.result;
image.width = 250; // a fake resize
holder.appendChild(image);
};
reader.readAsDataURL(file);
}
} else {
holder.innerHTML += '<p>Uploaded ' + file.name + ', ' + file.size + ' B, ' + file.type;
console.log(file);
}
}
Note that after loading the jsfiddle, the autogenerated files can be listed for debugging purposes:
Result
The screenshot shows that the simulated drop inserted the content of the autogenerated text file before the autogenerated image. The HTML code of the DND-target <div> looks like this:
<div id="holder" class="">
<p>hello, world</p>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggkFBTzlUWEwwWTRPSHdBQUFBQkpSVTVFcmtKZ2dnPT0=" width="250">
</div>
#kol answer is a good way to get rid of the drag and drop event but I
still have to manually select a file from my computer. This is the bit
I am interested in simulating. Is there a way to create a file
variable programatically? -caiocpricci2
Try this
function createFile() {
var create = ["<!doctype html><div>file</div>"];
var blob = new Blob([create], {"type" : "text/html"});
return ( blob.size > 0 ? blob : "file creation error" )
};
createFile()
Related
I upload images from one file input, but before that I display them with javascript. I want to add a function to select the primary image on click. I want to store the clicked image's name in a hidden input. My problem is I can't get the image's original name by clicking on the image itself. I found a solution but that displays every images name.
For example I select these images:
Example.jpg, Example2.png, Example3.jpg
I display them like this:
<div class="uploaded-image" data-index="1" id="image-1">
<img src="blob:http://localhost/5e75ca0e-f912-461e-98a6-9f8ad302bc84">
<button class="delete-image"><i class="iui-close"></i></button>
</div>
<div class="uploaded-image" data-index="2" id="image-2">
<img src="blob:http://localhost/5e75ca0e-f912-461e-98a6-9f831344adc84">
<button class="delete-image"><i class="iui-close"></i></button>
</div>
And when I click on image-1, I'd like to get Example.jpg to a hidden text input, instead of blob:...
The only solution I found works with a button and displays all of them.
How can modify it to select the clicked image only?
This is the code:
<input type="submit" value="Show Details" onclick="FileDetails()">
function FileDetails() {
// GET THE FILE INPUT.
var fi = document.getElementById('image-upload');
// VALIDATE OR CHECK IF ANY FILE IS SELECTED.
if (fi.files.length > 0) {
console.log(fi.files);
// RUN A LOOP TO CHECK EACH SELECTED FILE.
for (var i = 0; i <= fi.files.length - 1; i++) {
fi.files[0] = document.getElementById('image-1')
var fname = fi.files.item(i).name; // THE NAME OF THE FILE.
console.log(fname);
}
} else {
alert('Please select a file.')
}
}
Inside your plugin file there is already an event i.e : $container.on("click", function(e) { you can write your code there whenever image divs get clicked to add click image name inside input field .i.e:
$container.on("click", function(e) {
var fi = $("input[type=file]")[0];
prevent(e);
//**get files using index (0,1,2..) add value to input..**//
$("#imgs").val(fi.files.item($(this).data('index')).name)
});
Demo Code :
(function($) {
$.fn.imageUploader = function(options) {
let defaults = {
preloaded: [],
imagesInputName: 'images',
preloadedInputName: 'preloaded',
label: 'Drag & Drop files here or click to browse',
extensions: ['.jpg', '.jpeg', '.png', '.gif', '.svg'],
mimes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'],
maxSize: undefined,
maxFiles: undefined,
};
let plugin = this;
let dataTransfer = new DataTransfer();
// The file input
let $input;
// Set empty settings
plugin.settings = {};
// Plugin constructor
plugin.init = function() {
// Define settings
plugin.settings = $.extend(plugin.settings, defaults, options);
// Run through the elements
plugin.each(function(i, wrapper) {
// Create the container
let $container = createContainer();
// Append the container to the wrapper
$(wrapper).append($container);
// Set some bindings
$container.on("dragover", fileDragHover.bind($container));
$container.on("dragleave", fileDragHover.bind($container));
$container.on("drop", fileSelectHandler.bind($container));
// If there are preloaded images
if (plugin.settings.preloaded.length) {
// Change style
$container.addClass('has-files');
// Get the upload images container
let $uploadedContainer = $container.find('.uploaded');
// Set preloaded images preview
for (let i = 0; i < plugin.settings.preloaded.length; i++) {
$uploadedContainer.append(createImg(plugin.settings.preloaded[i].src, plugin.settings.preloaded[i].id, true));
}
}
});
};
let createContainer = function() {
// Create the image uploader container
let $container = $('<div>', {
class: 'image-uploader'
});
// Create the input type file and append it to the container
$input = $('<input>', {
type: 'file',
id: plugin.settings.imagesInputName + '-' + random(),
name: plugin.settings.imagesInputName + '[]',
accept: plugin.settings.extensions.join(','),
multiple: ''
}).appendTo($container);
// Create the uploaded images container and append it to the container
let $uploadedContainer = $('<div>', {
class: 'uploaded'
}).appendTo($container),
// Create the text container and append it to the container
$textContainer = $('<div>', {
class: 'upload-text'
}).appendTo($container),
// Create the icon and append it to the text container
$i = $('<i>', {
class: 'iui-cloud-upload'
}).appendTo($textContainer),
// Create the text and append it to the text container
$span = $('<span>', {
text: plugin.settings.label
}).appendTo($textContainer);
// Listen to container click and trigger input file click
$container.on('click', function(e) {
// Prevent browser default event and stop propagation
prevent(e);
// Trigger input click
$input.trigger('click');
});
// Stop propagation on input click
$input.on("click", function(e) {
e.stopPropagation();
});
// Listen to input files changed
$input.on('change', fileSelectHandler.bind($container));
return $container;
};
let prevent = function(e) {
// Prevent browser default event and stop propagation
e.preventDefault();
e.stopPropagation();
};
let createImg = function(src, id, preloaded) {
// Create the upladed image container
let $container = $('<div>', {
class: 'uploaded-image'
}),
// Create the img tag
$img = $('<img>', {
src: src
}).appendTo($container),
// Create the delete button
$button = $('<button>', {
class: 'delete-image'
}).appendTo($container),
// Create the delete icon
$i = $('<i>', {
class: 'iui-close'
}).appendTo($button);
// If the image is preloaded
if (preloaded) {
// Set a identifier
$container.attr('data-preloaded', true);
// Create the preloaded input and append it to the container
let $preloaded = $('<input>', {
type: 'hidden',
name: plugin.settings.preloadedInputName + '[]',
value: id
}).appendTo($container)
} else {
// Set the index
$container.attr('data-index', id);
}
//**** on click of image...***//
$container.on("click", function(e) {
var fi = $("input[type=file]")[0];
prevent(e);
console.log($(this).data('index'))
console.log(fi.files.item($(this).data('index')).name);
//**get files using index (0,1,2..) add value to input..**//
$("#imgs").val(fi.files.item($(this).data('index')).name)
});
// Set delete action
$button.on("click", function(e) {
// Prevent browser default event and stop propagation
prevent(e);
// Get the parent element
let $parent = $container.parent();
// If is not a preloaded image
if ($container.data('preloaded') === true) {
// Remove from preloaded array
plugin.settings.preloaded = plugin.settings.preloaded.filter(function(p) {
return p.id !== id;
});
} else {
// Get the image index
let index = parseInt($container.data('index'));
// Update other indexes
$parent.find('.uploaded-image[data-index]').each(function(i, cont) {
if (i > index) {
$(cont).attr('data-index', i - 1);
}
});
// Remove the file from input
dataTransfer.items.remove(index);
// Update input files
$input.prop('files', dataTransfer.files);
}
// Remove this image from the container
$container.remove();
// If there is no more uploaded files
if (!$parent.children().length) {
// Remove the 'has-files' class
$parent.parent().removeClass('has-files');
}
});
return $container;
};
let fileDragHover = function(e) {
// Prevent browser default event and stop propagation
prevent(e);
// Change the container style
if (e.type === "dragover") {
$(this).addClass('drag-over');
} else {
$(this).removeClass('drag-over');
}
};
let fileSelectHandler = function(e) {
// Prevent browser default event and stop propagation
prevent(e);
// Get the jQuery element instance
let $container = $(this);
// Get the files as an array of files
let files = Array.from(e.target.files || e.originalEvent.dataTransfer.files);
// Will keep only the valid files
let validFiles = [];
// Run through the files
$(files).each(function(i, file) {
// Run the validations
if (plugin.settings.extensions && !validateExtension(file)) {
return;
}
if (plugin.settings.mimes && !validateMIME(file)) {
return;
}
if (plugin.settings.maxSize && !validateMaxSize(file)) {
return;
}
if (plugin.settings.maxFiles && !validateMaxFiles(validFiles.length, file)) {
return;
}
validFiles.push(file);
});
// If there is at least one valid file
if (validFiles.length) {
// Change the container style
$container.removeClass('drag-over');
// Makes the upload
setPreview($container, validFiles);
} else {
// Update input files (it is now empty due to a default browser action)
$input.prop('files', dataTransfer.files);
}
};
let validateExtension = function(file) {
if (plugin.settings.extensions.indexOf(file.name.replace(new RegExp('^.*\\.'), '.')) < 0) {
alert(`The file "${file.name}" does not match with the accepted file extensions: "${plugin.settings.extensions.join('", "')}"`);
return false;
}
return true;
};
let validateMIME = function(file) {
if (plugin.settings.mimes.indexOf(file.type) < 0) {
alert(`The file "${file.name}" does not match with the accepted mime types: "${plugin.settings.mimes.join('", "')}"`);
return false;
}
return true;
};
let validateMaxSize = function(file) {
if (file.size > plugin.settings.maxSize) {
alert(`The file "${file.name}" exceeds the maximum size of ${plugin.settings.maxSize / 1024 / 1024}Mb`);
return false;
}
return true;
};
let validateMaxFiles = function(index, file) {
if ((index + dataTransfer.items.length + plugin.settings.preloaded.length) >= plugin.settings.maxFiles) {
alert(`The file "${file.name}" could not be added because the limit of ${plugin.settings.maxFiles} files was reached`);
return false;
}
return true;
};
let setPreview = function($container, files) {
// Add the 'has-files' class
$container.addClass('has-files');
// Get the upload images container
let $uploadedContainer = $container.find('.uploaded'),
// Get the files input
$input = $container.find('input[type="file"]');
// Run through the files
$(files).each(function(i, file) {
// Add it to data transfer
dataTransfer.items.add(file);
// Set preview
$uploadedContainer.append(createImg(URL.createObjectURL(file), dataTransfer.items.length - 1), false);
});
// Update input files
$input.prop('files', dataTransfer.files);
};
// Generate a random id
let random = function() {
return Date.now() + Math.floor((Math.random() * 100) + 1);
};
this.init();
// Return the instance
return this;
};
}(jQuery));
$('.input-images').imageUploader();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" id="imgs">
<div class="input-images"></div>
There is a previous unanswered question about this here but no code or answer was provided. I'm hoping providing some code you'll be able to help me out.
Removing any existing file from Dropzone shows dictDefaultMessage
When I load the page I'm adding mock files to the dropzone. When I click remove on one of those files, the default add image text displays in the dropzone even though there are still files present. How does dropzone keep track of the number of files in the drop zone. I've tried directly modifying the myDropzone.files.length property to match the number of mock files but it breaks the dropzone as I've said in the other question. Here is my code for dropzone.
var jsphotos = '#jsphotos';
var mockFiles = [];
Dropzone.autoDiscover = false;
var fileList = new Array;
var fileListCounter = 0;
var photoDropzone = new Dropzone('#photoDropzone', {
url: 'importphotos.cshtml',
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 5, // MB
method: 'post',
acceptedFiles: 'image/jpeg,image/pjpeg',
dictInvalidFileType: 'Files uploaded must be type .jpg or .jpeg',
init: function () {
this.on("addedfile", function (file) {
// remove size
file.previewElement.querySelector('.dz-size').innerHTML = '';
// add custom button
// Create the remove button
var removeButton = Dropzone.createElement('<i class="fa fa-times-circle-o fa-3x removeButton"></i>');
// Capture the Dropzone instance as closure.
var _this = this;
// Listen to the click event
removeButton.addEventListener("click", function (e) {
// Make sure the button click doesn't submit the form:
e.preventDefault();
e.stopPropagation();
// Remove the file preview.
_this.removeFile(file);
});
// Add the button to the file preview element.
file.previewElement.appendChild(removeButton);
});
this.on("success", function (file, serverFileName) {
file.previewElement.querySelector('.dz-filename').innerHTML = '<span data-dz-name>'+serverFileName+'</span>';
});
this.on("removedfile", function (file) {
//var rmvFile = "";
//for (f = 0; f < fileList.length; f++) {
// if (fileList[f].fileName == file.name) {
// rmvFile = fileList[f].serverFileName;
// fileListCounter--;
// }
//}
//if (rmvFile) {
// $.ajax({
// url: "deletephoto.cshtml",
// type: "POST",
// data: { "fileList": rmvFile }
// });
//}
});
}
});
$('#photoDropzone').sortable({
items: '.dz-preview',
cursor: 'move',
opacity: 0.5,
containment: "parent",
distance: 10,
tolerance: 'pointer',
sort: function (event, ui) {
var $target = $(event.target);
if (!/html|body/i.test($target.offsetParent()[0].tagName)) {
var top = event.pageY - $target.offsetParent().offset().top - (ui.helper.outerHeight(true) / 2);
ui.helper.css({ 'top': top + 'px' });
}
},
update: function (e, ui) {
// do what you want
}
});
if (jsphotos.length > 0) {
var tmpSplit = jsphotos.split(',');
for (i = 0; i < tmpSplit.length; i++) {
if (tmpSplit[i].length > 0) {
mockFiles.push(tmpSplit[i]);
}
}
}
for (i = 0; i < mockFiles.length; i++) {
// Create the mock file:
var mockFile = { name: mockFiles[i]};
// Call the default addedfile event handler
photoDropzone.emit("addedfile", mockFile);
photoDropzone.emit("success", mockFile, mockFile.name);
// And optionally show the thumbnail of the file:
//photoDropzone.emit("thumbnail", mockFile, '#Globals.tempUploadFolderURL' + mockFile.name);
// Or if the file on your server is not yet in the right
// size, you can let Dropzone download and resize it
// callback and crossOrigin are optional.
photoDropzone.createThumbnailFromUrl(mockFile, '#Globals.tempUploadFolderURL' + mockFile.name);
// Make sure that there is no progress bar, etc...
photoDropzone.emit("complete", mockFile);
// If you use the maxFiles option, make sure you adjust it to the
// correct amount:
//var existingFileCount = 1; // The number of files already uploaded
//Dropzone.options.maxFiles = myDropzone.options.maxFiles - existingFileCount;
}
//photoDropzone.files.length = mockFiles.length;
After attempting to code a solution that monitored the count manually and modified the value of the default text, I didn't want a hack to modify class names to 'fool' the dropzone into thinking there were files. So I added this
photoDropzone.files.push(mockFile);
just below
photoDropzone.emit("complete", mockFile);
and now dropzone knows how many files it has, and everything functions appropriately. Files pushed into the array do not get resubmitted, it's the same as adding the mock preview originally.
I am using dropzone.js. I want to implement the "Copy & Paste" feature in it.
What I tried is:
Inside dropzone.js:
paste: function(e) {
Dropzone.prototype.emit("paste");
}
Dropzone.prototype.paste = function(e) {
var items, _ref;
if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) {
return;
}
this.emit("paste", e);
items = e.clipboardData.items;
if (items.length) {
return this._addFilesFromItems(items);
}
};
Page level script:
<script>
var dropZone = Dropzone.forElement('#dropzone1');
dropZone.paste();
</script>
The above is not calling paste:function(e){..}
How to rectify it?
If you don't want to use other JS libraries, you can integrate dropzone with a paste event fairly easily by retrieving the data as a file from the paste event:
// create dropzone however you wish
const myDropzone = new Dropzone("div#element", { url: "/path/to/upload"});
// add paste event listener to the page
document.onpaste = function(event){
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
items.forEach((item) => {
if (item.kind === 'file') {
// adds the file to your dropzone instance
myDropzone.addFile(item.getAsFile())
}
})
}
This worked for me. It uses the FileReaderJS wrapper.
As I am not creating the dropzone programatically, I had to store it at runtime with the init() method.
See here for the FileReaderJS part.
var myDropzone;
function checkUploadFile(filename) {
//do some input checking here, if you want
return true;
}
Dropzone.options.FileDropUploadZone = {
paramName: "myDiv",
maxFilesize: 3, // MB
uploadMultiple: true,
addRemoveLinks: true,
acceptedFiles: 'image/*',
maxFiles: 10,
accept: function(file, done) {
if (!checkUploadFile(file.name)) {
done('Invalid file');
myDropzone.removeFile(file);
}
else { done(); }
},
init: function() {
myDropzone = this;
}
};
$(document).ready(function () {
FileReaderJS.setupClipboard(document.body, {
accept: {
'image/*': 'DataURL'
},
on: {
load: function(e, file) {
myDropzone.addFile(file);
}
}
});
});
var myDropzone = new Dropzone(".dropzone", { });
document.onpaste = function(event){
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (index in items) {
var item = items[index];
if (item.kind === 'file') {
// adds the file to your dropzone instance
myDropzone.addFile(item.getAsFile())
}
}
}
Just add this code. Do not declare URL because URL also declared in PHP or coding file, paste this code in view file (HTML, PHP, etc).
I tried to create my own plugin for upload files via ajax.
If the page where there is the input file is reloaded after upload It seems to work good.
If the page where there is the input file is NOT reloaded after upload (because was reloaded only ajax content) There are problems with IE and Chrome because the files to upload are appended to previous just uploaded (with firefox is ok).
I tried to fix it by cleaning the input file after the first upload but in this way then with IE and Chrome I can no longer upload other files.
MY FIX
complete: function () {
defaults.onFinish.call(this);
// If page where is the input file not reloaded
// after upload files IE and Chrome not working
$this.replaceWith($this.val('').clone(true));
$this.val('');
}
In truth I would clean the formData object after every upload but I haven't been able to do it
MY PLUGIN
;(function ($, window, document, undefined) {
// Function-level strict mode syntax
'use strict';
$.fn.ajaxUpload = function(options) {
var defaults = {
num_files : 0,
max_files : 2,
max_concurrent : 10,
max_filesize : 1024 * 4096,
php_max_size : 1024 * 8192,
allowed_types : ['jpeg','jpg'],
ajax_url : 'action.php',
var_name : 'file',
extra_fields : {},
onFinish : function() {}
};
var options = $.extend(defaults, options);
return this.each(function() {
var $this = $(this);
$this.on('change', function() {
var files = $this[0].files;
var len = files.length;
var items = 0;
var diff_files = parseInt(defaults.max_files - defaults.num_files - len);
if(diff_files < 0) {
return false;
}
if(!maxUploadFiles(len, defaults.max_concurrent)) {
return false;
}
var formdata = new FormData();
jQuery.each(files, function(i, file) {
if(!isOverSized(file, defaults.max_filesize)) {
return false;
}
if(!isAllowedTypes(file, defaults.allowed_types)) {
return false;
}
if(!totalFilesSize(file, defaults.php_max_size)) {
return false;
}
formdata.append(defaults.var_name + '['+i+']', file);
items++;
});
// Append extra data to formdata
$.each(defaults.extra_fields, function(name, value) {
formdata.append(name, value);
});
// Check that files have passed all test
if (len != items) { return false; }
$.ajax({
url: defaults.ajax_url,
data: formdata,
cache: false,
contentType: false,
processData: false,
type: 'POST',
beforeSend: function () {
},
success: function(data) {
totalSize = 0;
},
complete: function () {
defaults.onFinish.call(this);
// If page where is the input file not reloaded
// after upload files IE and Chrome not working
//$this.replaceWith($this.val('').clone(true));
//$this.val('');
}
});
});
});
};
var totalSize = 0;
function totalFilesSize(file, php_max_size) {
totalSize += file.size;
if(totalSize > php_max_size) {
totalSize = 0;
return false;
}
return true;
}
function maxUploadFiles(len, max_concurrent) {
if(len > max_concurrent) {
return false;
}
return true;
}
function isAllowedTypes(file, allowed_types) {
var ext = file.name.split('.').pop().toLowerCase();
if(jQuery.inArray(ext, allowed_types) < 0) {
return false;
}
return true;
}
function isOverSized(file, max_filesize) {
if(file.size > max_filesize) {
return false;
}
return true;
}
})(jQuery, window, document);
According to you that changes should I do to solve my problem?
Thank you
EDIT
I add this line on complete, and It seems to work
$this.val('');
$this.wrap('<form>').parent('form').trigger('reset');
$this.unwrap();
$this.replaceWith($this.clone());
The problem with your plugin is that you keep a reference to the original input with $this and then tried to replace it with a clone. Because you are cloning is better to get a new reference each time so you should unbind and bind .
(function ($, window, document, undefined) {
// Function-level strict mode syntax
'use strict';
$.fn.ajaxUpload = function (options) {
var defaults = {
num_files: 0,
max_files: 2,
max_concurrent: 10,
max_filesize: 1024 * 4096,
php_max_size: 1024 * 8192,
allowed_types: ['jpeg', 'jpg'],
ajax_url: 'action.php',
var_name: 'file',
extra_fields: {},
onFinish: function () {}
};
var options = $.extend(defaults, options);
var bindInput = function (elem) {
var element = $(elem),
bindFunc = function (evt) {
var files = evt.currentTarget.files;
var len = files.length;
var items = 0;
var diff_files = parseInt(defaults.max_files - defaults.num_files - len);
if (diff_files < 0) {
return false;
}
if (!maxUploadFiles(len, defaults.max_concurrent)) {
return false;
}
var formdata = new FormData();
jQuery.each(files, function (i, file) {
if (!isOverSized(file, defaults.max_filesize)) {
return false;
}
if (!isAllowedTypes(file, defaults.allowed_types)) {
return false;
}
if (!totalFilesSize(file, defaults.php_max_size)) {
return false;
}
formdata.append(defaults.var_name + '[' + i + ']', file);
items++;
});
// Append extra data to formdata
$.each(defaults.extra_fields, function (name, value) {
formdata.append(name, value);
});
// Check that files have passed all test
if (len != items) {
return false;
}
$.ajax({
url: defaults.ajax_url,
data: formdata,
cache: false,
contentType: false,
processData: false,
type: 'POST',
beforeSend: function () {},
success: function (data) {
totalSize = 0;
},
complete: function () {
defaults.onFinish.call(this);
var previous = $(evt.currentTarget);
previous.off('change', bindFunc);
var newElem = previous.val('').clone(true)
previous.replaceWith(newElem);
bindInput(newElem);
}
});
};
element.on('change', bindFunc);
};
return this.each(function () {
bindInput(this)
});
};
var totalSize = 0;
function totalFilesSize(file, php_max_size) {
totalSize += file.size;
if (totalSize > php_max_size) {
totalSize = 0;
return false;
}
return true;
}
function maxUploadFiles(len, max_concurrent) {
if (len > max_concurrent) {
return false;
}
return true;
}
function isAllowedTypes(file, allowed_types) {
var ext = file.name.split('.').pop().toLowerCase();
if (jQuery.inArray(ext, allowed_types) < 0) {
return false;
}
return true;
}
function isOverSized(file, max_filesize) {
if (file.size > max_filesize) {
return false;
}
return true;
}
})(jQuery, window, document);
{Edit}
The problem that originate your question is the nightmare of every file upload plugin developer. As you are developing a plugin you should be aware that the input tag may contain other styles and event handlers set by the consumer of the plugin that you must preserve or you will break existing functionality.
For security reasons the value of the input type file cannot be changed with javascript. There are a lot of answers in SO about that. Search for clear+input+file and see for yourself, the most remarkable is this Clearing <input type='file' /> using jQuery
As you can see there are basically two choices:
Clone the input and call val('') before cloning (calling jQuery $(input).val('') is not the same that calling input.value = '').
The problems of this approach is for example that in IE this event is called twice when clearing the file input and you must be carefull about releasing memory and references to the input being replaced while preserving current styles and event handlers that were not set by your plugin
The second is better but has issues as well. Wrap your input in a form tag and call the form's reset method.
input.wrap('<form>').parent('form').trigger('reset');
input.unwrap();
Check the docs about the sintax of the form tag and you will see the following quote
Note: It's strictly forbidden to nest a form inside another form. Doing so can behave in an unpredictable way that will depend on which browser the user is using.
The main reasoning behind that is that your plugin can be applied to an input tag that is already inside a form leaving you with invalid html so you must wrap the form call the reset method and remove this form right away. Also remember that forms may have visual styles applied to them breaking the user interface if you leave them around.
In the second alternative is easier to fix your code. Just change the complete callback like this. No cloning is needed in this case.
complete: function () {
defaults.onFinish.call(this);
$this.wrap('<form>').parent('form').trigger('reset');
$this.unwrap();
}
This changes should happen so fast that the users will not notice them. I tested with 1000 elements around and no visual glitches were visible.
I have a problem I am using on my website dropzone.js, but It doesnt work good for me. I have 2 problems. JSON works fine.
In existing files I havent remove button
When I upload by drag and drop it upload file twice
var FormDropzone = function () {
$(function() {
var projectID = $("#projectID").html();
var myDropzone = new Dropzone("#my-dropzone");
$.getJSON('http://'+window.location.hostname+'/project/getFile/'+projectID, function( json ) {
for (var i = 0; i < json.length; i++) {
var mockFile = { name: json[i].name, size: json[i].size };
myDropzone.emit("addedfile", mockFile);
myDropzone.emit("thumbnail", mockFile, "../../upload_files/project/1/"+json[i].name);
};
})
});
return {
//main function to initiate the module
init: function () {
Dropzone.options.myDropzone = {
init: function() {
this.on("success", function(file, serverFileName) {
FileList = {"serverFileName" : serverFileName, "fileName" : file.name };
});
this.on("addedfile", function(file) {
// Create the remove button
var removeButton = Dropzone.createElement("<button class='btn btn-sm btn-block'>Remove file</button>");
// Capture the Dropzone instance as closure.
var _this = this;
// Listen to the click event
removeButton.addEventListener("click", function(e) {
var projectID = $("#projectID").html();
// Make sure the button click doesn't submit the form:
e.preventDefault();
e.stopPropagation();
$.post( "http://"+window.location.hostname+"/project/deleteFile", { file_name: file.name, project_id: projectID, FileList : FileList["serverFileName"]});
// Remove the file preview.
_this.removeFile(file);
});
// Add the button to the file preview element.
file.previewElement.appendChild(removeButton);
});
}
}
}
};
}();
For adding remove button you have to add delete option i.e addRemoveLinks : true to the dropzone object.
just like this one :
Dropzone.options.mysample = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 1, // MB
addRemoveLinks : true,
};