Как стилизовать поле для отправки файла

Привет, друзья. Давно просят сделать статью о том, как стилизовать input с type file, то есть поле для прикрепления файла. Однажды, косвенно я затрагивал эту тему в одной из статей, но сегодня мы остановимся на ней подробнее.

Конечно, как обычно есть несколько путей решить эту задачу. Это:

  • Использовать фреймворк, такой как Bootstrap, Foundation и подобные.
  • Попробовать самостоятельно стилизовать при помощи JS и CSS.
  • Воспользоваться плагином для стилизации поля с типом «file».

Стилизация поля input с типом 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>

Вот так сейчас выглядит поле в браузере:

Cтилизация инпут с type file

Добавим стили, чтобы получить описываемый результат. То есть скроем input, оставим только label, поместим в него иконку и сделаем его похожим на обычную кнопку, но сначала, добавим кое-что в верстку:

<div class="input__wrapper">
   <input name="file" type="file" name="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 поля, чтобы продемонстрировать что это работает и в случае, если у вас несколько форм или полей.

Как стилизовать input type file

Теперь давайте чуть преобразуем нашу разметку стили и код, и сделаем более привычных для всех внешний вид, когда кнопка с добавлением файла расположено справа, а поле с выводом сообщения слева.

Разметка:

<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.

3 комментария

  • Даня

    Годнота, спасибо, я просто делал всегда рамку вокруг инпута и центрировал его. Вообще есть много споров по поводу того, стоит ли индивидуализировать этот и другие элементы, или оставить так, как многие уже привыкли, то есть по умолчанию и одинаково +- во всех браузерах.

    Ответить
  • Яна

    Здравствуйте. Не работает смена текста и вывод подсчитанных файлов в связке с плагином multifile-upload-field-for-contact-form-7 — может есть предположения как это можно исправить?

    Ответить
  • dimadv7

    Что делает multifile-upload-field-for-contact-form-7? Если просто добавляет enctype='multipart/form-data', то попробуйте добавить этот параметр скриптом обычным, а не плагином.

    Ответить

Добавить комментарий

 

Подписывайтесь на обновления, чтобы ничего не пропустить.