Home > OS >  Vanilla JS multi image upload doesn't work when dropping files, does work when selecting files
Vanilla JS multi image upload doesn't work when dropping files, does work when selecting files

Time:11-02

I am using a script based on this one to allow users to add multiple images to a form.

The form has multiple other inputs and the multi-image selection is part of it.

If I select files 'manually' using the 'click here' then the files are sent with the form submit. If the user drops them onto the 'dropzone' however, they do not get sent with the form. How can I make sure they do?

let dropBox = document.getElementById('dropBox');

['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
  dropBox.addEventListener(evt, prevDefault, false);
});

function prevDefault(e) {
  e.preventDefault();
  e.stopPropagation();
}
['dragenter', 'dragover'].forEach(evt => {
  dropBox.addEventListener(evt, hover, false);
});
['dragleave', 'drop'].forEach(evt => {
  dropBox.addEventListener(evt, unhover, false);
});

function hover(e) {
  dropBox.classList.add('!bg-lime-100', 'border-lime-300');
}

function unhover(e) {
  dropBox.classList.remove('!bg-lime-100', 'border-lime-300');
}

dropBox.addEventListener('drop', mngDrop, false);

function mngDrop(e) {
  let dataTrans = e.dataTransfer;
  let files = dataTrans.files;
  filesManager(files);
}

function filesManager(files) {
  files = [...files];
  files.forEach(previewFile);
}

function previewFile(file) {
  if (!file.type.match(/image.*/)) {
    return alert('Only JPG, JPEG and PNG files are allowed');
  }

  let fReader = new FileReader();
  let gallery = document.getElementById('gallery');

  fReader.readAsDataURL(file);

  fReader.onloadend = function() {
    let wrap = document.createElement('div');
    let img = document.createElement('img');

    img.src = fReader.result;
    img.classList.add('rounded-lg');

    let imgCapt = document.createElement('p');

    let fSize = Math.round((file.size / 1024 / 1024) * 10) / 10   ' MB';

    imgCapt.innerHTML = `<span >${fSize}</span>`;
    gallery.appendChild(wrap).appendChild(img);
    gallery.appendChild(wrap).appendChild(imgCapt);
  }
}
<form method="post" action="..." enctype="multipart/form-data">

  <input type="text" name="name">
  <input type="text" name="example">
  <input type="text" name="etc">

  <div id="dropBox" >
    <p >Drag and drop your images here, or</p>
    <input type="file" name="photos[]" id="imgUpload" multiple accept="image/*" onchange="filesManager(this.files)" >
    <label for="imgUpload" ><u>Click here</u> to select from your computer</label>
    <div id="gallery" ></div>
  </div>

</form>

I tried adding this in the previewFile function but that doesn't work:

let dropBox = document.getElementById('dropBox');
let formData = new FormData(dropBox[0]);
formData.append('photos', file);

CodePudding user response:

<input type="file"> elements are read-only inputs. Files can't be assigned to them. For security reasons user input is necessary to attach a file on the client computer to a form. The "Browse..." or "Choose file..." button provides that user input.

In the case of a drag and drop interaction create a FormData object:

const photoFormData = new FormData();

...and append() each file to it using forEach():

files.forEach((file, idx) => {
  photoFormData.append(`photos[${idx}]`, file);
}); 

The index (idx) of the file in the files array is used to differentiate each file in the FormData object.

When the user submits the form, use a submit addEventListener() to post the FormData object holding the files to the location the form is posted to using fetch():

document.querySelector('form').addEventListener('submit', function ({target}) {
  fetch(target.action, {
    method: 'put',
      body: photoFormData
  })
  .then(result => { console.log('Success:', result); })
  .catch(error => { console.error('Error:', error); });
});

Appending the files can happen in the filesManager() function:

function filesManager(files) {
  files = [...files];
  files.forEach((file, idx) => {
    photoFormData.append(`photos[${idx}]`, file);
  }); 
  files.forEach(previewFile);
}

let dropBox = document.getElementById('dropBox');
const photoFormData = new FormData();

document.querySelector('form').addEventListener('submit', function ({target}) {
        fetch(target.action, {
        method: 'put',
            body: photoFormData
  })
  .then(result => { console.log('Success:', result); })
  .catch(error => { console.error('Error:', error); });
});

['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
  dropBox.addEventListener(evt, prevDefault, false);
});

function prevDefault(e) {
  e.preventDefault();
  e.stopPropagation();
}
['dragenter', 'dragover'].forEach(evt => {
  dropBox.addEventListener(evt, hover, false);
});
['dragleave', 'drop'].forEach(evt => {
  dropBox.addEventListener(evt, unhover, false);
});

function hover(e) {
  dropBox.classList.add('!bg-lime-100', 'border-lime-300');
}

function unhover(e) {
  dropBox.classList.remove('!bg-lime-100', 'border-lime-300');
}

dropBox.addEventListener('drop', mngDrop, false);

function mngDrop(e) {
  let dataTrans = e.dataTransfer;
  let files = dataTrans.files;
  filesManager(files);
}

function filesManager(files) {
  files = [...files];
  files.forEach((file, idx) => {
    photoFormData.append(`photos[${idx}]`, file);
  }); 
  files.forEach(previewFile);
}

function previewFile(file) {
  if (!file.type.match(/image.*/)) {
    return alert('Only JPG, JPEG and PNG files are allowed');
  }

  let fReader = new FileReader();
  let gallery = document.getElementById('gallery');

  fReader.readAsDataURL(file);

  fReader.onloadend = function() {
    let wrap = document.createElement('div');
    let img = document.createElement('img');

    img.src = fReader.result;
    img.classList.add('rounded-lg');

    let imgCapt = document.createElement('p');

    let fSize = Math.round((file.size / 1024 / 1024) * 10) / 10   ' MB';

    imgCapt.innerHTML = `<span >${fSize}</span>`;
    gallery.appendChild(wrap).appendChild(img);
    gallery.appendChild(wrap).appendChild(imgCapt);
  }
}
  • Related