[Из песочницы] Стилизация поля ввода для загрузки файлов на чистом CSS

В данной статье предлагаю рассмотреть стилизацию поля ввода для загрузки файлов. Данная тема не раз рассматривалась на Хабре, но, тем не менее, адекватного решения до сих пор не было предложено.Итак, начнем. Вначале рассмотрим общую идею. Основным камнем предктовения при стилизации данного элемента является определение загружен файл или нет, чтобы менять текст и стили поля в зависимости от загрузки.На первый взгляд, здесь трудно обойтись без Javascript, однако если внимательно исследовать элемент, то можно обратить внимание на надпись, которая выставляется по умолчанию, когда файл не загружен. Возможно, это многих вводит в заблуждение при решении данной задачи, однако данная надпись на самом деле ничего не значит, то есть пользователь ее видит, но несмотря на это в понимании браузера поле все равно остается пустым. Таким образом, определить является файл выбран или нет проще простого — использовать атрибут required и прилагающийся к нему псевдокласс : invalid.

Теперь подробно разберем стилизацию для основных браузеров. Начнем с самого простого случая — Webkit. Здесь стилизация вообще превращается в сказку, так как браузера на основе webkit для поля input с атрибутом file поддерживают псевдоэлементы before и after и поэтому всю верстку можно сделать всего одним тегом. Стоит также отметить, что для вебкит-браузеров поддерживается вендорный псевдоэлемент ::-webkit-file-upload-button для стилизации кнопки 'Выберите файл', который мы благополучно убьем, так как при самой детальной кастомизации элемента толку от него не очень много, потому что в нем нельзя менять текст.

Кнопку для загрузки мы будем имитировать с помощью псевдоэлемента after, а само поле с помощью псевдоэлемента before. Ну, а дальше дело техники. В нашем примере в зависимости от того загружен файл или нет меняется цвет рамки, текст в кнопке и поле для ввода. Нативную кнопку мы скрываем (::-webkit-file-upload-button) с помощью visibility: hidden, на ее место ставим псевдоэлемент after, а псевдоэлемент before растягиваем на всю оставшуюся ширину, этот псевдоэлемент исчезает, если файл загружен.

input {position: relative; width: 100%; height: 36 px; padding-top: 14 px; border: 2 px solid #ccc; outline: 0; color: blue; background: yellow;} input: invalid {border: 2 px solid red;} input::-webkit-file-upload-button {visibility: hidden; width: 160 px;} input: before, input: after {position: absolute;} input: after {content: 'Загрузите еще'; left: 0; top: 0; background: #ccc; height: 50 px; line-height: 50 px; width: 150 px; text-align: center; color: magenta;} input: invalid: after {content: 'Загрузите файл';} input: before {display: none;} input: invalid: before {content: 'Файл не загружен'; display: inline-block; background: yellow; left: 150 px; top: 0; height: 50 px; line-height: 50 px; width: calc (100% — 160 px); padding-left: 10 px; color: blue;} Рассмотрим вариант, когда кнопка находится справа. Здесь мы просто нативной кнопке (::-webkit-file-upload-button) задаем нулевую ширину, оставляя ее спрятанной с помощью visibility: hidden, а фиктивную кнопку (псевдоэлемент after) просто прижимаем к правому краю. input {position: relative; width: 100%; height: 36 px; padding-top: 14 px; border: 2 px solid #ccc; outline: 0; color: blue; background: yellow;} input: invalid {border: 2 px 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: 50 px; line-height: 50 px; width: 150 px; text-align: center; color: magenta;} input: invalid: after {content: 'Загрузите файл';} input: before {display: none;} input: invalid: before {content: 'Файл не загружен'; display: inline-block; background: yellow; left: 10 px; top: 0; height: 50 px; line-height: 50 px; width: calc (100% — 160 px); padding-left: 10 px; color: blue;} Теперь перейдем к Firefox. Этот браузер не поддерживает псевдоэлементы к инпутам вообще, поэтому здесь придется вместо псевдоэлементов использовать две метки, которые ссылаются на поле для загрузки файла.Рассмотрим вариант с кнопкой слева. Сделаем врапер, цвет которого будет соответствовать цвету поля. Здесь мы закроем нативную кнопку фаерфокса одной меткой. А с помощью второй метки будем имитировать поле, в которое можно засунуть любой текст. Это фиктивное поле будет исчезать после того, как файл будет выбран. По этой причине необходимый цвет поля будет виден тот, который указан во врапере.

.wrap {position: relative; background: yellow;} input {width: calc (100% — 70 px); height: 50 px; padding-left: 70 px; border: 2 px solid #ccc; color: blue; font: 16 px/50 px Arial;} input: invalid {border: 2 px solid red; padding-left: 70 px; width: calc (100% — 70 px);} label {position: absolute;} .button {left: 2 px; top: 2 px; width: 150 px; height: 50 px; font: 16 px/50 px 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% — 160 px); right: 0; top: 2 px; height: 50 px; padding-left: 10 px; background: yellow; color: blue; font: 16 px/50 px Arial;} Рассмотрим вариант фаерфокса, где кнопка расположена справа. Здесь чуть сложнее, но тем не менее задача решаема. Так как нативная кнопка в фаервоксе расположена слева, то мы все поле подгоним под врапер на ширину кнопки, для этого враперу зададим overflow: hidden. Дальше как и в предыдущих примерах двигаем кнопку вправо, которой является label, а второй label используем в качестве поля. В общем разберетесь.
.wrap {position: relative; background: yellow; overflow: hidden;} input {width: 100%; height: 50 px; border: 2 px solid #ccc; color: blue; font: 16 px/50 px Arial; margin-left: -82 px;} input: invalid {border: 2 px solid red; padding-left: 150 px; width: calc (100% — 150 px);} label {position: absolute;} .button {z-index: 1; right: 0; top: 0; width: 150 px; height: 54 px; font: 16 px/50 px Arial; text-align: center; background: #ccc; color: magenta; border-top: 2 px solid #ccc; border-bottom: 2 px solid #ccc; border-right: 2 px solid #ccc;} .field {display: none;} .button: after {content: 'Загрузите еще';} input: invalid ~ .button {border-top: 2 px solid red; border-bottom: 2 px solid red; border-right: 2 px solid red; height: 50 px; top: 0;} input: invalid ~ .button: after {content: 'Загрузите файл';} input: invalid ~ .field {display: inline-block; width: calc (100% — 150 px); left: 2 px; top: 2 px; height: 50 px; padding-left: 5 px; background: yellow; color: blue; font: 16 px/50 px Arial;} .left-border {width: 2 px; height: 50 px; position: absolute; top: 2 px; left: 0; background: #ccc;} input: invalid ~ .left-border {background: red;} Отдельно стоит сказать несколько слов про Internet Explorer. В нем данный способ поддерживается частично, так как поле для ввода там прописано очень жестко, точнее само поле стилизуется, но строка с текстом внутри поля стилизуется на крестах в самом браузере, поэтому ключи к нему я подобрать не смог (во всяком случае пока), возможно это и к лучшему. Плюс ко всему после загрузки файла в эксплорере фиктивное поле так и остается висеть пока на него не наведешь мышкой (во всяком случае у меня так получается на win7 ie11). Стоит также отметить, что в эксплорере есть нативный севдоэлемент для работы с данным элементом ::-ms-browse, предназначенный для стилизации кнопки для загрузки (ie10+).Подводя итоги можно выделить достоинства и недостатки данного решения.Достоинства: — полноценная стилизация элемента загрузки файлов на чистом CSS— на Webkit (который должен стать стандартом) стилизация делается одним файломНедостатки: — не поддерживается IEПример можно посмотреть и скачать здесь.

© Habrahabr.ru