Привет, друзья. Давно просят сделать статью о том, как стилизовать input с type file, то есть поле для прикрепления файла. Однажды, косвенно я затрагивал эту тему в одной из статей, но сегодня мы остановимся на ней подробнее.
Конечно, как обычно есть несколько путей решить эту задачу. Это:
- Использовать фреймворк, такой как Bootstrap, Foundation и подобные.
- Попробовать самостоятельно стилизовать при помощи JS и CSS.
- Воспользоваться плагином для стилизации поля с типом «file».
Конечно, если у вас серьезный проект и вы делаете его с нуля, то позаботитесь об этом заранее и будете знать, как решите эту проблему, когда столкнетесь с ней. Но, иногда, приходится дорабатывать уже существующий сайт и в таком случае нецелесообразно подключать фреймворк для решения одной конкретной задачи. Да и если бы вы использовали фреймворк, то не пришли бы читать эту статью, поэтому сразу откинем этот вариант.
Остается попробовать самостоятельно привести все в порядок или воспользоваться плагином. Давайте остановимся на этих 2 вариантах подробнее, чтобы у вас больше не возникало вопросов как всё-таки стилизовать тег input с атрибутом type=file. Я подробно опишу вариант самостоятельной стилизации, а в конце дам ссылку на плагин, который делает практически тоже самое.
Самостоятельная стилизация тега input с типом «файл»
Существует несколько подходов к решению этой задачи, но, как по мне, самым правильным является способ, когда мы используем label для отслеживания клика, который располагается рядом с полем для прикрепления файла.
Не претендую на авторство кода и разметки, ведь еще в 2016 году я подсмотрел метод реализации на одном зарубежном блоге. Поэтому и ссылка сохранилась, так как я давно планировал написать эту стать, но в свое время откладывал, потом забыл, а совсем недавно получил пару обращений от читателей и решил сесть и написать все-таки. Поэтому, если хотите, читайте оригинал.
Итак, обычно я оборачиваю все инпуты и элементы, которые относятся к ним в общий контейнер. Мне так удобней, легче верстать, позиционировать и стилизовать.
Как я уже и говорил, мы будем отслеживать клик по label, который связан с инпутом, при этом само поле, которое предназначено для отправки файла мы скроем, задав ему прозрачность «0».
<div class="input__wrapper">
<input type="file" name="file" id="input__file" class="input input__file">
<label for="input__file">Выберите файл</label>
</div>
Вот так сейчас выглядит поле в браузере:
Добавим стили, чтобы получить описываемый результат. То есть скроем input, оставим только label, поместим в него иконку и сделаем его похожим на обычную кнопку, но сначала, добавим кое-что в верстку:
<div class="input__wrapper">
<input name="file" type="file" id="input__file" class="input input__file" multiple>
<label for="input__file" class="input__file-button">
<span class="input__file-icon-wrapper"><img class="input__file-icon" src="./img/add.svg" alt="Выбрать файл" width="25"></span>
<span class="input__file-button-text">Выберите файл</span>
</label>
</div>
Теперь добавим стили:
.input__wrapper {
width: 100%;
position: relative;
margin: 15px 0;
text-align: center;
}
.input__file {
opacity: 0;
visibility: hidden;
position: absolute;
}
.input__file-icon-wrapper {
height: 60px;
width: 60px;
margin-right: 15px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
border-right: 1px solid #fff;
}
.input__file-button-text {
line-height: 1;
margin-top: 1px;
}
.input__file-button {
width: 100%;
max-width: 290px;
height: 60px;
background: #1bbc9b;
color: #fff;
font-size: 1.125rem;
font-weight: 700;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
border-radius: 3px;
cursor: pointer;
margin: 0 auto;
}
Вот что получилось:
Как видите, уже лучше, но пока мы не видим никакой реакции на то, что файл выбран. Нет никакого уведомления и никакой индикации. Давайте исправим это. Под формой, ближе к закрывающему тегу body добавьте следующий скрипт:
<script>
let inputs = document.querySelectorAll('.input__file');
Array.prototype.forEach.call(inputs, function (input) {
let label = input.nextElementSibling,
labelVal = label.querySelector('.input__file-button-text').innerText;
input.addEventListener('change', function (e) {
let countFiles = '';
if (this.files && this.files.length >= 1)
countFiles = this.files.length;
if (countFiles)
label.querySelector('.input__file-button-text').innerText = 'Выбрано файлов: ' + countFiles;
else
label.querySelector('.input__file-button-text').innerText = labelVal;
});
});
</script>
В этом скрипте мы проверяем сколько файлов выбрал пользователь и выводим сообщение в саму кнопку. При этом, если пользователь не выбрал ничего, то оставляем текст, такой, как был изначально, а точнее - перезаписываем его.
Для примера добавил сразу 2 поля, чтобы продемонстрировать что это работает и в случае, если у вас несколько форм или полей.
Теперь давайте чуть преобразуем нашу разметку, стили и код, и сделаем привычный для всех внешний вид, когда кнопка с добавлением файла расположено справа, а поле с выводом сообщения - слева.
Разметка:
<div class="field__wrapper">
<input name="file" type="file" name="file" id="field__file-2" class="field field__file" multiple>
<label class="field__file-wrapper" for="field__file-2">
<div class="field__file-fake">Файл не вбран</div>
<div class="field__file-button">Выбрать</div>
</label>
</div>
Стили:
.field__wrapper {
width: 100%;
position: relative;
margin: 15px 0;
text-align: center;
}
.field__file {
opacity: 0;
visibility: hidden;
position: absolute;
}
.field__file-wrapper {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.field__file-fake {
height: 60px;
width: calc(100% - 130px);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
padding: 0 15px;
border: 1px solid #c7c7c7;
border-radius: 3px 0 0 3px;
border-right: none;
}
.field__file-button {
width: 130px;
height: 60px;
background: #1bbc9b;
color: #fff;
font-size: 1.125rem;
font-weight: 700;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
border-radius: 0 3px 3px 0;
cursor: pointer;
}
Скрипт:
<script>
let fields = document.querySelectorAll('.field__file');
Array.prototype.forEach.call(fields, function (input) {
let label = input.nextElementSibling,
labelVal = label.querySelector('.field__file-fake').innerText;
input.addEventListener('change', function (e) {
let countFiles = '';
if (this.files && this.files.length >= 1)
countFiles = this.files.length;
if (countFiles)
label.querySelector('.field__file-fake').innerText = 'Выбрано файлов: ' + countFiles;
else
label.querySelector('.field__file-fake').innerText = labelVal;
});
});
</script>
Что получилось:
Как видите, все не очень сложно, плюс, без jQuery, так что если у вас она уже не используется, то можно не тянуть ее за собой, ради такого скромного функционала.
Стилизация «input type file» при помощи плагина
Теперь давайте рассмотрим вариант стилизация поля для отправки файла при помощи jQuery плагина. Называется он jQuery Fileinput. Как я уже говорил, делает он практически тоже самое, просто автоматически.
Для настройки сначала подключаем сам плагин и jQuery:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="./js/fileinput.js"></script>
Затем создаем input:
<input name="upl" type="file" class="input-file" multiple>
И инициализируете плагин:
<script>
$(function() {
$(".input-file").fileinput();
});
</script>
После инициализации сам инпут скроется, а на его месте появится кнопка.
Чтобы задать ей свой класс и поместить в нее свой текст, нужно передать в fileinput html. Например так:
<script>
$(function() {
$(".input-file").fileinput('<button class="input-file__button">Выбрать файл</button>');
});
</script>
Для наглядности, смотрите пример на jsfiddle, а на сегодня все. Надеюсь, что у вас больше не осталось вопросов по поводу того, как стилизовать input с type fifle.
13 комментариев
Годнота, спасибо, я просто делал всегда рамку вокруг инпута и центрировал его. Вообще есть много споров по поводу того, стоит ли индивидуализировать этот и другие элементы, или оставить так, как многие уже привыкли, то есть по умолчанию и одинаково +- во всех браузерах.
Здравствуйте. Не работает смена текста и вывод подсчитанных файлов в связке с плагином multifile-upload-field-for-contact-form-7 - может есть предположения как это можно исправить?
Что делает multifile-upload-field-for-contact-form-7? Если просто добавляет enctype='multipart/form-data', то попробуйте добавить этот параметр скриптом обычным, а не плагином.
Отличная стилизация Input. Давно искал такой вариант дизайна. Но при попытке передать файл на сервер, у меня появилась ошибка «Не удалось открыть поток: отказано в доступе». Что я сделал?
Я загуглил эту тему и вот что нашел. Может кому то пригодиться.
1. Узнайте код ошибки php. Поместите этот код в начало файла php.
2. К папке должен быть доступ 777. Проверь это.
3. Тег form должен иметь атрибут enctype = "multipart/form-data" method = "post".
4. Полностью открой и посмотри массив $ _FILES на сервере.
5. Открой и посмотри массив $ _FILES на клиенте.
6. Проверь права пользователя и группы пользователей на каталог.
Подробнее: __http://profi.spage.me/php/check-file-input-on-php-and-jquery-why-error
Aleksey, спасибо, что не оставили без внимания и дали такой развернутый ответ. Кому-то обязательно пригодится!
Доброго времени суток... Оформил по данному примеру кнопку отправки.
Прошу прощения за, может быть, тупой вопрос, но...
Как получить вложенные файлы на почту? Если с остальными полями понятно
[tel* tel-136] выведет телефон который введет пользователь и т.д.
Но что в данном случае вставить в поле "Прикрепленные файлы", чтобы файл пришел на почту?
Еще раз прошу прощения, за может быть примитивный вопрос....
Добрый день sn_eg, несколько я понял, вы хотите отправить email с вложением. При помощи input file мы получаем файл от пользователя и загружаем его к себе на сервер в папку. Выше есть код. А что бы отправить email с этим файлом из этой папки с сервера, надо делать php код отправки email с вложенным файлом. Вот здесь есть пример: http://www.php.su/articles/?cat=examples&page=060
Если вы делаете отправку email с сервера первый раз. То разберите сперва пример попроще. PHP отправка email с сервера (загуглите пример). А затем если получится, email с вложением.
Я впервые сталкиваюсь вообще с отправкой файлов с формы. Все сделал по инструкции с поста, но так и не понял в какой папке хранятся файлы отправленные с формы cf7. На самом деле, проблема была только в стилизации кнопки отправки файлов, но более разумного решение, чем тут, не нашел. И стилизовал так как нужно, но столкнулся с рядом вопросов, с которыми ранее не сталкивался... В какую папку сохраняются файлы? Где их можно найти? Тыкните носом, пожалуйста...
Я понял вас, здесь нет кода. Код этого примера, вот здесь: https://profi.spage.me/php/check-file-input-on-php-and-jquery-why-error/
Там есть такие строчки:
$name_file_avatar = $_FILES['myfile']['name'];
////----еще код----///
$tmp_dir = $_FILES['myfile']['tmp_name'];
$destiation_dir = '/var/www/spage.me/html/user/download/'.$name_file_avatar;
move_uploaded_file($tmp_dir, $destiation_dir );
1.Узнаем имя файла.
2.Берем сам файл.
3.Путь (папка) на сервере куда будет скопирован файл.
4.Перемещаем файл с браузера на сервер.
Если есть трудности, напишите мне пожалуйста в VK https://vk.com/alekseyvanin
Я чем смогу, помогу!
Спасибо большое. Как доберусь до ноута, посмотрю/попробую.
Здравствуйте. подскажите на счет ошибки. у меня скрипт добавлен к форме с валидацией. сначала отработало, потом стало ошибку выдавать Uncaught TypeError: Cannot read properties of null (reading 'innerText') и ругается на forEach.
в сайт после добавление вносились правки. но совсем мелкие. типа на jquery парочка классов была навешана. и хз чего перестало отрабатывать. вроде все осталось как было
Здравствуйте, сложно ответить как решить эту проблему, мало данных. Надо смотреть на сайте, в консоле отладки кода. Желательно ещё иметь доступ к папке сайта, что бы править код. Если хотите, скажу своему помощнику он отладит код.
Алексей, Спасибо за быстрый ответ. В результате долгий тыканий удалось найти проблему. Она была в параметре nextElementSibling. Поскольку параметр отвечает за выборку предыдущего элемента формы, пошел косяк. В форме просто добавили доп поле с текстом.