Уже немало копий 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. Да, это уже работает во всех современных браузерах. Еще раз спасибо за !У всех четырех последних способов, разумеется, есть свои минусы. Существенный недостаток 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, сейчас наше кастомизированное поле выбора файла для разных размеров окна выглядит так:
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 }
Теперь наше поле выбора файла выглядит так:
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");
});
Из плюсов всех рассмотренных в статье подходов можно выделить следующие основные:
Из основных минусов:
Какой из трех двух элегантных способов создания стилизованного input выбрать для повседневного использования - решать вам. Мой выбор - способ с использованием label (хоть он и имеет несемантичную верстку).
Рабочие примеры всех трех способов можно посмотреть на CodePen.
Я думаю, что многих, как и меня, не совсем устраивает стандартный вид поля загрузки файлов. Помимо того, что оно, грубо говоря, некрасивое, так еще и в разных браузерах выглядит по-разному.
Наша же задача – сделать поле более привлекательным и, разумеется, привести его к единству.
Для решения задачи нам поможет тег 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 (очень частая проблема статичных сайтов). Решается следующим способом.
Перед закрывающим тегом подключите библиотеку:
Хочу подчеркнуть , что, в отличие от подобных инструкций на других сайтах, моя – более простая и к тому же может работать с множеством фалов в вашем поле (в таком случае все выбранные файлы в поле будут указываться через запятую).
Недавно столкнулся с интересной задачей: на странице нужно разместить пользовательскую форму, в которой было несколько полей для ввода данных и поле для выбора файла. Причем, кнопка выбора файла сделана именно как красивая кнопка, а не стандартный компонент input. А отправка этой формы, в соответствии с техническим заданием, должна выполняться сразу после выбора файла. В итоге должна получится подобная пользовательская форма:
Имя файла:
Загрузить файл
Здесь можно выделить две отдельные задачи. Первая – выполнить отправку формы сразу после выбора файла. Вторая – оформить компонент выбора файла в соответствии с дизайном в виде нестандартной кнопки.
Форма, для которой реализуем все описанные задачи, имеет следующий исходный код:
Имя файла:
и в начале выглядит так:
Имя файла:
Submit формы после выбора файла в inputПодобная задача рассматривалась в статье о перезагрузке и обновлении страницы выбором в select . Там так же использовалось событие компонента формы для отправки формы с помощью скрипта.
Выполнить отправку формы можно с помощью jQuery кода:
$("#ID_формы" ).submit();
Этот вызов нужно привязать к событию change (обновлению) компонента выбора файла input. В итоге код jQuery для решения нашей формы должен быть следующий:
jQuery(function(){
$("#myfile"
).change(function(){
// событие выбора файла
$("#myform"
).submit();
// отправка формы
});
});
Сложность этой задачи заключается в отсутствии стандартов оформления компонента. В различных браузерах он отображается его по-разному. А CSS стилей, определяющих его внешний вид не достаточно, чтобы оформить его.
Самый простой способ для решения этой задачи будет скрыть сам компонент, а вызов его выполнять по нажатию на другой элемент. При этом возникает вопрос только о том, как связать нажатие на "пользовательскую кнопку" с вызовом стандартного события выбора файла.
Одним из вариантов может быть снова применение jQuery. Но, на мой взгляд, если есть возможность обойтись без дополнительных скриптов, а использовать только теги HTML, то стоит делать без скриптов. И здесь нам на помощь приходит тег HTML label. В статье "Переключение radio кнопки и checkbox нажатием на подпись в HTML" уже описывалось его применение.
Здесь, подобным образом, тег компонента выбора файла обрамляется тегом label. В label так же добавим текст "Загрузить файл"
Имя файла:
Загрузить файл
Теперь остается только назначить стили для оформления имеющихся тегов. Компонент выбора файлов скрывается. Тегу label, придаем нужный внешний вид:
#myform p
{
text-align
: center;
margin
: 10px 0;
}
#myform #myfile
{
display
: none;
}
#myform #mylabel
{
background-color
: #fd685b;
padding
: 10px 20px;
color
: #fff;
cursor
: pointer;
}
Все преобразование мы совершим с помощью стилей CSS и jQuery . Когда вы изучите материал, то увидите, что можно обойтись только CSS, но тогда не будет видно какой файл выбран. как раз для динамики и будет использоваться jQuery. Когда все будет закончится, результат будет примерно следующим:
Суть такого изменения в подмене нашего поля на новое. По сути такой же способ как я описывал недавно в статьях - и . И так не будем втягивать и приступим к нашей задаче.
Для начала нужна разметка HTML. У меня будет свой код, у вашей формы свой. Главное, вы должны заменить свое поле выбора файла на мое или подогнать свое под основные параметры моего. Обычное поле добавления файла выглядит примерно так:
У нас же будет иметь следующий вид:
Выберите файл Файл не выбран
Давайте немножко разберем, что и как.
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_inp{display:none;}
Теперь приступим к последнему шагу, добавлению скрипта вывода имени файла при добавлении. Так как скрипт работает на jQuery , сначала нужно подключить библиотеку. если это уже сделано делать этого не нужно.
}