skip to Main Content

On my form, there is a button to select an image file. After pressing select You will be able to select additional image files. which every time a new image file is selected The input variable will also have a new value based on the last selected image. This causes me to store all image files with the variable in JS(imageMap). But when submitting the form, I can’t send these image files from the variable in JS(imageMap).

<form id="my_form" name="my_form" method="post" enctype="multipart/form-data" action="....">
....
    <label for="img_picker">เลือกไฟล์ภาพ</label>
    <input type="file" id="img_picker" class="hidden" multiple>
    <div id="img_preview"></div>

<script>
    var imageMap = {};

    $('#img_picker').on('change', function () {
        for (const file of this.files) {
            const key = "id" + Math.random().toString(16).slice(2);
            imageMap[key] = file;

            const reader = new FileReader();
            reader.onload = (e) => {
                const img = document.createElement('img');
                img.classList.add('img-thumbnail', 'mb-2');
                img.style.width = '100px';
                img.src = e.target.result;

                const deleteButton = document.createElement('button');
                deleteButton.classList.add('btn', 'btn-danger', 'btn-sm');
                deleteButton.textContent = 'delete';
                deleteButton.onclick = () => {
                    img.parentNode.removeChild(img);
                    deleteButton.parentNode.removeChild(deleteButton);
                    delete imageMap[key];
                };

                const div = document.createElement('div');
                div.appendChild(img);
                div.appendChild(deleteButton);

                $('#img_preview').append(div);
            };
            reader.readAsDataURL(file);
        }

        this.value = null;
    });
</script>

....
    <button type="button" class="btn btn-primary sub_form">save</button>
</form>

<script>
    $(document).on('click', ".sub_form", function () {
        document.getElementById('my_form').submit();
    });
</script>

Once, I tried to create the input to store these images and I used $(#input_files).val(imageMap) But it didn’t work.

2

Answers


  1. Chosen as BEST ANSWER

    I didn't find any solutions after I tried for 8 hours but I found it after I asked in the stackoverflow for 10 minutes. This is quite strange.

    Just use the DataTransfer.

    First, add a name for file input.

    <input type="file" id="img_picker" class="hidden" name="imagePicker[]" multiple>
    

    After this, transfer files with the DataTransfer before submitting this form.

    <script>
        function getImageFromJS() {
            let data = new DataTransfer();
            for (const file of Object.values(imageMap)) {
            data.items.add(file);
            }
            document.getElementById('imagePicker').files = data.files;
        }
    
        $(document).on('click', ".sub_form", function () {
            getImageFromJS();
            document.getElementById('my_form').submit();
        });
    </script>
    

  2. Clever idea using the DataTransfer object. Apparently that is the only kind of object that implements the FileList interface and at the same time has methods for adding elements/files.

    An alternative approach could be to use the FormData object and then send the data using AJAX aka. using the fetch method (and then clear the form/list of images when the request is successful).

    var imageMap = {};
    
    document.forms.my_form.images.addEventListener('change', e => {
      for (const file of e.target.files) {
        const key = "id" + Math.random().toString(16).slice(2);
        imageMap[key] = file;
    
        const reader = new FileReader();
        reader.addEventListener('load', e => {
          const img = document.createElement('img');
          img.classList.add('img-thumbnail', 'mb-2');
          img.style.width = '100px';
          img.src = e.target.result;
    
          const deleteButton = document.createElement('button');
          deleteButton.classList.add('btn', 'btn-danger', 'btn-sm');
          deleteButton.textContent = 'delete';
          deleteButton.type = 'button';
          deleteButton.name = 'delete';
          deleteButton.dataset.key = key;
          
          const div = document.createElement('div');
          div.dataset.key = key;
          div.appendChild(img);
          div.appendChild(deleteButton);
    
          document.getElementById('img_preview').append(div);
        });
        reader.readAsDataURL(file);
      }
      e.target.value = null;
    });
    
    document.forms.my_form.addEventListener('click', e => {
      let form = e.target.form;
      switch (e.target.name) {
        case 'delete':
          form.querySelector(`div[data-key="${e.target.dataset.key}"]`).remove();
          delete imageMap[e.target.dataset.key];
          break;
      }
    });
    
    document.forms.my_form.addEventListener('submit', e => {
      e.preventDefault();
      let form = e.target;
      let data = new FormData(form);
      data.delete('images');
      for (const key of Object.keys(imageMap)) {
        data.append('images', imageMap[key]);
      }
      fetch(form.action, {method: form.method, body: data});
    });
    .hidden {
      display: none;
    }
    <form name="my_form" method="post" enctype="multipart/form-data" action="posturl">
      <label>
        <span>Select image file</span>
        <input type="file" name="images" class="hidden" multiple>
      </label>
      <div id="img_preview"></div>
      <button>Send</button>
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search