Стилизация поля для загрузки файла

О чём эта статья?

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

План действий

Поле для загрузки файла наряду с радиокнопками, полем ввода типа select и рядом других относится к тем элементам HTML, стилизовать которые стандартными средствами не получится (точнее, изменять можно только очень ограниченный набор стилей). Рассмотрим очень простой пример:

<input type="file" name="my_file" id="my_file" />

Мы создали простое поле для загрузки файла, которое обычно является частью формы. В разных браузерах и разных операционных системах это поле будет выглядеть совершенно по-разному (включая текст на кнопке: в русскоязычном браузере он будет один, в англоязычном - другой, и изменить его нельзя).

Как же нам быть в таком случае? Во многих случаях необходимо, чтобы поле для загрузки файла имело определённый стиль и вписывалось в дизайн сайта, и заказчики обращают на это внимание. Как мы уже выяснили, стандартные средства CSS здесь не помогут, поэтому мы будем действовать иначе. Оставим это поле без изменений, за одним исключением: сделаем его полностью прозрачным с помощью стилей CSS. Затем на нашей странице создадим два простых элемента: текстовое поле и кнопку, которые и будем стилизовать так, как нам это нужно. Фокус в том, чтобы поместить это текстовое поле и кнопку под нашим полем для загрузки файла. Наверное, вы уже поняли, что с помощью этих двух элементов мы "обманываем" пользователя: он будет думать, что кликает, по нашей новой кнопке, чтобы выбрать файл для загрузки, но реально клик будет производиться по невидимому полю, которое и открывает окно для выбора файла; созданная же кнопка не делает ничего (см. иллюстрацию).

Поле для загрузки файла и маска

Синим помечено скрытое текстовое поле, которое наложено поверх "поддельной" кнопки и текстового поля (они помечены красными линиями в разметке). Кликая по кнопке "Обзор", пользователь на самом деле кликает по наложенному сверху скрытому полю, вызывая таким образом окно для выбора файла.

Кстати говоря, на этом основан один из принципов создания сайтов для угона аккаунтов: поверх некоей безобидной формы расположен невидимый iframe, в котором загружена страница изменения пароля, например, в вашей почте. Вводя текст в форму на сайте вы, на самом деле, заполяете поля Новый пароль и Подтверждение пароля, и, таким образом, злоумышленник знает ваш новый пароль. Заставить пользователя заполнить такую "форму" можно, например, выведя следующее сообщение: "Напишите в этих двух полях слово IPHONE, нажмите кнопку Отправить, и у вас появится реальный шанс выиграть новый IPhone!". Пользователь думает, что заполняет форму для выигрыша, но реально меняет собственный пароль, вводя текст в скрытую форму. Фокус лишь в том, что расположение полей формы на хакерском сайте должно точно совпадать с расположением полей на почтовом сайте, где мы пытаемся заставить пользователя сменить пароль.

Но в данном случае мы будем использовать этот принцип для вполне безобидной цели - стилизации нашего поля. Возникает вопрос: зачем все так усложнять? Почему нельзя просто отфутболить настоящее поле для загрузки куда-нибудь за границы экрана, а на "поддельное" поле, которое мы будем стилизовать, повесить событие клика? Как только пользователь кликнул на "поддельном" поле, это же событие должно принудительно вызваться на настоящем поле, и появится окно для выбора файла. Такой подход действительно предлагается в некоторых статьях, но, к сожалению, он далеко не всегда работает. Многие современные браузеры в целях безопасности запрещают программным способом кликать по элементам формы - пользователь должен сделать это самостоятельно. Именно поэтому мы должны создать иллюзию того, что пользователь кликает по кнопке загрузки, над которой располагается настоящее поле. Приступим к реализации.

За работу!

В первую очередь нам потребуется создать два новых "поддельных" элемента: текстовое поле, где будет выводиться имя выбранного файла, и кнопку с текстом "Обзор" (или любым другим на ваш выбор). Эти два элемента будут помещены в блок div; этот div вместе с настоящим полем для загрузки файла также будет заключён в div. Получилось следующее:

<div class="mask-wrapper">
   <div class="mask">
      <input class="fileInputText" type="text" disabled>
      <button class="send-file">Обзор</button>
   </div>
   <input id="my_file" class="custom-file-input" type="file" name="my_file">
</div>

.mask-wrapper - обёртка нашей композиции, .mask - обёртка для "поддельных" элементов (маска), .custom-file-input - настоящее поле для загрузки файла, которое мы скроем. Применим следующие стили для получения желаемого эффекта:

.mask-wrapper {
   height: 37px;
   margin-left: 5px;
   margin-top: 5px;
}
.mask-wrapper .send-file {
   padding: 3px 20px;
   margin-left: 3px;
   color: #fff;
   background-color: #ccc;
}
.mask-wrapper .fileInputText {
   position: relative;
   width: 133px;
   top: 3px;
}
.custom-file-input {
   width: 252px;
   opacity: 0;
   filter: alpha(opacity=0);
   position: relative;
   top: -27px;
   left: -16px;
   z-index: 99999;
   cursor: pointer;
}

Для .mask-wrapper я задал отступы, чтобы в приведённых примерах она не прижималась к краям - делать этого не обязательно, а также высоту, которую надо подбирать в зависимости от желаемого размера кпопки и текстового поля. .send-file - это наша "поддельная" кнопка. Мы немного её сдвигаем, чтобы она не слишком прилипала к текстовому полю, а также задаём собственный стиль. .fileInputText - "поддельное" текстовое поле. Мы позиционируем его относительно, чтобы немного сдвинуть по отношению к кнопке и также стилизуем. И, наконец, .custom-file-input - настоящее поле для загрузки файла. Мы задаём для него нужную ширину (обратите внимание: если на вашем сайте "поддельное" текстовое поле должно быть шире, то надо правильным образом указать ширину и для настоящего поля, чтобы оно полностью закрывало "поддельные" элементы), делаем полностью прозрачным, позиционируем относительно и сдвигаем так, чтобы оно закрывало собой текстовое поле и кнопку, а затем задаём z-index, чтобы оно находилось поверх обоих элементов. Кроме того, при наведении на него курсор будет менять свою форму, чтобы было понятно, что сюда можно кликнуть. Вот, что получилось:

Нажав на любой из двух элементов вы увидите окно для загрузки файла. Но есть одна проблема: если выбрать файл для загрузки, то в текстовом поле не будет написано его имя. Это очевидно, потому что имя пишется в настоящем поле для загрузки, которое скрыто. Нам придётся повесить событие, которое будет следить за изменениями в настоящем поле и правильным образом отображать их в "поддельном":

$(document).ready(function() {
   $('.custom-file-input').on('change', function() {
      realVal = $(this).val();
      lastIndex = realVal.lastIndexOf('\\') + 1;
      if(lastIndex !== -1) {
         realVal = realVal.substr(lastIndex);
         $(this).prev('.mask').find('.fileInputText').val(realVal);
      }
   });
});

При каждом изменении значения скрытого поля .custom-file-input мы получаем его значение, а затем подставляем имя выбранного файла в текстовое поле .fileInputText. При этом если значение представляет собой путь к файлу, то мы отрезаем от него всё, кроме непосредственно имени. Результат:

Ну, и последнее. Сейчас у нас нет эффекта "анимации" кнопки, то есть когда пользователь наводит курсор на "поддельную" кнопку, она никак не меняется. Чтобы реализовать такой функционал, нам придётся повесить ещё одно событие на наше невидимое поле и присваивать нужный класс кнопку при наведении. Данный класс определим таким образом:

.mask-wrapper button.hovered {
   color: #888;
}

Событие запишем так:

$('.custom-file-input').on('mouseenter mouseleave', function() {
   $(this).prev('.mask').find('.send-file').toggleClass('hovered');
});

Окончательный результат наших манипуляций:

Таким нехитрым способом мы стилизовали наше поле для загрузки файла. Вам осталось только выбрать только нужные стили для текстового поля и и кнопки и правильно их применить. Надеюсь, этот пост был полезен для вас. :)

Follow me on Twitter to get updates about new articles >>

Comments

Please authorize via one of the following social networks to leave a comment:

  • Ilya BodrovKrukowski
    Пардон за долгий ответ, только что комментарий увидел. Боюсь, что тут дело значительно сложнее и вопрос даже не в стилизации. Само по себе поле будет точно такое же, просто на него придётся навернуть JS. Вот очень хорошее решение с кучей примеров https://blueimp.github.io/jQuery-File-Upload/
  • Андрей Пирожков
    а как сделать тоже самое, но для загрузки нескольких файлов?