在同级元素的单击事件中从内存中删除 Image() 对象 — JavaScript

Delete An Image() Object From Memory On Click Event Of Sibling Element — JavaScript

我有一个图像预览器,它使用 JavaScript Image() 对象在用 PHP 处理图像之前预览图像。我有一个 div,其中包含一个 'x' SVG 图形,该图形以单击事件为目标以删除图像。

在下面函数底部的代码中,它使用 evt.target 并实质上删除了每个图像所在的父元素,以便在用户希望删除图像时删除图像。

这在视觉层面上一切正常,但即使图像被删除(并且它们从 HTML 中删除),当单击表单上的 'submit' 元素进行上传时图像,任何已删除的图像仍会被处理。据我所知,图像存储在内存中并从那里进行处理。

我已经尝试将图像本身(JavaScript 中的 thumbnailElement)设置为空并将其 src 属性设置为空字符串,但这不起作用。

防止处理这些已删除的图像预览的最佳方法是什么?

在下面的代码中,我将 'x' 的 SVG 图形替换为字母 'x' 以使其更易于阅读。

注意: 我在下面展示了整个图片上传器 - 但它是 // Delete Images 下面 JS 的最后一部分,我需要帮助。

代码笔:https://codepen.io/emilychews/pen/WNjZVGZ

const dropZone = document.getElementById('drop-zone'),
    showSelectedImages = document.getElementById('show-selected-images'),
    fileUploader = document.getElementById('standard-upload-files')  

dropZone.addEventListener("click", (evt) => {
    // assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
    fileUploader.click();
});

// Prevent browser default when draging over
dropZone.addEventListener("dragover", (evt) => {
    evt.preventDefault();
});

fileUploader.addEventListener("change", (evt) => {
    // Clear the already selected images
    showSelectedImages.innerHTML = "";
    // this function is further down but declared here and shows a thumbnail of the image
    [...fileUploader.files].forEach(updateThumbnail);
});

dropZone.addEventListener("drop", (evt) => {
    evt.preventDefault();
    // Clear the already selected images
    showSelectedImages.innerHTML = "";

    // assign dropped files to the hidden input element
    if (evt.dataTransfer.files.length) {
        fileUploader.files = evt.dataTransfer.files;
    }

    // function is declared here but written further down
    [...evt.dataTransfer.files].forEach(updateThumbnail);


});

// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(file) {
    
    if (file.type.startsWith("image/")) {

        const uploadImageWrapper = document.createElement('article'),
        removeImage = document.createElement('div'),
        thumbnailElement = new Image();

        // 'x' that deletes the image
        removeImage.classList.add("remove-image");
        removeImage.innerHTML = 'x';

        // image thumbnail
        thumbnailElement.classList.add("drop-zone__thumb");
        thumbnailElement.src = URL.createObjectURL(file);

        // appending elements
        showSelectedImages.append(uploadImageWrapper)   // <article> element
        uploadImageWrapper.append(removeImage)          // 'x' to delete
        uploadImageWrapper.append(thumbnailElement);    // image thumbnail
        
        // Delete images

        removeImage.addEventListener('click', (evt) => {
            if(evt.target) {
                var deleteImage = removeImage.closest('article');
                deleteImage.remove()
            }
        })
    }

} // end of 'updateThumbnail' function
body {
  margin: 0;
  display: flex;
  justify-content: center;
  width: 100%;
}

form {
  width: 30%;
}

#drop-zone {
  border: 1px dashed;
  width: 100%;
  padding: 1rem;
  margin-bottom: 1rem;
}

.select-files {
  text-decoration: underline;
  cursor: pointer;
}

/* image that is preview prior to form submit*/
.drop-zone__thumb {
  width: 200px;
  height: auto;
  display: block;
}

#remove-x {
  width: 1rem;
  height: 1rem;
}

#submit-images {
  margin: 1rem 0;
}

#show-selected-images {
  display: flex;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
  <h1>Upload Your Images</h1>
  <div id="drop-zone" class="drop-zone flex">
    <p class="td text-center">DRAG AND DROP IMAGES HERE</p>
    <p class="td text-center" style="margin: 0">Or</p>
    <p class="tl text-center select-files text-bold pointer">Select Files</p>
  </div>
  <input id="standard-upload-files" style="display:none" style="min-width: 100%" type="file" name="standard-upload-files[]" multiple>
  <input type="submit" name="submit-images" id="submit-images" value="SUBMIT IMAGES">
  <div id="show-selected-images"></div>
</form>

这是因为提交表单时,它不会提交表单中的每个 HTML 个元素,而只会提交 inputtextarea、pressed button 的值] 和其他一些具有 name 属性的人。

所以您还需要清除文件 input 字段:

const showSelectedImages = document.getElementById("preview");
const input = document.getElementById("test");

function updateThumbnail(file) {
  if (file.type.startsWith("image/")) {

    // image and 'x' to delete wrapper 
    const uploadImageWrapper = document.createElement('article')

    // div that holds the 'x' to delete
    const removeImage = document.createElement('div')

    // image preview element
    thumbnailElement = new Image()

    // 'x' that deletes the image
    removeImage.classList.add("remove-image")
    removeImage.innerHTML = 'x'

    // image thumbnail
    thumbnailElement.classList.add("drop-zone__thumb")
    thumbnailElement.src = URL.createObjectURL(file)

    // appending elements
    showSelectedImages.append(uploadImageWrapper) // <article> element
    uploadImageWrapper.append(removeImage) // 'x' to delete
    uploadImageWrapper.append(thumbnailElement) // image thumbnail

    // Delete Images when the 'x' div is clicked
    removeImage.addEventListener('click', (evt) => {
      if (evt.target) {
        var deleteImage = removeImage.parentElement
        deleteImage.remove();

        /* for single file input is enough clear value property */

        //        input.value = null;

        /* for multiple files input we'll need recreate new files list excluding deleted file */
        const dt = new DataTransfer();
        for (let f of input.files) {
          if (f !== file)
            dt.items.add(f);
        }

        input.files = dt.files;
      }
    })
  }

}

function updateThumbnails(files) {
  showSelectedImages.innerHTML = ""; //remove all previous previews
  for (let f of files)
    updateThumbnail(f);
}

function showform(form) {
  const list = {};
  for (let i of [...new FormData(form).entries()]) {
    const key = i[0].match(/^([^\[]+)\[\]$/);
    if (key) {
      if (!list[key[1]])
        list[key[1]] = [];
      list[key[1]][list[key[1]].length] = i[1];
    } else
      list[i[0]] = i[1];
  }
  console.log(list)
  return false;
}
.remove-image {
  cursor: pointer;
}

article {
  display: inline-block;
}
<form type="post" onsubmit="return showform(this);">
  <input type="hidden" name="myHiddenInput" value="blah">
  <input type="file" name="test[]" id="test" oninput="updateThumbnails(this.files)" multiple>
  <span id="preview"></span>
  <button type="submit">submit</button>
</form>

问题:

当您 add/remove 一个项目时,您没有更新 fileUploader.files

解决方案

每次你 drop/remove 一个文件你需要更新 fileUploader 输入。第一步是创建一个函数来处理 FileList 对象并仅更改两行代码:

//--> to create a FileList
function getFileListItems (files) {
  var transferObject = new ClipboardEvent("").clipboardData || new DataTransfer()
  for (var i = 0; i<files.length; i++) transferObject.items.add(files[i])
  return transferObject.files;
}

 

在放置事件期间添加 个文件:

 //--> Updating files during drop event
 fileUploader.files = getFileListItems([...fileUploader.files, ...evt.dataTransfer.files]);

删除一个文件:

fileUploader.files = getFileListItems([...fileUploader.files].filter(f => file!==f));

查看完整的工作示例

const dropZone = document.getElementById("drop-zone"),
  showSelectedImages = document.getElementById("show-selected-images"),
  fileUploader = document.getElementById("standard-upload-files");

dropZone.addEventListener("click", (evt) => {
  // assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
  fileUploader.click();
});

// Prevent browser default when draging over
dropZone.addEventListener("dragover", (evt) => {
  evt.preventDefault();
});

fileUploader.addEventListener("change", (evt) => {
  // this function is further down but declared here and shows a thumbnail of the image
  [...fileUploader.files].forEach(updateThumbnail);
});

function getFileListItems(files) {
  var transferObject = new ClipboardEvent("").clipboardData || new DataTransfer()
  for (var i = 0; i < files.length; i++) transferObject.items.add(files[i])
  return transferObject.files;
}

dropZone.addEventListener("drop", (evt) => {
  evt.preventDefault();
  // assign dropped files to the hidden input element
  if (evt.dataTransfer.files.length) {
    fileUploader.files = getFileListItems([...fileUploader.files, ...evt.dataTransfer.files]);
  }
  // function is declared here but written further down
  [...evt.dataTransfer.files].forEach(updateThumbnail);
});

// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(file) {
  if (file.type.startsWith("image/")) {
    let uploadImageWrapper = document.createElement("article"),
      removeImage = document.createElement("div"),
      thumbnailElement = new Image();

    // 'x' that deletes the image
    removeImage.classList.add("remove-image");
    removeImage.innerHTML =
      '<svg id="remove-x" viewBox="0 0 150 150"><path fill="#000" d="M147.23,133.89a9.43,9.43,0,1,1-13.33,13.34L75,88.34,16.1,147.23A9.43,9.43,0,1,1,2.76,133.89L61.66,75,2.76,16.09A9.43,9.43,0,0,1,16.1,2.77L75,61.66,133.9,2.77a9.42,9.42,0,1,1,13.33,13.32L88.33,75Z"/></svg>';

    // image thumbnail
    thumbnailElement.classList.add("drop-zone__thumb");
    thumbnailElement.src = URL.createObjectURL(file);

    // appending elements
    showSelectedImages.append(uploadImageWrapper); // <article> element
    uploadImageWrapper.append(removeImage); // 'x' to delete
    uploadImageWrapper.append(thumbnailElement); // image thumbnail

    // Delete images
    removeImage.addEventListener("click", (evt) => {
      if (evt.target) {
        var deleteImage = removeImage.parentElement;
        deleteImage.remove();
        fileUploader.files = getFileListItems([...fileUploader.files].filter(f => file !== f));
      }
    });
  }
} // end of 'updateThumbnail' function
body {
  margin: 0;
  display: flex;
  justify-content: center;
  width: 100%;
}

form {
  width: 30%;
}

#drop-zone {
  border: 1px dashed;
  width: 100%;
  padding: 1rem;
  margin-bottom: 1rem;
}

.select-files {
  text-decoration: underline;
  cursor: pointer;
}


/* image that is preview prior to form submit*/

.drop-zone__thumb {
  width: 200px;
  height: auto;
  display: block;
}

#remove-x {
  width: 1rem;
  height: 1rem;
}

#submit-images {
  margin: 1rem 0;
}

#show-selected-images {
  display: flex;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
  <h1>Upload Your Images</h1>
  <div id="drop-zone" class="drop-zone flex">
    <p class="td text-center">DRAG AND DROP IMAGES HERE</p>
    <p class="td text-center" style="margin: 0">Or</p>
    <p class="tl text-center select-files text-bold pointer">Select Files</p>
  </div>
  <input id="standard-upload-files" style="display:none" style="min-width: 100%" type="file" name="standard-upload-files[]" multiple>
  <input type="submit" name="submit-images" id="submit-images" value="SUBMIT IMAGES">
  <div id="show-selected-images"></div>

</form>

工作示例