В данной статье предлагаю рассмотреть стилизацию поля ввода для загрузки файлов. Данная тема не раз рассматривалась на Хабре, но, тем не менее, адекватного решения до сих пор не было предложено.
Итак, начнем. Вначале рассмотрим общую идею. Основным камнем предктовения при стилизации данного элемента является определение загружен файл или нет, чтобы менять текст и стили поля в зависимости от загрузки.
На первый взгляд, здесь трудно обойтись без Javascript, однако если внимательно исследовать элемент, то можно обратить внимание на надпись, которая выставляется по умолчанию, когда файл не загружен. Возможно, это многих вводит в заблуждение при решении данной задачи, однако данная надпись на самом деле ничего не значит, то есть пользователь ее видит, но несмотря на это в понимании браузера поле все равно остается пустым. Таким образом, определить является файл выбран или нет проще простого — использовать атрибут required и прилагающийся к нему псевдокласс :invalid.
Теперь подробно разберем стилизацию для основных браузеров. Начнем с самого простого случая — Webkit. Здесь стилизация вообще превращается в сказку, так как браузера на основе webkit для поля input с атрибутом file поддерживают псевдоэлементы before и after и поэтому всю верстку можно сделать всего одним тегом. Стоит также отметить, что для вебкит-браузеров поддерживается вендорный псевдоэлемент ::-webkit-file-upload-button для стилизации кнопки 'Выберите файл', который мы благополучно убьем, так как при самой детальной кастомизации элемента толку от него не очень много, потому что в нем нельзя менять текст.
Кнопку для загрузки мы будем имитировать с помощью псевдоэлемента after, а само поле с помощью псевдоэлемента before. Ну а дальше дело техники. В нашем примере в зависимости от того загружен файл или нет меняется цвет рамки, текст в кнопке и поле для ввода. Нативную кнопку мы скрываем (::-webkit-file-upload-button) с помощью visibility: hidden, на ее место ставим псевдоэлемент after, а псевдоэлемент before растягиваем на всю оставшуюся ширину, этот псевдоэлемент исчезает, если файл загружен.
<input type="file" required>
input {position: relative; width: 100%; height: 36px; padding-top: 14px; border: 2px solid #ccc; outline: 0; color: blue; background: yellow;}
input:invalid {border: 2px solid red;}
input::-webkit-file-upload-button {visibility: hidden; width: 160px;}
input::before, input::after {position: absolute;}
input::after {content: 'Загрузите еще'; left: 0; top: 0; background: #ccc; height: 50px; line-height: 50px; width: 150px; text-align: center; color: magenta;}
input:invalid::after {content: 'Загрузите файл';}
input::before {display: none;}
input:invalid::before {content: 'Файл не загружен'; display: inline-block; background: yellow; left: 150px; top: 0; height: 50px; line-height: 50px; width: calc(100% - 160px); padding-left: 10px; color: blue;}
Рассмотрим вариант, когда кнопка находится справа. Здесь мы просто нативной кнопке (::-webkit-file-upload-button) задаем нулевую ширину, оставляя ее спрятанной с помощью visibility: hidden, а фиктивную кнопку (псевдоэлемент after) просто прижимаем к правому краю.
input {position: relative; width: 100%; height: 36px; padding-top: 14px; border: 2px solid #ccc; outline: 0; color: blue; background: yellow;}
input:invalid {border: 2px solid red;}
input::-webkit-file-upload-button {visibility: hidden; width: 0;}
input::before, input::after {position: absolute;}
input::after {content: 'Загрузите еще'; right: 0; top: 0; background: #ccc; height: 50px; line-height: 50px; width: 150px; text-align: center; color: magenta;}
input:invalid::after {content: 'Загрузите файл';}
input::before {display: none;}
input:invalid::before {content: 'Файл не загружен'; display: inline-block; background: yellow; left: 10px; top: 0; height: 50px; line-height: 50px; width: calc(100% - 160px); padding-left: 10px; color: blue;}
Теперь перейдем к Firefox. Этот браузер не поддерживает псевдоэлементы к инпутам вообще, поэтому здесь придется вместо псевдоэлементов использовать две метки, которые ссылаются на поле для загрузки файла.
Рассмотрим вариант с кнопкой слева. Сделаем врапер, цвет которого будет соответствовать цвету поля. Здесь мы закроем нативную кнопку фаерфокса одной меткой. А с помощью второй метки будем имитировать поле, в которое можно засунуть любой текст. Это фиктивное поле будет исчезать после того, как файл будет выбран. По этой причине необходимый цвет поля будет виден тот, который указан во врапере.
<div class="wrap">
<input type="file" required id="f">
<label for="f" class="button"></label>
<label for="f" class="field">‘айл не загружен</label>
</div>
.wrap {position: relative; background: yellow;}
input {width: calc(100% - 70px); height: 50px; padding-left: 70px; border: 2px solid #ccc; color: blue; font: 16px/50px Arial;}
input:invalid {border: 2px solid red; padding-left: 70px; width: calc(100% - 70px);}
label {position: absolute;}
.button {left: 2px; top: 2px; width: 150px; height: 50px; font: 16px/50px Arial; text-align: center; background: #ccc; color: magenta;}
.field {display: none;}
.button::after {content: 'Загрузите еще';}
input:invalid ~ .button::after {content: 'Загрузите файл';}
input:invalid ~ .field {display: inline-block; width: calc(100% - 160px); right: 0; top: 2px; height: 50px; padding-left: 10px; background: yellow; color: blue; font: 16px/50px Arial;}
Рассмотрим вариант фаерфокса, где кнопка расположена справа. Здесь чуть сложнее, но тем не менее задача решаема. Так как нативная кнопка в фаервоксе расположена слева, то мы все поле подгоним под врапер на ширину кнопки, для этого враперу зададим overflow: hidden. Дальше как и в предыдущих примерах двигаем кнопку вправо, которой является label, а второй label используем в качестве поля. В общем разберетесь.
<div class="wrap">
<input type="file" required id="f">
<label for="f" class="button"></label>
<label for="f" class="field">Файл не загружен</label>
<span class="left-border"></span>
</div>
.wrap {position: relative; background: yellow; overflow: hidden;}
input {width: 100%; height: 50px; border: 2px solid #ccc; color: blue; font: 16px/50px Arial; margin-left: -82px;}
input:invalid {border: 2px solid red; padding-left: 150px; width: calc(100% - 150px);}
label {position: absolute;}
.button {z-index: 1; right: 0; top: 0; width: 150px; height: 54px; font: 16px/50px Arial; text-align: center; background: #ccc; color: magenta; border-top: 2px solid #ccc; border-bottom: 2px solid #ccc; border-right: 2px solid #ccc;}
.field {display: none;}
.button::after {content: 'Загрузите еще';}
input:invalid ~ .button {border-top: 2px solid red; border-bottom: 2px solid red; border-right: 2px solid red; height: 50px; top: 0;}
input:invalid ~ .button::after {content: 'Загрузите файл';}
input:invalid ~ .field {display: inline-block; width: calc(100% - 150px); left: 2px; top: 2px; height: 50px; padding-left: 5px; background: yellow; color: blue; font: 16px/50px Arial;}
.left-border {width: 2px; height: 50px; position: absolute; top: 2px; left: 0; background: #ccc;}
input:invalid ~ .left-border {background: red;}
Отдельно стоит сказать несколько слов про Internet Explorer. В нем данный способ поддерживается частично, так как поле для ввода там прописано очень жестко, точнее само поле стилизуется, но строка с текстом внутри поля стилизуется на крестах в самом браузере, поэтому ключи к нему я подобрать не смог (во всяком случае пока), возможно это и к лучшему. Плюс ко всему после загрузки файла в эксплорере фиктивное поле так и остается висеть пока на него не наведешь мышкой (во всяком случае у меня так получается на win7 ie11). Стоит также отметить, что в эксплорере есть нативный севдоэлемент для работы с данным элементом ::-ms-browse, предназначенный для стилизации кнопки для загрузки (ie10+).
Подводя итоги можно выделить достоинства и недостатки данного решения.
Достоинства:
— полноценная стилизация элемента загрузки файлов на чистом CSS
— на Webkit (который должен стать стандартом) стилизация делается одним файлом
Недостатки:
— не поддерживается IE
Пример можно посмотреть и скачать здесь.
Автор: grifangel