Home > OS >  Move Click Event Listener To Sibling Element
Move Click Event Listener To Sibling Element

Time:10-22

I have a file uploader that shows preview images and it is currently set so if you click an image the image preview is deleted and the image is also effectively deleted from the FileList array before the form submission by creating a second array and setting the FileList to hold these values. All of this works OK.

The Context

Whilst setting this up and posting this as a question on StackOverflow in order to (theortically) keep the code simpler I removed the 'remove image' button and set it so the image was deleted both visually and from the FileList if you clicked the image itself. I'm now struggling to work out how to transfer this functionality so when the 'x' is clicked the same functionality happens.

In the current code if you click the image you get the expected behaviour.

I've now included code so that the image preview for each individual image is wrapped in a parent <figure> element with the 'x' .remove-image element included in this too. I've also included some commented out code at the bottom that if uncommented removes the <figure> element containing the image when the 'x' is clicked.

The Question

How do I get it so that instead of when the <img> is clicked the required functionality only happens when the 'x' is clicked?

I would think I need to somehow have the click event on .remove-image (the 'x') that uses the .closest method to go up to the figure element and down to the image i.e. removeImage.closest('figure').querySelector('.img') but I only want the required functionality when the 'x' is clicked, and not when either the 'x' or the image are clicked, which would be simple to fix.

Any help greatly appreciated.

Codepen: https://codepen.io/thechewy/pen/oNdrzjz

let attachFiles = document.getElementById("attach-files");
let previewWrapper = document.getElementById("show-selected-images");
let form = document.getElementById("upload-images-form");
let submitData = new DataTransfer();

attachFiles.addEventListener("change", (e) => {
  const currentSubmitData = Array.from(submitData.files);

  // For each addded file, add it to submitData if not already present
  [...e.target.files].forEach((file) => {
    if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
      submitData.items.add(file);
    }
  });

  // Sync attachFiles FileList with submitData FileList
  attachFiles.files = submitData.files;

  // Clear the previewWrapper before generating new previews
  previewWrapper.replaceChildren();

  // Generate a preview <img> for each selected file
  [...submitData.files].forEach(showFiles);
});

function showFiles(file) {
  
  let uploadImageWrapper = document.createElement("figure");
  let previewImage = new Image();
  let removeImage = `<div > X </div>`;

  // Set relevant <img> attributes
  previewImage.dataset.name = file.name;
  previewImage.classList.add("img");
  previewImage.src = URL.createObjectURL(file);

  // Adds click event listener to <img> preview (this needs moving to the .remove-image element)
  previewImage.addEventListener("click", (e) => {
    const target = e.currentTarget;
    const name = target.dataset.name;

    // Remove the clicked file from the submitData
    [...submitData.files].forEach((file, idx) => {
      if (file.name === name) {
        submitData.items.remove(idx);
      }
    });

    // Reset the attachFiles FileList
    attachFiles.files = submitData.files;

    // Remove the <img> node from the DOM
    target.remove();
    
  });

  // Append <figure> and <img> preview node to DOM
  previewWrapper.append(uploadImageWrapper); // <figure> element
  uploadImageWrapper.append(previewImage); // <img>
  uploadImageWrapper.insertAdjacentHTML('afterbegin', removeImage); // 'x' to remove the image
  
// // ===== Delete figure element that wraps the image =====
  
//   document.querySelectorAll('#show-selected-images .remove-image').forEach(i => {    
//     i.addEventListener('click', (e) => {
//       if (e.target) {
        
//         let deleteFigureEl = e.target.closest('figure');

//         // removes the image via removing it's parent element
//         deleteFigureEl.remove();

//       }
//     })
//   })

}
* {
  position: relative;
}
form {
  padding: 1rem 2rem;
  width: 50%;
  border: 1px solid;
}

input,
button {
  display: block;
  margin: 2rem 0;
}

.remove-image {
  background: #000;
  width: 30px;
  height: 30px;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  top: 10px;
  left: -10px;
  position: absolute;
  z-index: 2;
}

.img {
  width: 200px;
  height: 200px;
  object-fit: cover;
  margin: 0 1rem;
}
<form enctype="multipart/form-data" method="post" id="upload-images-form">
  <input id="attach-files" type="file" name="attach-files[]" multiple>
  <button name="submit" id="submit">SUBMIT</button>
  <div id="show-selected-images"></div>
</form>

CodePudding user response:

The two dynamic values in the click listener are the target (which is the previewImage itself) and the name (which is the same as the file.name already in scope). So, all you'd really need to do is change those variable references inside a removeImage event listener (and make removeImage an actual element, not just a string, so that .addEventListener can be called on it).

  const removeImage = document.createElement('div');
  removeImage.className = 'remove-image';
  removeImage.textContent = ' X ';
  removeImage.addEventListener('click', () => previewImage.click());

  removeImage.addEventListener("click", () => {
    const name = file.name;
    [...submitData.files].forEach((file, idx) => {
      if (file.name === name) {
        submitData.items.remove(idx);
      }
    });
    attachFiles.files = submitData.files;
    previewImage.remove();
    removeImage.remove();
  });

let attachFiles = document.getElementById("attach-files");
let previewWrapper = document.getElementById("show-selected-images");
let form = document.getElementById("upload-images-form");
let submitData = new DataTransfer();

attachFiles.addEventListener("change", (e) => {
  const currentSubmitData = Array.from(submitData.files);

  // For each addded file, add it to submitData if not already present
  [...e.target.files].forEach((file) => {
    if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
      submitData.items.add(file);
    }
  });

  // Sync attachFiles FileList with submitData FileList
  attachFiles.files = submitData.files;

  // Clear the previewWrapper before generating new previews
  previewWrapper.replaceChildren();

  // Generate a preview <img> for each selected file
  [...submitData.files].forEach(showFiles);
});

function showFiles(file) {

  let uploadImageWrapper = document.createElement("figure");
  let previewImage = new Image();
  const removeImage = document.createElement('div');
  removeImage.className = 'remove-image';
  removeImage.textContent = ' X ';
  removeImage.addEventListener('click', () => previewImage.click());

  removeImage.addEventListener("click", () => {
    const name = file.name;
    [...submitData.files].forEach((file, idx) => {
      if (file.name === name) {
        submitData.items.remove(idx);
      }
    });
    attachFiles.files = submitData.files;
    previewImage.remove();
    removeImage.remove();
  });

  // Set relevant <img> attributes
  previewImage.classList.add("img");
  previewImage.src = URL.createObjectURL(file);


  // Append <figure> and <img> preview node to DOM
  previewWrapper.append(uploadImageWrapper); // <figure> element
  uploadImageWrapper.append(previewImage); // <img>
  uploadImageWrapper.insertAdjacentElement('afterbegin', removeImage); // 'x' to remove the image
}
* {
  position: relative;
}

form {
  padding: 1rem 2rem;
  width: 50%;
  border: 1px solid;
}

input,
button {
  display: block;
  margin: 2rem 0;
}

.remove-image {
  background: #000;
  width: 30px;
  height: 30px;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  top: 10px;
  left: -10px;
  position: absolute;
  z-index: 2;
}

.img {
  width: 200px;
  height: 200px;
  object-fit: cover;
  margin: 0 1rem;
}
<form enctype="multipart/form-data" method="post" id="upload-images-form">
  <input id="attach-files" type="file" name="attach-files[]" multiple>
  <button name="submit" id="submit">SUBMIT</button>
  <div id="show-selected-images"></div>
</form>

  • Related