- Стилизация Input File
- 1. Стилизация input type file
- 1.1 Кнопка «Прикрепить файл» — HTML, CSS
- 1.2 Кнопка «Прикрепить файл» — JavaScript
- 2. Drag-and-drop загрузка файлов
- 2.1 Drag-and-drop — HTML, CSS
- 2.2 Drag-and-drop загрузка файлов — JavaScript
- 3. Совместное использование кнопки «Прикрепить файл» и Drag-and-drop
- 4. Отправка множества input file multiple
- Статьи из данной категории:
- Стилизация input file
- Стандартный вид
- Результат:
- Обычная кнопка
- Результат:
- В виде input text
- Результат:
- Input file со списком выбранных файлов
- Результат:
- Загрузка изображений с превью
- Styling file inputs like a boss
- Styling File Inputs
- Hiding the input
- Styling the label
- Accessibility
- Javascript Enhancements
Стилизация Input File
Сегодня будем стилизовать input type file. Т.к. стилизация тесно связана с обработкой input file на фронтенде, мы так же рассмотрим, как обработать выбранный файл и отправить на сервер. Скрипт будет работать для множества форм с возможностью не только загрузить файл при клике на кнопку, но так же перетаскиванием файла (drag-and-drop) в область загрузки. Если вам не нужен тот или иной функционал, вы легко сможете удалить часть кода.
1. Стилизация input type file
1.1 Кнопка «Прикрепить файл» — HTML, CSS
Сначала нам необходимо создать html-разметку.
- multiple — данный атрибут необходим, если вы хотите разрешить отправку более одного файла;
- accept — в данный атрибут запишите типы файлов (MIME-типы), которые разрешены для выбора.
Скроем 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
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 * 1024 * 1024) < alert("Файл должен быть не более 5 МБ."); return; >// Показ загружаемых файлов if (file && file.length > 1) < if ( file.length файла`; > if ( file.length > 4 ) < textSelector.textContent = `Выбрано $файлов`; > > 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(this); // Добавление файлов 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
Улучшим юсабилити путём добавления стилей для 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
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 файла`; > else < dropZoneText.textContent = `Выбрано $файлов`; > > 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(this); // Добавление файлов 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 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 * 1024 * 1024) < alert("Файл должен быть не более 5 МБ."); return; >// Показ загружаемых файлов if (file && fileList.length > 1) < if (fileList.length файла`; > else < textSelector.textContent = `Выбрано $файлов`; > > 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 файла`; > else < dropZoneText.textContent = `Выбрано $файлов`; > > 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(this); // Добавление файлов 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 на почту.
$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) ? "" : " ") . " $key $value "; > > $message = ' $project_name. $form_subject ' . $message . '
'; // Отправляем сообщение if (empty($file_attach)) < $headers = "MIME-Version: 1.0" . PHP_EOL . "Content-Type: text/html; charset=utf-8" . PHP_EOL . "From: " . $project_name . " " . 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.
Статьи из данной категории:
Стилизация input file
Примеры изменения вида стандартного поля для загрузки файлов ( input[type=file] ) с помощью CSS и JS.
Стандартный вид
.input-file < position: relative; display: inline-block; >.input-file-btn < position: relative; display: inline-block; cursor: pointer; outline: none; text-decoration: none; font-size: 14px; vertical-align: middle; color: rgb(255 255 255); text-align: center; border-radius: 4px; background-color: #419152; line-height: 22px; height: 40px; padding: 10px 20px; box-sizing: border-box; border: none; margin: 0; transition: background-color 0.2s; >.input-file-text < padding: 0 10px; line-height: 40px; display: inline-block; >.input-file input[type=file] < position: absolute; z-index: -1; opacity: 0; display: block; width: 0; height: 0; >/* Focus */ .input-file input[type=file]:focus + .input-file-btn < box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); >/* Hover/active */ .input-file:hover .input-file-btn < background-color: #59be6e; >.input-file:active .input-file-btn < background-color: #2E703A; >/* Disabled */ .input-file input[type=file]:disabled + .input-file-btn
$('.input-file input[type=file]').on('change', function()< let file = this.files[0]; $(this).closest('.input-file').find('.input-file-text').html(file.name); >);
Результат:
Обычная кнопка
.input-file < position: relative; display: inline-block; >.input-file span < position: relative; display: inline-block; cursor: pointer; outline: none; text-decoration: none; font-size: 14px; vertical-align: middle; color: rgb(255 255 255); text-align: center; border-radius: 4px; background-color: #419152; line-height: 22px; height: 40px; padding: 10px 20px; box-sizing: border-box; border: none; margin: 0; transition: background-color 0.2s; >.input-file input[type=file] < position: absolute; z-index: -1; opacity: 0; display: block; width: 0; height: 0; >/* Focus */ .input-file input[type=file]:focus + span < box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); >/* Hover/active */ .input-file:hover span < background-color: #59be6e; >.input-file:active span < background-color: #2E703A; >/* Disabled */ .input-file input[type=file]:disabled + span
$('.input-file input[type=file]').on('change', function()< let file = this.files[0]; $(this).next().html(file.name); >);
Результат:
В виде input text
.input-file < position: relative; display: inline-block; >.input-file-text < padding: 0 10px; line-height: 40px; text-align: left; height: 40px; display: block; float: left; box-sizing: border-box; width: 200px; border-radius: 6px 0px 0 6px; border: 1px solid #ddd; >.input-file-btn < position: relative; display: inline-block; cursor: pointer; outline: none; text-decoration: none; font-size: 14px; vertical-align: middle; color: rgb(255 255 255); text-align: center; border-radius: 0 4px 4px 0; background-color: #419152; line-height: 22px; height: 40px; padding: 10px 20px; box-sizing: border-box; border: none; margin: 0; transition: background-color 0.2s; >.input-file input[type=file] < position: absolute; z-index: -1; opacity: 0; display: block; width: 0; height: 0; >/* Focus */ .input-file input[type=file]:focus + .input-file-btn < box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); >/* Hover/active */ .input-file:hover .input-file-btn < background-color: #59be6e; >.input-file:active .input-file-btn < background-color: #2E703A; >/* Disabled */ .input-file input[type=file]:disabled + .input-file-btn
$('.input-file input[type=file]').on('change', function()< let file = this.files[0]; $(this).closest('.input-file').find('.input-file-text').html(file.name); >);
Результат:
Input file со списком выбранных файлов
.input-file-row < display: inline-block; >.input-file < position: relative; display: inline-block; >.input-file span < position: relative; display: inline-block; cursor: pointer; outline: none; text-decoration: none; font-size: 14px; vertical-align: middle; color: rgb(255 255 255); text-align: center; border-radius: 4px; background-color: #419152; line-height: 22px; height: 40px; padding: 10px 20px; box-sizing: border-box; border: none; margin: 0; transition: background-color 0.2s; >.input-file input[type=file] < position: absolute; z-index: -1; opacity: 0; display: block; width: 0; height: 0; >/* Focus */ .input-file input[type=file]:focus + span < box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); >/* Hover/Active */ .input-file:hover span < background-color: #59be6e; >.input-file:active span < background-color: #2E703A; >/* Disabled */ .input-file input[type=file]:disabled + span < background-color: #eee; >/* Список файлов */ .input-file-list < padding: 10px 0; >.input-file-list-item < margin-bottom: 10px; >.input-file-list-remove
var dt = new DataTransfer(); $('.input-file input[type=file]').on('change', function()< let $files_list = $(this).closest('.input-file').next(); $files_list.empty(); for(var i = 0; i < this.files.length; i++)< let new_file_input = '' + ''; $files_list.append(new_file_input); dt.items.add(this.files.item(i)); >; this.files = dt.files; >); function removeFilesItem(target) < let name = $(target).prev().text(); let input = $(target).closest('.input-file-row').find('input[type=file]'); $(target).closest('.input-file-list-item').remove(); for(let i = 0; i < dt.items.length; i++)< if(name === dt.items[i].getAsFile().name)< dt.items.remove(i); >> input[0].files = dt.files; > Результат:
Загрузка изображений с превью
.input-file-row < display: inline-block; >.input-file < position: relative; display: inline-block; >.input-file span < position: relative; display: inline-block; cursor: pointer; outline: none; text-decoration: none; font-size: 14px; vertical-align: middle; color: rgb(255 255 255); text-align: center; border-radius: 4px; background-color: #419152; line-height: 22px; height: 40px; padding: 10px 20px; box-sizing: border-box; border: none; margin: 0; transition: background-color 0.2s; >.input-file input[type=file] < position: absolute; z-index: -1; opacity: 0; display: block; width: 0; height: 0; >/* Focus */ .input-file input[type=file]:focus + span < box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); >/* Hover/active */ .input-file:hover span < background-color: #59be6e; >.input-file:active span < background-color: #2E703A; >/* Disabled */ .input-file input[type=file]:disabled + span < background-color: #eee; >/* Список c превью */ .input-file-list < padding: 10px 0; >.input-file-list-item < display: inline-block; margin: 0 15px 15px; width: 150px; vertical-align: top; position: relative; >.input-file-list-item img < width: 150px; >.input-file-list-name < text-align: center; display: block; font-size: 12px; text-overflow: ellipsis; overflow: hidden; >.input-file-list-remove
var dt = new DataTransfer(); $('.input-file input[type=file]').on('change', function()< let $files_list = $(this).closest('.input-file').next(); $files_list.empty(); for(var i = 0; i < this.files.length; i++)< let file = this.files.item(i); dt.items.add(file); let reader = new FileReader(); reader.readAsDataURL(file); reader.onloadend = function()< let new_file_input = '' + '' + ''; $files_list.append(new_file_input); > >; this.files = dt.files; >); function removeFilesItem(target) < let name = $(target).prev().text(); let input = $(target).closest('.input-file-row').find('input[type=file]'); $(target).closest('.input-file-list-item').remove(); for(let i = 0; i < dt.items.length; i++)< if(name === dt.items[i].getAsFile().name)< dt.items.remove(i); >> input[0].files = dt.files; > Styling file inputs like a boss
HTML elements have default styles applied to them by individual browsers. These styles could vary depending on the browser, and applying your own custom styles could range from being very easy to unnecessarily complicated (sometimes impossible). In this article we would be going through how to style file inputs, which happens to be one of those difficult elements to style using CSS. If you’ve worked with file inputs in your application, you’d know that the default style doesn’t look so nice. Here’s an example of how it looks in chrome and firefox in case you’re wondering. The good news is, we can fix this. the bad news is we won’t be able to use the «conventional» method.
Styling File Inputs
class="file-input"> type="file" id="file" class="file"> for="file">Select file
Hiding the input
.file opacity: 0; width: 0.1px; height: 0.1px; position: absolute; >
You might be thinking, why not use display: none to hide the input. Well the problem with that is the input field would be removed from the layout and then become inaccessible to people using screen readers which would be very bad. Another important thing to note is the label . With file inputs, clicking on the label also opens up the file picker, so we can use that to our advantage and style the label anyway we want.
Styling the label
Now that we’ve hidden the default input, we can decide to style the label however we want. For simplicity sake, let’s just make it look like a button.
.file-input label display: block; position: relative; width: 200px; height: 50px; border-radius: 25px; background: linear-gradient(40deg, #ff6ec4, #7873f5); box-shadow: 0 4px 7px rgba(0, 0, 0, 0.4); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; cursor: pointer; transition: transform .2s ease-out; >
Accessibility
So we’ve been able to style the label to look like a button, but that’s not all. We need to add some sort of indications to the label for when people hover on the field, or try to focus on the file field using the keyboard. I’m just going to do something simple here and increase the size of the label when that happens.
input:hover + label, input:focus + label transform: scale(1.02); >
input:focus + label outline: 1px solid #000; outline: -webkit-focus-ring-color auto 2px; >
The -webkit-focus-ring-color is used to get the default outline look on webkit browsers like chrome or safari. For non webkit browsers, a black outline would be applied to the element.
Javascript Enhancements
We can use JavaScript to display the name and size of the file selected. This would create a slightly more natural feel to the custom field. So let’s modify our HTML and CSS a bit.
class="file-input"> type="file" id="file" class="file"> for="file"> Select file class="file-name">
.file-name position: absolute; bottom: -35px; left: 10px; font-size: 0.85rem; color: #555; >
Finally, we would add an event listener to the file input and reflect the name and size of the file below the label.
const file = document.querySelector('#file'); file.addEventListener('change', (e) => // Get the selected file const [file] = e.target.files; // Get the file name and size const name: fileName, size > = file; // Convert size in bytes to kilo bytes const fileSize = (size / 1000).toFixed(2); // Set the text content const fileNameAndSize = `$fileName> - $fileSize>KB`; document.querySelector('.file-name').textContent = fileNameAndSize; >);
Here’s how everything looks.
And that’s it for the file input. You could decide to style this however you want to get the behavior you want, it’s up to you. Happy styling!😁😁