Стилизация Input File
Сегодня будем стилизовать input type file. Т.к. стилизация тесно связана с обработкой input file на фронтенде, мы так же рассмотрим, как обработать выбранный файл и отправить на сервер. Скрипт будет работать для множества форм с возможностью не только загрузить файл при клике на кнопку, но так же перетаскиванием файла (drag-and-drop) в область загрузки. Если вам не нужен тот или иной функционал, вы легко сможете удалить часть кода.
Содержание:
- кнопка «Прикрепить файл»
- drag-and-drop загрузка файлов
- совместное использование кнопки «Прикрепить файл» и Drag-and-drop
- отправка множества input file multiple (PHP)
1. Стилизация input type file
1.1 Кнопка «Прикрепить файл» - HTML, CSS
Сначала нам необходимо создать html-разметку.
- multiple - данный атрибут необходим, если вы хотите разрешить отправку более одного файла
- accept - в данный атрибут запишите типы файлов (MIME-типы), которые разрешены для выбора
<form>
<input type="text" name="Ваше имя">
<input type="email" name="E-mail">
<div class="upload-file__wrapper">
<input
type="file"
name="files[]"
id="upload-file__input_1"
class="upload-file__input"
accept=".jpg, .jpeg, .png, .gif, .bmp, .doc, .docx, .xls, .xlsx, .txt, .tar, .zip, .7z, .7zip"
multiple
>
<label class="upload-file__label" for="upload-file__input_1">
<svg class="upload-file__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M286 384h-80c-14.2 1-23-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c11.6 11.6 3.7 33.1-13.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-23-23V366c0-13.3 10.7-24 24-24h136v8c0 31 24.3 56 56 56h80c30.9 0 55-26.1 57-55v-8h135c13.3 0 24 10.6 24 24zm-124 88c0-11-9-20-19-20s-19 9-20 20 9 19 20 20 21-9 20-20zm64 0c0-12-9-20-20-20s-20 9-19 20 9 20 20 20 21-9 20-20z">
</path>
</svg>
<span class="upload-file__text">Прикрепить файл</span>
</label>
</div>
<button type="submit">Отправить</button>
</form>
Скроем input и стилизуем кнопку для загрузки файла.
.upload-file__input {
opacity: 0;
position: absolute;
z-index: -1;
overflow: hidden;
width: 0.4px;
height: 0.4px;
}
.upload-file__label {
display: flex;
justify-content: center;
align-items: center;
max-width: 240px;
border: 2px dashed #150B22;
padding: 9px 49px;
border-radius: 6px;
cursor: pointer;
}
.upload-file__icon {
width: 30px;
height: auto;
margin-right: 11px;
}
.upload-file__label .upload-file__text,
.upload-file__label .upload-file__icon path {
transition: .25s ease;
}
.upload-file__label:hover .upload-file__text {
color: #58862D;
}
.upload-file__label:hover .upload-file__icon path {
fill: #58862D;
}
.upload-file__label:hover {
border: 2px dashed #58862D;
}
SASS
.upload-file__input
opacity: 0
position: absolute
z-index: -1
overflow: hidden
width: 0.4px
height: 0.4px
.upload-file__label
display: flex
justify-content: center
align-items: center
max-width: 240px
border: 2px dashed #150B22
padding: 9px 49px
border-radius: 6px
cursor: pointer
.upload-file__icon
width: 30px
height: auto
margin-right: 11px
.upload-file__label
.upload-file__text, .upload-file__icon path
transition: .25s ease
&:hover
border: 2px dashed #58862D
.upload-file__text
color: #58862D
.upload-file__icon path
fill: #58862D
1.2 Кнопка «Прикрепить файл» - JavaScript
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form');
const inputFile = document.querySelectorAll('.upload-file__input');
/////////// Кнопка «Прикрепить файл» ///////////
inputFile.forEach(function(el) {
let textSelector = document.querySelector('.upload-file__text');
let fileList;
// Событие выбора файла(ов)
el.addEventListener('change', function (e) {
// создаём массив файлов
fileList = [];
for (let i = 0; i < el.files.length; i++) {
fileList.push(el.files[i]);
}
// вызов функции для каждого файла
fileList.forEach(file => {
uploadFile(file);
});
});
// Проверяем размер файлов и выводим название
const uploadFile = (file) => {
// файла <5 Мб
if (file.size > 5 * 1024 * 1024) {
alert('Файл должен быть не более 5 МБ.');
return;
}
// Показ загружаемых файлов
if (file && file.length > 1) {
if ( file.length <= 4 ) {
textSelector.textContent = `Выбрано ${file.length} файла`;
}
if ( file.length > 4 ) {
textSelector.textContent = `Выбрано ${file.length} файлов`;
}
} else {
textSelector.textContent = file.name;
}
}
});
// Отправка формы на сервер
const postData = async (url, fData) => { // имеет асинхронные операции
// начало отправки
// здесь можно оповестить пользователя о начале отправки
// ждём ответ, только тогда наш код пойдёт дальше
let fetchResponse = await fetch(url, {
method: 'POST',
body: fData
});
// ждём окончания операции
return await fetchResponse.text();
};
if (forms) {
forms.forEach(el => {
el.addEventListener('submit', function (e) {
e.preventDefault();
// создание объекта FormData
let fData = new FormData();
// Добавление всех input, кроме type="file"
el.querySelectorAll('input:not([type="file"])').forEach(input => {
fData.append(input.name, input.value);
});
// Добавление файлов input type file
let file = el.querySelector('.upload-file__input');
for (let i = 0; i < (file.files.length); i++) {
fData.append('files[]', file.files[i]); // добавляем файлы в объект FormData()
}
// Отправка на сервер
postData('./mail.php', fData)
.then(fetchResponse => {
console.log('Данные успешно отправлены!');
console.log(fetchResponse);
})
.catch(function (error) {
console.log('Ошибка!');
console.log(error);
});
});
});
};
});
2. Drag-and-drop загрузка файлов
Структура останется прежней, т.к. наша первоначальная разметка вполне подойдёт для создания drag-and-drop области.
2.1 Drag-and-drop - HTML, CSS
<form>
<input type="text" name="Ваше имя">
<input type="email" name="E-mail">
<div class="upload-file__wrapper">
<input type="file" name="files" id="upload-file__input" class="upload-file__input" multiple accept="image/jpeg,image/png,image/gif">
<label class="upload-file__label" for="upload-file__input">
<svg class="upload-file__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M286 384h-80c-14.2 1-23-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c11.6 11.6 3.7 33.1-13.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-23-23V366c0-13.3 10.7-24 24-24h136v8c0 31 24.3 56 56 56h80c30.9 0 55-26.1 57-55v-8h135c13.3 0 24 10.6 24 24zm-124 88c0-11-9-20-19-20s-19 9-20 20 9 19 20 20 21-9 20-20zm64 0c0-12-9-20-20-20s-20 9-19 20 9 20 20 20 21-9 20-20z">
</path>
</svg>
<span class="upload-file__text">Прикрепить файл</span>
</label>
</div>
<button type="submit">Отправить</button>
</form>
Улучшим юсабилити путём добавления стилей для drag-and-drop.
/* drag and drop - "hover" */
.upload-file__label.hover .upload-file__text {
color: #ffb300;
}
.upload-file__label.hover .upload-file__icon path {
fill: #ffb300;
}
.upload-file__label.hover {
border: 2px dashed #ffb300;
}
/* drag and drop - ошибка */
.upload-file__label.error .upload-file__text {
color: #d32f2f;
}
.upload-file__label.error .upload-file__icon path {
fill: #d32f2f;
}
.upload-file__label.error {
border: 2px dashed #d32f2f;
}
/* drag and drop - файл(ы) успешно перетянут(ы) */
.upload-file__label.drop .upload-file__text {
color: #388e3c;
}
.upload-file__label.drop .upload-file__icon path {
fill: #388e3c;
}
.upload-file__label.drop {
border: 2px dashed #388e3c;
}
SASS
.upload-file__label
&.hover
border: 2px dashed #ffb300
.upload-file__text
color: #ffb300
.upload-file__icon
path
fill: #ffb300
&.error
border: 2px dashed #d32f2f
.upload-file__text
color: #d32f2f
.upload-file__icon
path
fill: #d32f2f
&.drop
border: 2px dashed #388e3c
.upload-file__text
color: #388e3c
.upload-file__icon
path
fill: #388e3c
2.2 Drag-and-drop загрузка файлов - JavaScript
Теперь напишем JavaScript для обработки событий перетаскивания файлов на веб-страницу. А уже в следующем пункте рассмотрим, как использовать кнопку добавления файла и область Drag-and-drop одновременно.
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form');
/////////// Загрузка файлов при помощи «Drag-and-drop» ///////////
const dropZone = document.querySelector('.upload-file__label');
const dropZoneText = document.querySelector('.upload-file__text');
const maxFileSize = 5000000; // максимальный размер файла - 5 мб.
let uploadDragFile = '';
// Проверка поддержки «Drag-and-drop»
if (typeof (window.FileReader) == 'undefined') {
dropZone.textContent = 'Drag&Drop Не поддерживается браузером!';
dropZone.classList.add('error');
}
// Событие - перетаскивания файла
dropZone.ondragover = function () {
this.classList.add('hover');
return false;
};
dropZone.ondragleave = function () {
this.classList.remove('hover');
return false;
};
let uploadDragFiles = '';
dropZone.ondrop = function (e) { // Событие - файл перетащили
e.preventDefault();
this.classList.remove('hover');
this.classList.add('drop');
uploadDragFiles = e.dataTransfer.files;
// Проверка размера файла
if (uploadDragFiles[0].size > maxFileSize) {
dropZoneText.textContent = 'Размер превышает допустимое значение!';
this.addClass('error');
return false;
}
// Показ загружаемых файлов
if (uploadDragFiles.length > 1) {
if (uploadDragFiles.length <= 4) {
dropZoneText.textContent = `Выбрано ${uploadDragFiles.length} файла`;
} else {
dropZoneText.textContent = `Выбрано ${uploadDragFiles.length} файлов`;
}
} else {
dropZoneText.textContent = e.dataTransfer.files[0].name;
}
}
// Отправка формы на сервер
const postData = async (url, fData) => { // имеет асинхронные операции
// начало отправки
// здесь можно оповестить пользователя о начале отправки
// ждём ответ, только тогда наш код пойдёт дальше
let fetchResponse = await fetch(url, {
method: 'POST',
body: fData
});
// ждём окончания операции
return await fetchResponse.text();
};
if (forms) {
forms.forEach(el => {
el.addEventListener('submit', function (e) {
e.preventDefault();
// создание объекта FormData
let fData = new FormData();
// Добавление всех input, кроме type="file"
el.querySelectorAll('input:not([type="file"])').forEach(input => {
fData.append(input.name, input.value);
});
// Добавление файлов input type file
let file = el.querySelector('.upload-file__input');
for (let i = 0; i < (file.files.length); i++) {
fData.append('files[]', file.files[i]); // добавляем файлы в объект FormData()
}
// Отправка на сервер
postData('./mail.php', fData)
.then(fetchResponse => {
console.log('Данные успешно отправлены!');
console.log(fetchResponse);
})
.catch(function (error) {
console.log('Ошибка!');
console.log(error);
});
});
});
};
});
3. Совместное использование кнопки «Прикрепить файл» и Drag-and-drop
Теперь рассмотрим случай, когда нам необходимо чтобы пользователь имел возможность прикрепить файл любым из этих способов.
HTML-структура и CSS останутся без изменений. Объеденим JavaScript код.
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form');
for (let i = 1; i <= 4; i++) { // сюда будем помещать drug-&-drop файлы (4)
window['uploadDragFiles_'+i] = new Object();
}
document.querySelectorAll('.upload-file__wrapper').forEach(function (current_item, index) {
const inputFile = current_item.querySelector('.upload-file__input');
// создаём массив файлов
let fileList = [];
/////////// Кнопка «Прикрепить файл» ///////////
let textSelector = current_item.querySelector('.upload-file__text');
// Событие выбора файла(ов)
inputFile.addEventListener('change', function () {
fileList.push(...inputFile.files);
// console.log(inputFile.files);
// вызов функции для каждого файла
fileList.forEach(file => {
uploadFile(file);
});
});
// Проверяем размер файлов и выводим название
const uploadFile = (file) => {
// размер файла <5 Мб
if (file.size > 5 * 1024 * 1024) {
alert('Файл должен быть не более 5 МБ.');
return;
}
// Показ загружаемых файлов
if (file && fileList.length > 1) {
if (fileList.length <= 4) {
textSelector.textContent = `Выбрано ${fileList.length} файла`;
} else {
textSelector.textContent = `Выбрано ${fileList.length} файлов`;
}
} else {
textSelector.textContent = file.name;
}
fileList = [];
}
/////////// Загрузка файлов при помощи «Drag-and-drop» ///////////
// const dropZones = document.querySelectorAll('.upload-file__label');
const dropZone = current_item.querySelector('.upload-file__label');
const dropZoneText = current_item.querySelector('.upload-file__text');
const maxFileSize = 5000000; // максимальный размер файла - 5 мб.
// Проверка поддержки «Drag-and-drop»
if (typeof (window.FileReader) == 'undefined') {
dropZone.textContent = 'Drag&Drop Не поддерживается браузером!';
dropZone.classList.add('error');
}
// Событие - перетаскивания файла
dropZone.ondragover = function () {
this.classList.add('hover');
return false;
};
// Событие - отмена перетаскивания файла
dropZone.ondragleave = function () {
this.classList.remove('hover');
return false;
};
// Событие - файл перетащили
dropZone.addEventListener('drop', function (e) {
e.preventDefault();
this.classList.remove('hover');
this.classList.add('drop');
uploadDragFiles = e.dataTransfer.files[0]; // один файл
// Проверка размера файла
if (uploadDragFiles.size > maxFileSize) {
dropZoneText.textContent = 'Размер превышает допустимое значение!';
this.addClass('error');
return false;
}
// Показ загружаемых файлов
if (uploadDragFiles.length > 1) {
if (uploadDragFiles.length <= 4) {
dropZoneText.textContent = `Выбрано ${uploadDragFiles.length} файла`;
} else {
dropZoneText.textContent = `Выбрано ${uploadDragFiles.length} файлов`;
}
} else {
dropZoneText.textContent = e.dataTransfer.files[0].name;
}
// добавляем файл в объект "uploadDragFiles_i"
window['uploadDragFiles_'+index] = uploadDragFiles;
});
});
// Отправка формы на сервер
const postData = async (url, fData) => { // имеет асинхронные операции
// начало отправки
// здесь можно сообщить пользователю о начале отправки
// ждём ответ, только тогда наш код пойдёт дальше
let fetchResponse = await fetch(url, {
method: 'POST',
body: fData
});
// ждём окончания операции
return await fetchResponse.text();
};
if (forms) {
forms.forEach(el => {
el.addEventListener('submit', function (e) {
e.preventDefault();
// создание объекта FormData
let fData = new FormData();
// Добавление всех input, кроме type="file"
el.querySelectorAll('input:not([type="file"])').forEach(input => {
fData.append(input.name, input.value);
});
// Добавление файлов input type file
el.querySelectorAll('.upload-file__input').forEach((one_file, index) => {
for (let i = 0; i < (one_file.files.length); i++) {
fData.append('files[]', one_file.files[i]); // добавляем файлы, добавленные кнопкой
}
fData.append('files[]', window['uploadDragFiles_'+index]); // добавляем drug-&-drop файлы
});
// Отправка на сервер
postData('./mail-files.php', fData)
.then(fetchResponse => {
swal({
title: 'Спасибо!',
text: 'Данные отправлены.',
icon: 'success',
button: 'Ok'
});
// console.table('Данные успешно отправлены!', fetchResponse);
el.reset(); // Очистка полей формы
document.querySelectorAll('.upload-file__text').forEach(this_text => {
this_text.textContent = 'Выберите файл или перетащите в поле';
});
})
.catch(function (error) {
swal({
title: error,
icon: 'error',
button: 'Ok'
});
// console.table('Ошибка!', error);
});
});
});
};
});
4. Отправка множества input file multiple
Ранее мы успешно отправили все даннные формы (текстовые поля и файлы) на сервер.
Расссмотрим пример отправки multiple формы с множеством input file на почту.
<?php
$project_name = trim($_POST["project_name"]);
$admin_email = trim($_POST["admin_email"]);
$form_subject = trim($_POST["form_subject"]);
$file_attach = array();
// Если поле выбора вложения не пустое - закачиваем его на сервер
if (!empty($_FILES)) {
foreach ($_FILES['files']['name'] as $key => $file) {
$path = __DIR__ . "/upload-files/" . $file; // путь загрузки файла
if (copy($_FILES['files']['tmp_name'][$key], $path)) {
$file_attach[] = $path;
}
}
}
$c = true;
foreach ($_POST as $key => $value) {
if (is_array($value)) {
$value = implode(", ", $value);
}
if (
$value != "" &&
$key != "project_name" &&
$key != "admin_email" &&
$key != "form_subject" &&
$key != "file_attach"
) {
$message .= "
" . (($c = !$c) ? '<tr>' : '<tr style="background-color: #f8f8f8;">') . "
<td style='padding: 10px; border: #e9e9e9 1px solid;'><b>$key</b></td>
<td style='padding: 10px; border: #e9e9e9 1px solid;'>$value</td>
</tr>
";
}
}
$message = "<table style='width: 100%;'>
<tr>
<td style='padding:10px; border:#e9e9e9 1px solid; text-align:center' colspan='2'>
<big>$project_name</big>. $form_subject
</td>
</tr>
$message
</table>";
// Отправляем сообщение
if (empty($file_attach)) {
$headers = "MIME-Version: 1.0" . PHP_EOL .
"Content-Type: text/html; charset=utf-8" . PHP_EOL .
'From: ' . $project_name . ' <' . $admin_email . '>' . PHP_EOL .
'Reply-To: ' . $admin_email . '' . PHP_EOL;
mail($admin_email, $form_subject, $message, $headers); # отправка текста
} else {
send_mail($admin_email, $form_subject, $message, $file_attach); # отправка файлов
}
// Функция для отправки сообщения с вложением
function send_mail($to, $form_subject, $html, $paths)
{
$boundary = "--" . md5(uniqid(time())); // генерируем разделитель
$headers = "MIME-Version: 1.0\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\n";
$multipart = "--$boundary\n";
$multipart .= "Content-Type: text/html; charset='utf-8'\n";
$multipart .= "Content-Transfer-Encoding: Quot-Printed\n\n";
$multipart .= "$html\n\n";
$message_part = '';
foreach ($paths as $path) {
$fp = fopen($path, "r");
if (!$fp) {
echo "Файл $path не может быть прочитан";
exit();
}
$file = fread($fp, filesize($path));
fclose($fp);
$message_part .= "--$boundary\n";
$message_part .= "Content-Type: application/octet-stream\n";
$message_part .= "Content-Transfer-Encoding: base64\n";
$message_part .= "Content-Disposition: attachment; filename = \"" . $path . "\"\n\n";
$message_part .= chunk_split(base64_encode($file)) . "\n";
}
$multipart .= $message_part . "--$boundary--\n";
if (!mail($to, $form_subject, $multipart, $headers)) {
echo "К сожалению, письмо не отправлено";
exit();
}
}
Надеюсь, вам понравилась данная информация. Если вам интересна тема web-разработки, то можете следить за выходом новых статей в Telegram.
Статьи из данной категории:
Комментарии
- Комментариев пока нет.