Отправка (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 (совпадает с версткой для «маскировочного» способа):
Выбрать
Файл не выбран
/* 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.
Все преобразование мы совершим с помощью стилей 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 , сначала нужно подключить библиотеку. если это уже сделано делать этого не нужно.
;
Некоторые браузеры предоставляют расширенный доступ к свойствам файла в поле 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 (совпадает с версткой для «маскировочного» способа):
Выбрать
Файл не выбран
/* 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.