Workaround for adding multiple files without refreshing the current ones (HTML, JS)

I have a simple <input> which takes multiple files as upload. The problem is the following: for each file uploaded (in my case its images) I am also adding a <select> tag for choosing a width and height for that image, lets say for example I select 5 images at first and choose their width x height from the <select> option and I want to add another image and here comes the problem, after adding another image the <select> tag options values are refreshed back to the default value for every image added before.

Here is a snippet of my code that handles the upload and the preview:

function loadFiles(event) {
    let imagesSection = document.getElementById("images-section");
    
    for (let i = 0; i < event.files.length; i++) {
        let image = new Image();
        image.src = URL.createObjectURL(event.files[i]);

        imagesSection.innerHTML += 
        "<div class="image-frame">" +
            "<img src="" + image.src + "">" +
            "<select id="image-options-" + i + "" name="size" id="image-size" required>" +
                "<option value="" selected disabled hidden>select size</option>" +
                "<option value="" disabled>horizontal</option>" +
                "<option value="w10h15">10cm x 15cm</option>" +
                "<option value="w13h15">13cm x 18cm</option>" +
                "<option value="w20h15">20cm x 25cm</option>" +
                "<option value="w20h15">20cm x 30cm</option>" +
                "<option value="" disabled>vertical</option>" +
                "<option value="w15h10">15cm x 10cm</option>" +
                "<option value="w18h13">18cm x 13cm</option>" +
                "<option value="w25h20">25cm x 20cm</option>" +
                "<option value="w30h20">30cm x 20cm</option>" +
            "</select>" +
        "</div>";
        //console.log(event.files[i]);
    }
};
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
        <link rel="stylesheet" href="root.css">
        <title>Images</title>
    </head>

    <body>
        <div class="container">
            <div class="print-section">
                <div id="images-section" class="images-section"></div>
                
                <div class="upload-section">
                    <input id="files-input" type="file" name="files" accept="image/*" multiple onchange="loadFiles(this)" value="" />
                </div>
            </div>
        </div>
    </body>
</html>

Is there a way to make a new upload without re-uploading the previous files so that the width x height will be saved? If not my best guess is to try and add an array that will hold the options values and then add them to each image respectfully after another upload is made.

Answer

That’s one reason why you shouldn’t use imagesSection.innerHTML +=.

What happens here is the current DOM is converted to HTML, then concatenated with the string you want to add and parsed again to DOM. In that step, everything that is represented as a state in the DOM will be lost, like the values of input elements, event listeners, … .

  • new Image already creates an ImageElement that can be added to the DOM.

  • Use createElement and appendChild

  • you should also avoid using onchange="loadFiles(this)" use addEventListener instead

  • you havetwo id for your select, and ids have to be unique, using i od the for loop won’t result in an unique id.

function createSizeSelectElement(id) {
    let select = document.createElement('select')
    // name should be unique for a form
    select.name = "size"

    // you have id twice and id has to be unique
    // select.id = "image-options-" + id
    // select.id = "image-size"
    select.required = true

    // here you should consider using a loop and appendChild too
    select.innerHTML =
      "<option value="" selected disabled hidden>select size</option>" +
      "<option value="" disabled>horizontal</option>" +
      "<option value="w10h15">10cm x 15cm</option>" +
      "<option value="w13h15">13cm x 18cm</option>" +
      "<option value="w20h15">20cm x 25cm</option>" +
      "<option value="w20h15">20cm x 30cm</option>" +
      "<option value="" disabled>vertical</option>" +
      "<option value="w15h10">15cm x 10cm</option>" +
      "<option value="w18h13">18cm x 13cm</option>" +
      "<option value="w25h20">25cm x 20cm</option>" +
      "<option value="w30h20">30cm x 20cm</option>"
    return select
}

function loadFiles(event) {
  let imagesSection = document.getElementById("images-section");

  for (let i = 0; i < event.target.files.length; i++) {
    let image = new Image();
    image.src = URL.createObjectURL( event.target.files[i]);

    let info = document.createElement('div')
    info.classList.add('image-frame')

    info.appendChild(image)
    info.appendChild(createSizeSelectElement(i))
    
    imagesSection.appendChild(info)
  }
}

document.getElementById('files-input').addEventListener('change',loadFiles)
<div class="container">
  <div class="print-section">
    <div id="images-section" class="images-section"></div>

    <div class="upload-section">
      <input id="files-input" type="file" name="files" accept="image/*" multiple value="" />
    </div>
  </div>
</div>