在同级元素的单击事件中从内存中删除 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 个元素,而只会提交 input
、textarea
、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>
工作示例
我有一个图像预览器,它使用 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 个元素,而只会提交 input
、textarea
、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>
工作示例