Как реализовать поочерёдную загрузку изображений и их предпросмотр, и возможность удаления по кнопке?
Собственно, у меня есть сгенерированный код js для добавления в форму изображений пользователем и их предпросмотра. Но глобально есть две проблемы.
1) При удалении изображений по кнопке происходит смена индексов (то ли у файлов в input, то ли у кнопок внутри div`a class="image-preview") в связи с этим если удалять начиная с конца к началу по порядку, то всё отлично работает, но если делать с начала в конец или произвольно, то всегда остаётся 1-2 файла в input'е, хотя все изображения из предпросмотра удалены.
пример некорректной работы
Показывает что файла два, а изображение осталось одно.
2) Уже менее критично, но всё же хочу реализовать возможность поочерёдного добавления изображений, а то в текущем состоянии при добавлении новой картинки с помощью input старые файлы автоматически очищаются, а мне это не нужно, ведь для очищения у юзера будут специальные кнопки возле каждого изображения в контейнере предпросмотра. Очевидно нужно убрать очищение сгенерированного innerHtml внутри js функции, но непонятно как сделать так чтобы сохранялись не только картинки в контейнере предпросмотра, но и чтобы фактически файлы внутри input`a тоже сохранялись.
function checkFileCount(input) { if (input.files.length > 5) { alert("Вы можете выбрать не более пяти файлов."); //хочу чтобы пользователь не мог загрузить суммарно больше 5 файлов input.value = ''; // очищаем значение поля input, чтобы пользователь мог выбрать файлы заново } else { previewImages(input); //вызываю функцию отвечающую за предпоказ } } function previewImages(input) { var files = input.files; var preview = document.querySelector('.image-preview'); //выбираем контейнер где будут отображаться картинки preview.innerHTML = ''; //очищение этого контейнера от изображений загруженных в предыдущий раз <b>//эту строку нужно будет закомментировать чтобы реализовать многократное добавление без очищения содержимого input, // но не всё так просто, проблема в том что закомментировав её старые изображения в предпросмотре будут сохраняться, // но в input эти старые изображения будут удалены (в пункте 2 говорил про это)</b> for (var i = 0; i < files.length; i++) { //цикл для чтения изображений var file = files[i]; var reader = new FileReader(); reader.onload = (function(file, index) { return function(e) { var image = document.createElement('img'); //реализуем предпросмотр выбранных изображений image.src = e.target.result; image.classList.add('preview-image'); preview.appendChild(image); var removeButton = document.createElement('button'); //кнопка удаления для каждого изображения removeButton.textContent = 'X'; removeButton.classList.add('remove-button'); removeButton.onclick = function() { //добавляем функцию чтобы при нажатии на кнопку удалялись изображения из предпросмотра и из файлов input'a preview.removeChild(image); preview.removeChild(removeButton); // Удаляем файл из выбранных файлов input.files = removeFile(input.files, index); }; preview.appendChild(removeButton); }; })(file, i); reader.readAsDataURL(file); } } <b>// Функция для удаления файла из списка файлов по индексу. //В ней как раз таки зарыта собака, потому что после удаления одного из элементов // индексы других меняются и некоторые дальнейшие удаления происходят некорректно, // хотя если удалять начиная с конца, то всё работает корректно (в пункте 1 писал об этом)</b> function removeFile(files, index) { var newFiles = new DataTransfer(); for (var i = 0; i < files.length; i++) { if (i !== index) { newFiles.items.add(files[i]); } } return newFiles.files; } |
function checkFileCount(input) { if (input.files.length > 5) { alert("Вы можете выбрать не более пяти файлов."); //хочу чтобы пользователь не мог загрузить суммарно больше 5 файлов input.value = ''; // очищаем значение поля input, чтобы пользователь мог выбрать файлы заново } else { previewImages(input); //вызываю функцию отвечающую за предпоказ } } function previewImages(input) { var files = input.files; var preview = document.querySelector('.image-preview'); //выбираем контейнер где будут отображаться картинки preview.innerHTML = ''; //очищение этого контейнера от изображений загруженных в предыдущий раз <b>//эту строку нужно будет закомментировать чтобы реализовать многократное добавление без очищения содержимого input, // но не всё так просто, проблема в том что закомментировав её старые изображения в предпросмотре будут сохраняться, // но в input эти старые изображения будут удалены (в пункте 2 говорил про это)</b> for (var i = 0; i < files.length; i++) { //цикл для чтения изображений var file = files[i]; var reader = new FileReader(); reader.onload = (function(file, index) { return function(e) { var image = document.createElement('img'); //реализуем предпросмотр выбранных изображений image.src = e.target.result; image.classList.add('preview-image'); preview.appendChild(image); var removeButton = document.createElement('button'); //кнопка удаления для каждого изображения removeButton.textContent = 'X'; removeButton.classList.add('remove-button'); removeButton.onclick = function() { //добавляем функцию чтобы при нажатии на кнопку удалялись изображения из предпросмотра и из файлов input'a preview.removeChild(image); preview.removeChild(removeButton); // Удаляем файл из выбранных файлов input.files = removeFile(input.files, index); }; preview.appendChild(removeButton); }; })(file, i); reader.readAsDataURL(file); } } <b>// Функция для удаления файла из списка файлов по индексу. //В ней как раз таки зарыта собака, потому что после удаления одного из элементов // индексы других меняются и некоторые дальнейшие удаления происходят некорректно, // хотя если удалять начиная с конца, то всё работает корректно (в пункте 1 писал об этом)</b> function removeFile(files, index) { var newFiles = new DataTransfer(); for (var i = 0; i < files.length; i++) { if (i !== index) { newFiles.items.add(files[i]); } } return newFiles.files; }
<div class="cont"> <label class="filter" for="images">Додати фотографію</label> <input type="file" id="images" name="images" accept="image/*" onchange="checkFileCount(this)" multiple required> </div> |
<div class="cont"> <label class="filter" for="images">Додати фотографію</label> <input type="file" id="images" name="images" accept="image/*" onchange="checkFileCount(this)" multiple required> </div>
Исходный html код
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="css/styles2.css"> <title>Add bus form</title> </head> <body> <header> <ul class="menu"> <div class="leftPart"><li id="findPet"><a href="/pets">Знайти тварину</a></li> <li><a href="/rules">Правила адопції</a></li> <li><a href="/help">Допомогти</a></li> <li><a href="/aboutProject">Про проєкт</a></li> </div> <div class="rightPart"><li id="createAdd"><a href="/myProfile">Додати оголошення</a></li> <li id="login"><a href="/authorization">Профіль</a></li> </div> </ul> </header> <div class="center-global-container"> <div class="errorMessage">${errorMessage}</div> <div class="filters"> <form class="addForm" action="/cats/add" method="post"> </div> <div class="cont"> <label class="filter" for="images">Додати фотографію</label> <input type="file" id="images" name="images" accept="image/*" onchange="checkFileCount(this)" multiple required> </div> <div class="image-preview"></div> <button type="submit" name="sumbit" class="sumbit-form">Опублікувати оголошення</button> </form> </div> </div> </body> </html> |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="css/styles2.css"> <title>Add bus form</title> </head> <body> <header> <ul class="menu"> <div class="leftPart"><li id="findPet"><a href="/pets">Знайти тварину</a></li> <li><a href="/rules">Правила адопції</a></li> <li><a href="/help">Допомогти</a></li> <li><a href="/aboutProject">Про проєкт</a></li> </div> <div class="rightPart"><li id="createAdd"><a href="/myProfile">Додати оголошення</a></li> <li id="login"><a href="/authorization">Профіль</a></li> </div> </ul> </header> <div class="center-global-container"> <div class="errorMessage">${errorMessage}</div> <div class="filters"> <form class="addForm" action="/cats/add" method="post"> </div> <div class="cont"> <label class="filter" for="images">Додати фотографію</label> <input type="file" id="images" name="images" accept="image/*" onchange="checkFileCount(this)" multiple required> </div> <div class="image-preview"></div> <button type="submit" name="sumbit" class="sumbit-form">Опублікувати оголошення</button> </form> </div> </div> </body> </html>
/*Обнуление*/ *, // Половину стилей пришлось удалить из-за ограничения по объему, но вроде все важные есть *:before, *:after{ padding: 0; margin: 0; border: 0; box-sizing: border-box; } .image-preview { display: flex; } .image-preview img { width: 100px; height: 100px; object-fit: cover; /* чтобы изображение не растягивалось и не сжималось, сохраняя соотношение сторон */ margin: 5px; } .remove-button { margin-left: -20px; height: 15px; width: 15px; margin-top: 5px; display: block; color: red; } .remove-button:hover { cursor: pointer; background-color: pink; } .filters { background-color: #788AB2; padding: 10px 20px; width: 590px; height: 10%; box-sizing: border-box; font-size: 20px; font-family: Tahoma; color: white; display: flex; flex-direction: column; } .filters label, .filters select, .filters option { flex: 1; display: inline-block; margin-bottom: 12px; line-height: 26px; } .filters label { margin-top: 12px } .filters p{ align-self: center; justify-self: center; font-size: 24px; margin-bottom: 10px; } .cont { display: flex; align-items: center; justify-content: space-between; } .filters select { position: relative; display: block; flex: 3.1; margin: 5px 0; font-family: 'Open Sans', 'Helvetica Neue', 'Segoe UI', 'Calibri', 'Arial', sans-serif; font-size: 18px; color: #60666d; flex-basis: 50px; } .center-global-container{ display: flex; justify-content: center; align-items: center; flex-direction: column; } .addForm-container { width: 32.5%; min-width: 325px; display: flex; flex-direction: column; } .addForm { width: 100%; display: flex; flex-direction: column; } .errorMessage{ margin-top: 0px; margin-bottom: 10px; font-size: 1.25rem; color: red; } |
/*Обнуление*/ *, // Половину стилей пришлось удалить из-за ограничения по объему, но вроде все важные есть *:before, *:after{ padding: 0; margin: 0; border: 0; box-sizing: border-box; } .image-preview { display: flex; } .image-preview img { width: 100px; height: 100px; object-fit: cover; /* чтобы изображение не растягивалось и не сжималось, сохраняя соотношение сторон */ margin: 5px; } .remove-button { margin-left: -20px; height: 15px; width: 15px; margin-top: 5px; display: block; color: red; } .remove-button:hover { cursor: pointer; background-color: pink; } .filters { background-color: #788AB2; padding: 10px 20px; width: 590px; height: 10%; box-sizing: border-box; font-size: 20px; font-family: Tahoma; color: white; display: flex; flex-direction: column; } .filters label, .filters select, .filters option { flex: 1; display: inline-block; margin-bottom: 12px; line-height: 26px; } .filters label { margin-top: 12px } .filters p{ align-self: center; justify-self: center; font-size: 24px; margin-bottom: 10px; } .cont { display: flex; align-items: center; justify-content: space-between; } .filters select { position: relative; display: block; flex: 3.1; margin: 5px 0; font-family: 'Open Sans', 'Helvetica Neue', 'Segoe UI', 'Calibri', 'Arial', sans-serif; font-size: 18px; color: #60666d; flex-basis: 50px; } .center-global-container{ display: flex; justify-content: center; align-items: center; flex-direction: column; } .addForm-container { width: 32.5%; min-width: 325px; display: flex; flex-direction: column; } .addForm { width: 100%; display: flex; flex-direction: column; } .errorMessage{ margin-top: 0px; margin-bottom: 10px; font-size: 1.25rem; color: red; }
Дополнительно:
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос


Для реализации поочередной загрузки изображений, их предпросмотра и возможности удаления по кнопке вам понадобится использовать HTML, CSS и JavaScript, а также серверный язык программирования, например PHP, для обработки загруженных изображений.
Вот пример кода, демонстрирующий данную функциональность:
```html
#images-preview {
display: flex;
flex-wrap: wrap;
}
.image-preview {
margin: 10px;
}
const imageInput = document.getElementById('image-input');
const deleteButton = document.getElementById('delete-button');
const imagesPreview = document.getElementById('images-preview');
imageInput.addEventListener('change', function() {
const files = imageInput.files;
for(let i = 0; i < files.length; i++) {
const file = files[i];
const reader = new FileReader();
reader.onload = function(e) {
const imagePreview = document.createElement('img');
imagePreview.src = e.target.result;
imagePreview.className = 'image-preview';
imagesPreview.appendChild(imagePreview);
}
reader.readAsDataURL(file);
}
});
deleteButton.addEventListener('click', function() {
const imagePreviews = document.getElementsByClassName('image-preview');
while(imagePreviews[0]) {
imagePreviews[0].parentNode.removeChild(imagePreviews[0]);
}
});
```
Этот код позволяет загружать изображения, предпросматривать их на странице и удалять их по кнопке. При выборе изображений через input file они поочередно отображаются на странице в виде миниатюр, которые можно удалить, нажав на кнопку "Удалить".
Помимо фронтенд-части, вам также потребуется написать серверный код на PHP для обработки загруженных изображений и их сохранения на сервере. В этом случае после выбора изображений пользователем, вы можете отправить их на сервер с помощью AJAX запроса и сохранить их на сервере.
Надеюсь, данное решение поможет вам реализовать функциональность поочередной загрузки изображений с их предпросмотром и возможностью удаления.