Способы решения проблемы стилизации поля
За то время, сколько существует эта проблема (а существует она очень долго), было найдено несколько способов ее решения. Всего их существует три:
Способ №1 (самый распространенный)
Убедить заказчика, что можно жить и со стандартным input[type=file].
Способ №2
Написать/использовать готовый загрузчик файлов на Flash/Java-апплете. Используется, например, на habrastorage.org/
Способ №3 (буден использован в статье)
Средствами CSS «замаскировать» стандартный input[type=file], сделать его полностью прозрачным и поместить на месте стилизованного фейкового поля, чтобы клик по последнему вызывал клик по стандартному, и, как следствие, открывал системное окно выбора файла.
И у второго, и у третьего способа, разумеется, есть свои минусы. Существенный недостаток Flash/Java-решения в том, что для его работы нужны соответствующие плагины, которых в браузере пользователя может не оказаться. Недостаток «маскировочного» решения же заключается в том, что для его реализации необходимо использовать хаки (про это речь пойдет ниже), а также потому, что оно бессмысленно без использования JavaScript (ведь нужно же как-то различать состояния «файл не выбран» и «файл выбран» для стилизованного фейкового поля, что на одном CSS сделать невозможно).
Схема велосипеда
Посмотрев несколько рабочих реализаций способа №3, мне захотелось изобрести свой велосипед, но не простой, а золотой адаптивный! Во всех решениях (как, например, здесь и здесь), которые мне встречались, стилизованное поле для выбора файла имеет фиксированную ширину, либо имеет вид простой одинарной кнопки (тоже с фиксированной шириной). Задание ширины же фейкового поля в процентах либо невозможно в принципе, либо может привести к тому, что данное поле будет работать в некоторых браузерах не совсем так, как хотелось бы.
Поэтому ключевой задачей было поставлено создание «резинового» input[type=file], который на экранах мобильных устройств представлял бы из себя простую кнопку для выбора файла (имя выбранного файла выводится на ней же), а на широких экранах выглядел бы как привычное для всех текстовое поле + кнопка, которое может тянуться на всю ширину окна:
Схематический вид элемента на мобильных устройствах
Схематический вид элемента на десктопных устройствах
Дабы упростить себе жизнь, было решено отказаться от поддержки «любимца» всех верстальщиков — браузера IE 8. Да, сейчас многие меня упрекнут за это, но во-первых, этот браузер сейчас используется всего 4,8% пользователей (с тенденцией на понижение), что видно из статистики, а во-вторых, у меня не было и нет большой уверенности, что реализовать подобный input[type=file] вообще возможно для этого браузера.
Таким образом, с учетом оговоренной выше схемы, исходная верстка для нашего стилизованного поля выбора файла может иметь следующий вид:
<div class="file_upload"> <div>Файл не выбран</div> <button>Выбрать</button> <input type="file"> </div>
«Тяни, Пятачок!»
Перейдем к стилям. Чтобы у читателя не сложилось неверное впечатление, что каждое используемое в статье значение свойств 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:
.file_upload > div, .file_upload > button{ float: left; height: 100%; margin: 0 }
Зададим также какую-нибудь базовую ширину для обоих элементов в процентах:
.file_upload > div{ width: 80%; /* example */ padding-left: 1em; /* example */ margin-right: -1em; /* the same as padding-left, but negative */ line-height: inherit } .file_upload > button{ width: 20% /* example */ }
Поскольку мы хотим, чтобы на мобильных устройствах текстовое поле скрывалось, и оставалась одна кнопка выбора файла, необходимо задать media query:
@media only screen and ( max-width: 500px ){ /* example */ .file_upload > div{ display: none } .file_upload > button{ width: 100% } }
Ну а теперь — самое веселое! Необходимо сделать стандартный input[type=file] полностью прозрачным, и растаращить растянуть его до размеров «обертки» div.file_upload. Для реализации последнего применим хак в виде абсолютного позиционирования и свойства CSS 3 transform, с помощью которого увеличим элемент, например, в 20 раз (да, это самое обычное «магическое число»):
.file_upload input[type=file]{ 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, сейчас наше кастомизированное поле выбора файла для разных размеров окна выглядит так:
Нужно больше стилей!
Разумеется, в таком примитивном виде поле выбора файла вряд ли кого-то устроит, поэтому добавим дополнительные стили, которые сделают кнопку выбора файла, скажем, фиолетовой, добавят тени и т.д. Не забудем также добавить свой стиль для кнопки, когда на нее наведен курсор, стиль для нажатой кнопки, а еще добавим стиль для всего элемента, когда на нем находится фокус (будет применяться при помощи 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[type=file], что никак визуально не проявляется). Для этого, разумеется, используем 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" ); });
Поле ввода до сих пор оставалось «мертвым» — при выборе файла имя последнего нигде не отображалось. Пришло время исправить и это:
var file_api = ( window.File && window.FileReader && window.FileList && window.Blob ) ? true : false; inp.change(function(){ var file_name = file_api ? inp[ 0 ].files[ 0 ].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[type=file]. Для мобильных устройств, когда элемент представляет из себя одну кнопку, имя выбранного файла выводится на ней же.
Казалось бы, все, что требуется, уже написано. А вот фигушки! Если выбрать файл, используя «мобильное» поле, а затем увеличить размер окна и перевести элемент в «десктопный», то в текстовом поле так и останется «Файл не выбран» — нужно каждый раз обновлять элемент при изменении размеров окна:
$( 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.
Рабочий пример можно посмотреть на CodePen.
ссылка на оригинал статьи http://habrahabr.ru/post/189570/
Добавить комментарий