Отправка (submit) формы после выбора файла с оформлением input. Проблема стилизации элементов форм

Я думаю, что многих, как и меня, не совсем устраивает стандартный вид поля загрузки файлов. Помимо того, что оно, грубо говоря, некрасивое, так еще и в разных браузерах выглядит по-разному.

Наша же задача – сделать поле более привлекательным и, разумеется, привести его к единству.

Для решения задачи нам поможет тег label , который связывает текст и прочие элементы с элементами формы (в нашем случае – с кнопкой), jQuery (который поможет нам получить необходимую информацию о выбранных файлах – имена и их расширение) и дополнительное поле input, где мы будем непосредственно хранить нашу информацию о файлах.

1. Первое, что вам потребуется – это само поле с выбором файла. Код его следующий:

Обзор...

И выглядит все это пока так:

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

2. Теперь мы напишем небольшой скрипт, который как раз и будет выводить нам информацию о файле:

$(document).ready(function() { $(".main_input_file").change(function() { var f_name = ; for (var i = 0; i < $(this).get(0).files.length; ++i) { f_name.push(" " + $(this).get(0).files[i].name); } $("#f_name").val(f_name.join(", ")); }); });

Код желательно вставить перед закрывающим тегом . И смотрим, что у нас получилось:

Отлично! То, что нам нужно. Теперь осталось скрыть стандартное поле выбора файла и причесать нашу созданную форму.

3. Для этого напишем небольшой CSS-стиль:

Main_input_file { display: none; } .upload_form div { width: 100px; height: 32px; background: #3498db; border-radius: 4px; color: #fff; text-align: center; line-height: 32px; font-family: arial; font-size:14px; display: inline-block; vertical-align: top; } .upload_form div:hover { background: #2980b9; cursor: pointer; } #f_name { background: transparent; border: 0; display: inline-block; vertical-align: top; height: 30px; padding: 0 8px; width: 150px; }

Все это сохраняем и любуемся результатом:

По-моему, очень неплохо.

Возможные ошибки в работе этого метода

Из так называемых проблем, по которым этот способ может у вас не сработать, – это отсутствие библиотеки jQuery (очень частая проблема статичных сайтов). Решается следующим способом.

Перед закрывающим тегом подключите библиотеку:

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

Уже немало копий front-end разработчиков было сломано об проблему стилизации поля ввода input . Суть проблемы заключается в том, что в спецификации HTML нет строгих правил, устанавливающих, как же должен отображаться браузером этот элемент. Более того, для input не предусмотрено атрибутов, которые позволили бы изменить его внешний вид, с помощью стилей CSS можно изменить лишь вид его границы и шрифт, а средствами JavaScript, из соображений безопасности, нельзя сымитировать клик по этому элементу, который вызвал бы системное окно для выбора файла * . Но что же делать, когда заказчик хочет адаптивный сайт с красивыми стилизованными формами, в которых нельзя обойтись без этого поля ввода?

* - на момент написания этой статьи, мне было еще неизвестно, что уже во всех современных браузерах имитация клика по input вызывает системное окно выбора файла. Большое спасибо lutov за ценный комментарий с ссылкой на рабочий пример от Pagefest !

Способы решения проблемы стилизации поля За то время, сколько существует эта проблема (а существует она очень долго), было найдено несколько способов ее решения. Всего их существует пять:Способ №1 (самый распространенный)Убедить заказчика, что можно жить и со стандартным input .Способ №2Написать/использовать готовый загрузчик файлов на Flash/Java-апплете. Используется, например, на habrastorage.org Способ №3 (будет рассмотрен в статье)Средствами CSS «замаскировать» стандартный input , сделать его полностью прозрачным и поместить на месте стилизованного фейкового поля, чтобы клик по последнему вызывал клик по стандартному, и, как следствие, открывал системное окно выбора файла.Способ №4 new! (будет рассмотрен в статье) Поместить прозрачный input внутрь элемента label, вместе с произвольными стилизованными инлайновыми элементами (кроме input, button, select и textarea, разумеется). Клик по label автоматически приведет к клику и по скрытому полю для выбора файла. Спасибо lampa за ценный комментарий !Способ №5 new! (будет рассмотрен в статье)Использовать имитацию клика по скрытому input средствами JavaScript. Да, это уже работает во всех современных браузерах. Еще раз спасибо lutov за ценный комментарий !
UPD: Внимание , данный способ неприменим для браузера Internet Explorer! Несмотря на то, что файл выбирается в скрытом input , при отправке формы значение последнего будет «сброшено». Спасибо LeonidFrolov за ценный комментарий !

У всех четырех последних способов, разумеется, есть свои минусы. Существенный недостаток Flash/Java-решения в том, что для его работы нужны соответствующие плагины, которых в браузере пользователя может не оказаться. Большой недостаток «маскировочного» решения же заключается в том, что для его реализации необходимо использовать хаки (про это речь пойдет ниже), а также потому, что оно бессмысленно без использования JavaScript (ведь нужно же как-то различать состояния «файл не выбран» и «файл выбран» для стилизованного фейкового поля, что на одном CSS сделать невозможно). Решение на JavaScript, в целом, было бы очень хорошим, но, как оказалось на практике, оно не поддерживается браузером Internet Explorer, о чем было сказано выше. Минус решения с использованием label - все то же использование JavaScript, однако, оно гораздо лучше «маскировочного» способа и должно, на мой взгляд, использоваться сейчас для решения этой острой проблемы.

Схема велосипеда Ключевой задачей было поставлено создание «резинового» input , который на экранах мобильных устройств представлял бы из себя простую кнопку для выбора файла (имя выбранного файла выводится на ней же), а на широких экранах выглядел бы как привычное для всех текстовое поле + кнопка, которое может тянуться на всю ширину окна:

Схематический вид элемента на мобильных устройствах

Схематический вид элемента на десктопных устройствах

В статье будут рассмотрены три последних способа стилизации поля выбора файла. Таким образом, с учетом оговоренной выше схемы, исходная верстка для «маскировочного» способа №3 будет иметь следующий вид (порядок дочерних элементов важен!):

Выбрать Файл не выбран

Возможная верстка для способа с применением элемента label:
Выбрать Файл не выбран

Возможная верстка для решения на JavaScript (совпадает с версткой для «маскировочного» способа):
Выбрать Файл не выбран

«Тяни, Пятачок!» или стили для «маскировочного» способа Чтобы у читателя не сложилось неверное впечатление, что каждое используемое в статье значение свойств CSS имеет огромную важность (так называемые «магические числа»), договоримся помечать те из них, которые можно смело изменять под свои нужды, комментарием

/* example */

Договорились? Отлично! Начнем стилизовать наше фейковое поле выбора файла с его «обертки» - div.file_upload :

File_upload{ position: relative; overflow: hidden; font-size: 1em; /* example */ height: 2em; /* example */ line-height: 2em /* the same as height */ }
- свойство position задается для того, чтобы относительно div.file_upload можно было абсолютно позиционировать его дочерние элементы, а свойство overflow - для того, чтобы скрывать все то, что по каким-то причинам не влезет в нашу обертку (а такое найдется, но об этом позже). На широких экранах наши красивые поле и кнопка должны отображаться в одну строку - зададим для последней фиксированную ширину и float: right, а для первого - небольшой внутренний отступ:

File_upload > button{ float: right; width: 8em; /* example */ height: 100% } .file_upload > div{ padding-left: 1em /* example */ }
Поскольку мы хотим, чтобы на мобильных устройствах текстовое поле скрывалось, и оставалась одна кнопка выбора файла, необходимо задать media query :
@media only screen and (max-width: 500px){ /* example */ .file_upload > div{ display: none } .file_upload > button{ width: 100% } }
Ну а теперь - самое веселое в данном методе! Необходимо сделать стандартный input полностью прозрачным, и растаращить растянуть его до размеров «обертки» div.file_upload . Для реализации последнего применим хак в виде абсолютного позиционирования и свойства CSS 3 transform , с помощью которого увеличим элемент, например, в 20 раз (да, это самое обычное «магическое число»):
.file_upload input{ position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform: scale(20); letter-spacing: 10em; /* IE 9 fix */ -ms-transform: scale(20); /* IE 9 fix */ opacity: 0; cursor: pointer }
Как видно из приведенного выше фрагмента CSS, для IE 9 потребовались дополнительные костыли. Это связано с тем, что данный браузер при клике на текстовое поле не вызывает системное окно выбора файла, а любезно предлагает «стереть» имя уже выбранного, что символизируется мигающим текстовым курсором. Поэтому для него дополнительно задается огромный интервал между буквами, что увеличивает кнопку элемента до размеров div.file_upload . Отмечу также, что z-index в данном случае не указывается, т.к. элемент идет последним «потомком» в выбранной с самого начала разметке.

На примере десктопного браузера FireFox, сейчас наше кастомизированное поле выбора файла для разных размеров окна выглядит так:

«Все гениальное - просто!» или стили для способа с применением label Основные стили, применяемые к текстовому полю и кнопке, для этого способа похожи на уже рассмотренные выше:

File_upload{ display: block; position: relative; overflow: hidden; font-size: 1em; /* example */ height: 2em; /* example */ line-height: 2em /* the same as height */ } .file_upload .button, .file_upload > mark{ display: block; cursor: pointer /* example */ } .file_upload .button{ float: right; box-sizing: border-box; -moz-box-sizing: border-box; width: 8em; /* example */ height: 100%; text-align: center /* example */ } .file_upload > mark{ background: transparent; /* example */ padding-left: 1em /* example */ }
Однако, теперь уже нет необходимости использовать хак с «растягиванием» прозрачного input :

File_upload input{ position: absolute; top: 0; opacity: 0 }

«Как это работает?» или стили для решения на JavaScript Поскольку исходная верстка для данного способа была выбрана такой же, как и в «маскировочном», стили для кнопки и текстового поля для обоих способов также совпадают (за исключением, разве что, свойства cursor: pointer , которое, в данном случае, будет применяться к кнопке и текстовому полю). Стиль же input можно взять тот же, что использовался в методе c применением элемента label, но в нем лучше вместо свойства opacity использовать visibility:

File_upload input{ position: absolute; top: 0; visibility: hidden }

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

/* Making it beautiful */ .file_upload{ border: 1px solid #ccc; border-radius: 3px; box-shadow: 0 0 5px rgba(0,0,0,0.1); transition: box-shadow 0.1s linear } .file_upload.focus{ box-shadow: 0 0 5px rgba(0,30,255,0.4) } .file_upload > button{ background: #7300df; transition: background 0.2s; border: 1px solid rgba(0,0,0,0.1); border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); border-radius: 2px; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); color: #fff; text-shadow: #6200bd 0 -1px 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis } .file_upload:hover > button{ background: #6200bd; text-shadow: #5d00b3 0 -1px 0 } .file_upload:active > button{ background: #5d00b3; box-shadow: 0 0 3px rgba(0,0,0,0.3) inset }

Теперь наше поле выбора файла выглядит так:

Нужно больше костылей! Поскольку мы делаем полноценное поле для выбора файла, то нужно позаботиться о том, чтобы его можно было комфортно заполнять и с клавиатуры (для «маскировочного» способа фокус сейчас вначале устанавливается на стилизованную кнопку, а затем - на скрытый input , что никак визуально не проявляется). Для этого, разумеется, используем JavaScript. Чтобы не писать много кода, я позволю себе использовать популярную библиотеку jQuery. Тогда, для «маскировочного» способа:

Var wrapper = $(".file_upload"), inp = wrapper.find("input"), btn = wrapper.find("button"), lbl = wrapper.find("div"); btn.focus(function(){ inp.focus() }); // Crutches for the:focus style: inp.focus(function(){ wrapper.addClass("focus"); }).blur(function(){ wrapper.removeClass("focus"); });
Для метода с использованием label можно убрать участок кода, отвечающий за принудительный перенос фокуса с кнопки на input (т.к. там у нас и не кнопка вовсе, а span).

Для метода, суть которого заключается в имитации клика по input , нужно, собственно, добавить эту самую имитацию:

// Yep, it works! btn.add(lbl).click(function(){ inp.click(); }); а также скорректировать код для установки класса .focus , предварительно убрав фрагмент, отвечающий за принудительный перенос фокуса:
// Crutches for the:focus style: btn.focus(function(){ wrapper.addClass("focus"); }).blur(function(){ wrapper.removeClass("focus"); });

Поле ввода до сих пор оставалось «мертвым» - при выборе файла имя последнего нигде не отображалось. Пришло время исправить и это:

Var file_api = (window.File && window.FileReader && window.FileList && window.Blob) ? true: false; inp.change(function(){ var file_name; if(file_api && inp[ 0 ].files[ 0 ]) file_name = inp[ 0 ].files[ 0 ].name; else file_name = inp.val().replace("C:\\fakepath\\", ""); if(! file_name.length) return; if(lbl.is(":visible")){ lbl.text(file_name); btn.text("Выбрать"); }else btn.text(file_name); }).change();
- если браузер поддерживает File API, то имя файла определяется с помощью него, в противном случае оно вырезается из значения скрытого input . Для мобильных устройств, когда элемент представляет из себя одну кнопку, имя выбранного файла выводится на ней же.

Казалось бы, все, что требуется, уже написано. А вот фигушки! Если выбрать файл, используя «мобильное» поле, а затем увеличить размер окна и перевести элемент в «десктопный», то в текстовом поле так и останется «Файл не выбран» - нужно каждый раз обновлять элемент при изменении размеров окна:
$(window).resize(function(){ $(".file_upload input").triggerHandler("change"); });

И что же мы получили в итоге? Полученное стилизованное поле выбора файла было успешно протестировано для всех трех способов в следующих браузерах:
  • FireFox 22.0 (Linux, Windows)
  • Opera 12.16 (Linux, Windows)
  • Internet Explorer 9
  • Chromium 27.0 (Linux)
  • Apple Safari (iOS 6.3.1)
  • Android browser (Android 2.3.6)
  • Android FireFox

Из плюсов всех рассмотренных в статье подходов можно выделить следующие основные:

Из основных минусов:

  • Необходимость использования JavaScript (касается всех способов).
  • Использование хаков CSS для «маскировочного» способа.
  • Необходимость писать дополнительные костыли для поля с атрибутом multiple (касается всех способов).
  • Некроссбраузерность - у всех способов отсутствует поддержка IE 8, а также необходимо использовать «браузерные» свойства CSS для поддержки остальных «старичков».
  • Решение на JavaScript не поддерживается всеми версиями Internet Explorer, а также некоторыми старыми версиями других популярных браузеров (хотя ими уже практически никто и не пользуется).

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

Рабочие примеры всех трех способов можно посмотреть на CodePen.

Все преобразование мы совершим с помощью стилей CSS и jQuery . Когда вы изучите материал, то увидите, что можно обойтись только CSS, но тогда не будет видно какой файл выбран. как раз для динамики и будет использоваться jQuery. Когда все будет закончится, результат будет примерно следующим:

Суть такого изменения в подмене нашего поля на новое. По сути такой же способ как я описывал недавно в статьях - и . И так не будем втягивать и приступим к нашей задаче.

Для начала нужна разметка HTML. У меня будет свой код, у вашей формы свой. Главное, вы должны заменить свое поле выбора файла на мое или подогнать свое под основные параметры моего. Обычное поле добавления файла выглядит примерно так:

У нас же будет иметь следующий вид:

Выберите файл Файл не выбран

Давайте немножко разберем, что и как.

  • - заключаем все в блок контейнер. Условие обязательное. Присваиваем блоку класс fl_upld .
  • Выберите файл - наше поле прикрепления файла заключаем в label. Это наш и идентификатор поля. Самому полю присваиваем id="fl_inp". Тоже важный момент. Ну и добавляем текст - Выберите файл . Этот текст будет на будущей кнопке.
  • Файл не выбран - блок, который изначально выводит текст - Файл не выбран. После выбора файла вместо этого текста появится название файла. данному блоку присваиваем id="fl_nm". Забегу наперед, скажу, что если не будете подключать скрипт для вывода имени файла, то можете не добавлять данный блок. У вас будет просто кнопка без динамики.

Fl_upld{width:300px;} #fl_inp{display:none;} .fl_upld label{ cursor:pointer; background:#36c; border-radius:3px; padding:10px 25px; color:#fff; font-weight:bold; text-align: center; } .fl_upld label:hover{background:#fc0;} #fl_nm{ margin-top:20px; color:#f00;}

Быстро расскажу, что за что отвечает.

  • Для контейнера fl_upld устанавливаем ширину в 300px. Если это поле будет внутри вашей формы, у которой будет ширина, то в принципе можно эту строчку не писать и ширину не задавать.
  • #fl_inp{display:none;} - скрываем наше стандартное поле прикрепления файла. Это важный и единственный главный параметр.
  • Далее для .fl_upld label - пишем параметры. Идентификатор и станет нашей кнопкой. Прописываете все что хотите, чтобы сделать кнопку нужного вида.
  • .fl_upld label:hover{background:#fc0;} - стили для кнопки. кола на нее наводишь курсор. У нас она просто становится желтой.
  • Ну и напоследок для #fl_nm блока названия файла тоже добавляем параметры.

Вот и все стили, конечно же как и всегда можете менять их как угодно и подгонять под себя. Главный параметр - это скрыть стандартное поле. #fl_inp{display:none;}

Теперь приступим к последнему шагу, добавлению скрипта вывода имени файла при добавлении. Так как скрипт работает на jQuery , сначала нужно подключить библиотеку. если это уже сделано делать этого не нужно.

;

  • if (file ) {
  • var file_size = 0 ;
  • if (file . size > 1024 * 1024 ) {
  • file_size = (Math . round (file . size * 100 /(1024 * 1024 ))/ 100 ). toString () + "MB" ;
  • else {
  • file_size = (Math . round (file . size * 100 / 1024 )/ 100 ). toString () + "KB" ;
  • document . getElementById ("file_name" ). innerHTML = "Name: " + file . name ;
  • document . getElementById ("file_size" ). innerHTML = "Size: " + file_size ;
  • catch(e ) {
  • var file = document . getElementById ("uploaded_file" ). value ;
  • file = file . replace (/\\/ g , "/" ). split ("/" ). pop ();
  • document . getElementById ("file_name" ). innerHTML = "Name: " + file ;
  • Некоторые браузеры предоставляют расширенный доступ к свойствам файла в поле input, для таких браузеров можно получить не только имя файла, но и его размер. Соответственно, такая расширенная информация и будет отображаться. Для остальных браузеров мы просто будем показывать имя выбранного файла, это значение всегда доступно через свойство value поля input.

    Готовый пример формы со стилизованным полем выбора файла и его обработкой, вы можете посмотреть на

    Уже немало копий front-end разработчиков было сломано об проблему стилизации поля ввода input . Суть проблемы заключается в том, что в спецификации HTML нет строгих правил, устанавливающих, как же должен отображаться браузером этот элемент. Более того, для input не предусмотрено атрибутов, которые позволили бы изменить его внешний вид, с помощью стилей CSS можно изменить лишь вид его границы и шрифт, а средствами JavaScript, из соображений безопасности, нельзя сымитировать клик по этому элементу, который вызвал бы системное окно для выбора файла * . Но что же делать, когда заказчик хочет адаптивный сайт с красивыми стилизованными формами, в которых нельзя обойтись без этого поля ввода?

    * - на момент написания этой статьи, мне было еще неизвестно, что уже во всех современных браузерах имитация клика по input вызывает системное окно выбора файла. Большое спасибо за с ссылкой на рабочий пример от !

    Способы решения проблемы стилизации поля За то время, сколько существует эта проблема (а существует она очень долго), было найдено несколько способов ее решения. Всего их существует пять:Способ №1 (самый распространенный)Убедить заказчика, что можно жить и со стандартным input .Способ №2Написать/использовать готовый загрузчик файлов на Flash/Java-апплете. Используется, например, на habrastorage.org Способ №3 (будет рассмотрен в статье)Средствами CSS «замаскировать» стандартный input , сделать его полностью прозрачным и поместить на месте стилизованного фейкового поля, чтобы клик по последнему вызывал клик по стандартному, и, как следствие, открывал системное окно выбора файла.Способ №4 new! (будет рассмотрен в статье) Поместить прозрачный input внутрь элемента label, вместе с произвольными стилизованными инлайновыми элементами (кроме input, button, select и textarea, разумеется). Клик по label автоматически приведет к клику и по скрытому полю для выбора файла. Спасибо за !Способ №5 new! (будет рассмотрен в статье)Использовать имитацию клика по скрытому input средствами JavaScript. Да, это уже работает во всех современных браузерах. Еще раз спасибо за !
    UPD: Внимание , данный способ неприменим для браузера Internet Explorer! Несмотря на то, что файл выбирается в скрытом input , при отправке формы значение последнего будет «сброшено». Спасибо за !

    У всех четырех последних способов, разумеется, есть свои минусы. Существенный недостаток Flash/Java-решения в том, что для его работы нужны соответствующие плагины, которых в браузере пользователя может не оказаться. Большой недостаток «маскировочного» решения же заключается в том, что для его реализации необходимо использовать хаки (про это речь пойдет ниже), а также потому, что оно бессмысленно без использования JavaScript (ведь нужно же как-то различать состояния «файл не выбран» и «файл выбран» для стилизованного фейкового поля, что на одном CSS сделать невозможно). Решение на JavaScript, в целом, было бы очень хорошим, но, как оказалось на практике, оно не поддерживается браузером Internet Explorer, о чем было сказано выше. Минус решения с использованием label - все то же использование JavaScript, однако, оно гораздо лучше «маскировочного» способа и должно, на мой взгляд, использоваться сейчас для решения этой острой проблемы.

    Схема велосипеда Ключевой задачей было поставлено создание «резинового» input , который на экранах мобильных устройств представлял бы из себя простую кнопку для выбора файла (имя выбранного файла выводится на ней же), а на широких экранах выглядел бы как привычное для всех текстовое поле + кнопка, которое может тянуться на всю ширину окна:

    Схематический вид элемента на мобильных устройствах

    Схематический вид элемента на десктопных устройствах

    В статье будут рассмотрены три последних способа стилизации поля выбора файла. Таким образом, с учетом оговоренной выше схемы, исходная верстка для «маскировочного» способа №3 будет иметь следующий вид (порядок дочерних элементов важен!):

    Выбрать Файл не выбран

    Возможная верстка для способа с применением элемента label:
    Выбрать Файл не выбран

    Возможная верстка для решения на JavaScript (совпадает с версткой для «маскировочного» способа):
    Выбрать Файл не выбран

    «Тяни, Пятачок!» или стили для «маскировочного» способа Чтобы у читателя не сложилось неверное впечатление, что каждое используемое в статье значение свойств CSS имеет огромную важность (так называемые «магические числа»), договоримся помечать те из них, которые можно смело изменять под свои нужды, комментарием

    /* example */

    Договорились? Отлично! Начнем стилизовать наше фейковое поле выбора файла с его «обертки» - div.file_upload :

    File_upload{ position: relative; overflow: hidden; font-size: 1em; /* example */ height: 2em; /* example */ line-height: 2em /* the same as height */ }
    - свойство position задается для того, чтобы относительно div.file_upload можно было абсолютно позиционировать его дочерние элементы, а свойство overflow - для того, чтобы скрывать все то, что по каким-то причинам не влезет в нашу обертку (а такое найдется, но об этом позже). На широких экранах наши красивые поле и кнопка должны отображаться в одну строку - зададим для последней фиксированную ширину и float: right, а для первого - небольшой внутренний отступ:

    File_upload > button{ float: right; width: 8em; /* example */ height: 100% } .file_upload > div{ padding-left: 1em /* example */ }
    Поскольку мы хотим, чтобы на мобильных устройствах текстовое поле скрывалось, и оставалась одна кнопка выбора файла, необходимо задать media query :
    @media only screen and (max-width: 500px){ /* example */ .file_upload > div{ display: none } .file_upload > button{ width: 100% } }
    Ну а теперь - самое веселое в данном методе! Необходимо сделать стандартный input полностью прозрачным, и растаращить растянуть его до размеров «обертки» div.file_upload . Для реализации последнего применим хак в виде абсолютного позиционирования и свойства CSS 3 transform , с помощью которого увеличим элемент, например, в 20 раз (да, это самое обычное «магическое число»):
    .file_upload input{ position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform: scale(20); letter-spacing: 10em; /* IE 9 fix */ -ms-transform: scale(20); /* IE 9 fix */ opacity: 0; cursor: pointer }
    Как видно из приведенного выше фрагмента CSS, для IE 9 потребовались дополнительные костыли. Это связано с тем, что данный браузер при клике на текстовое поле не вызывает системное окно выбора файла, а любезно предлагает «стереть» имя уже выбранного, что символизируется мигающим текстовым курсором. Поэтому для него дополнительно задается огромный интервал между буквами, что увеличивает кнопку элемента до размеров div.file_upload . Отмечу также, что z-index в данном случае не указывается, т.к. элемент идет последним «потомком» в выбранной с самого начала разметке.

    На примере десктопного браузера FireFox, сейчас наше кастомизированное поле выбора файла для разных размеров окна выглядит так:

    «Все гениальное - просто!» или стили для способа с применением label Основные стили, применяемые к текстовому полю и кнопке, для этого способа похожи на уже рассмотренные выше:

    File_upload{ display: block; position: relative; overflow: hidden; font-size: 1em; /* example */ height: 2em; /* example */ line-height: 2em /* the same as height */ } .file_upload .button, .file_upload > mark{ display: block; cursor: pointer /* example */ } .file_upload .button{ float: right; box-sizing: border-box; -moz-box-sizing: border-box; width: 8em; /* example */ height: 100%; text-align: center /* example */ } .file_upload > mark{ background: transparent; /* example */ padding-left: 1em /* example */ }
    Однако, теперь уже нет необходимости использовать хак с «растягиванием» прозрачного input :

    File_upload input{ position: absolute; top: 0; opacity: 0 }

    «Как это работает?» или стили для решения на JavaScript Поскольку исходная верстка для данного способа была выбрана такой же, как и в «маскировочном», стили для кнопки и текстового поля для обоих способов также совпадают (за исключением, разве что, свойства cursor: pointer , которое, в данном случае, будет применяться к кнопке и текстовому полю). Стиль же input можно взять тот же, что использовался в методе c применением элемента label, но в нем лучше вместо свойства opacity использовать visibility:

    File_upload input{ position: absolute; top: 0; visibility: hidden }

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

    /* Making it beautiful */ .file_upload{ border: 1px solid #ccc; border-radius: 3px; box-shadow: 0 0 5px rgba(0,0,0,0.1); transition: box-shadow 0.1s linear } .file_upload.focus{ box-shadow: 0 0 5px rgba(0,30,255,0.4) } .file_upload > button{ background: #7300df; transition: background 0.2s; border: 1px solid rgba(0,0,0,0.1); border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); border-radius: 2px; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); color: #fff; text-shadow: #6200bd 0 -1px 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis } .file_upload:hover > button{ background: #6200bd; text-shadow: #5d00b3 0 -1px 0 } .file_upload:active > button{ background: #5d00b3; box-shadow: 0 0 3px rgba(0,0,0,0.3) inset }

    Теперь наше поле выбора файла выглядит так:

    Нужно больше костылей! Поскольку мы делаем полноценное поле для выбора файла, то нужно позаботиться о том, чтобы его можно было комфортно заполнять и с клавиатуры (для «маскировочного» способа фокус сейчас вначале устанавливается на стилизованную кнопку, а затем - на скрытый input , что никак визуально не проявляется). Для этого, разумеется, используем JavaScript. Чтобы не писать много кода, я позволю себе использовать популярную библиотеку jQuery. Тогда, для «маскировочного» способа:

    Var wrapper = $(".file_upload"), inp = wrapper.find("input"), btn = wrapper.find("button"), lbl = wrapper.find("div"); btn.focus(function(){ inp.focus() }); // Crutches for the:focus style: inp.focus(function(){ wrapper.addClass("focus"); }).blur(function(){ wrapper.removeClass("focus"); });
    Для метода с использованием label можно убрать участок кода, отвечающий за принудительный перенос фокуса с кнопки на input (т.к. там у нас и не кнопка вовсе, а span).

    Для метода, суть которого заключается в имитации клика по input , нужно, собственно, добавить эту самую имитацию:

    // Yep, it works! btn.add(lbl).click(function(){ inp.click(); }); а также скорректировать код для установки класса .focus , предварительно убрав фрагмент, отвечающий за принудительный перенос фокуса:
    // Crutches for the:focus style: btn.focus(function(){ wrapper.addClass("focus"); }).blur(function(){ wrapper.removeClass("focus"); });

    Поле ввода до сих пор оставалось «мертвым» - при выборе файла имя последнего нигде не отображалось. Пришло время исправить и это:

    Var file_api = (window.File && window.FileReader && window.FileList && window.Blob) ? true: false; inp.change(function(){ var file_name; if(file_api && inp[ 0 ].files[ 0 ]) file_name = inp[ 0 ].files[ 0 ].name; else file_name = inp.val().replace("C:\\fakepath\\", ""); if(! file_name.length) return; if(lbl.is(":visible")){ lbl.text(file_name); btn.text("Выбрать"); }else btn.text(file_name); }).change();
    - если браузер поддерживает File API, то имя файла определяется с помощью него, в противном случае оно вырезается из значения скрытого input . Для мобильных устройств, когда элемент представляет из себя одну кнопку, имя выбранного файла выводится на ней же.

    Казалось бы, все, что требуется, уже написано. А вот фигушки! Если выбрать файл, используя «мобильное» поле, а затем увеличить размер окна и перевести элемент в «десктопный», то в текстовом поле так и останется «Файл не выбран» - нужно каждый раз обновлять элемент при изменении размеров окна:
    $(window).resize(function(){ $(".file_upload input").triggerHandler("change"); });

    И что же мы получили в итоге? Полученное стилизованное поле выбора файла было успешно протестировано для всех трех способов в следующих браузерах:
    • FireFox 22.0 (Linux, Windows)
    • Opera 12.16 (Linux, Windows)
    • Internet Explorer 9
    • Chromium 27.0 (Linux)
    • Apple Safari (iOS 6.3.1)
    • Android browser (Android 2.3.6)
    • Android FireFox

    Из плюсов всех рассмотренных в статье подходов можно выделить следующие основные:

    • Не используется Flash.
    • Элемент можно легко стилизовать средствами CSS, используя современные технологии адаптивного дизайна.
    • Поле можно заполнять и с клавиатуры.

    Из основных минусов:

    • Необходимость использования JavaScript (касается всех способов).
    • Использование хаков CSS для «маскировочного» способа.
    • Необходимость писать дополнительные костыли для поля с атрибутом multiple (касается всех способов).
    • Некроссбраузерность - у всех способов отсутствует поддержка IE 8, а также необходимо использовать «браузерные» свойства CSS для поддержки остальных «старичков».
    • Решение на JavaScript не поддерживается всеми версиями Internet Explorer, а также некоторыми старыми версиями других популярных браузеров (хотя ими уже практически никто и не пользуется).

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

    Рабочие примеры всех трех способов можно посмотреть на CodePen.