620

Cтилизация файл-инпутов

1 августа 2008, 17:11Дизайнрейтинг +35-
Конечный результат стилизации инпута

Привет. Сегодня я хочу вам рассказать о том, как можно изменить внешний вид файлового инпута, как стилизовать файл-инпут под свой дизайн, как стилизовать <input type="file">.

Хватит ключевых слов =). Суть я думаю вы поняли.

Дело в том, что изменение внешнего вида инпутов, как правило, не вызывает трудностей, но этот вид инпутов отличается от остальных. В первую очередь это связано с безопасностью, во вторую с тем, что каждый браузер по своему отображает этот элемент, и на это почти нельзя повлиять.

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

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

Пустые файл-инпуты

Как видим, во всех браузерах кроме сафари имеется текстовое поле, а справа от него кнопка. Расскажу о различиях. В Опере и IE мы можем написать адрес файла вручную (сомневаюсь, что кто-либо когда-либо пользовался этой возможностью). Файерфокс, несмотря на то, что у него есть текстовое поле, не дает нам вписать туда адрес файла. Вместо этого появляется окно выбора файла как при нажатии на кнопку «Обзор».
Давайте теперь выберем в этих полях какой-либо файл.

заполненные файл-инпуты

В результате везде, кроме сафари мы видим начало адреса, которое не несет почти никакой смысловой нагрузки. Я сомневаюсь, что кто-то об этом не знал, но на эту глупость обратили внимание только ребята из эпла и сделали возможность сразу увидеть не только название выбранного файла, но и иконку. Такую же функциональность файл-инпута мы и собираемся реализовать для всех браузеров.

Но cначала ознакомимся с проблематикой.
1. Средствами JS мы не можем сымитировать клик на такой инпут. Вот что говорится об этом в святом писании спецификации DOM:

click
Simulate a mouse-click. For INPUT elements whose type attribute has one of the following values: “button”, “checkbox”, “radio”, “reset”, or “submit”.
No Parameters
No Return Value
No Exceptions

То есть методом click мы можем сымитировать клик почти на всех типах инпутов, но не на файл-инпуте. Это сделано чтобы обезопасить компьютер клиента: в противном случае хозяин сайта мог бы без проблем получать любые файлы с компьютера клиента. Хотя с другой стороны, по клику вызывается только окно выбора файла. Так или иначе, в девелопер-центре файерфокса этот факт обозначен как баг.

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

Основная трудность в следующей проблеме.

2. Мы не можем свободно влиять на размеры кнопок «обзор», чтобы подогнать инпут под размер перекрываемой картинки. В файерфоксе мы вообще не можем изменить внешний вид файл-инпута средствами css (кроме высоты). То есть задача заключается определении оптимального размера перекрываемой картинки, чтобы минимальное количество пикселей было некликабельно, а пустые области не реагировали на клик.

Посмотрим на кликабельные области и их размеры в разных браузерах.

Рабочие области в разных браузерах

Все что внутри голубой обводки - кликабельная область. Замечу, что высоту кнопок мы можем увеличить, а вот ширину — нет.


Проблемы ясны. Переходим к делу.

Для начала нарисуем кнопку для выбора файла. Вот какая получилась у меня.

В обычном состоянии:

при наведении:

Теперь для разных типов файлов подготовим иконки и объединим их в один файл.

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

Итак, сверстаем шаблон инпута:

HTML:
<div class="blocker"> <!--блок, перекрывающий поле ввода слева от кнопки-->
<input type="file" id="File1" class="customFile" />
<div id="BrowseButton" title="Выбрать файл" class="fakeButton">
<div id="FileName"> <!--сюда мы будем вставлять имя файла и иконку-->

CSS:
<style type="text/css">
#File1 {
position: absolute;
}
.customFile {
width: 219px;
margin-left: -140px;
cursor: default;
height: 21px;
z-index: 2;
filter: alpha(opacity: 0);
opacity: 0;
}
.fakeButton {
position: absolute;
z-index: 1;
width: 85px;
height: 21px;
background: url(images/button.jpg) no-repeat left top;
float: left;
}

.blocker {
position: absolute;
z-index: 3;
width: 150px;
height: 21px;
background: url(images/transparent.gif);
margin-left: -155px;
}
#FileName {
position: absolute;
height: 15px;
margin-left: 90px;
font-family: Verdana;
font-size: 8pt;
color: Gray;
margin-top: 2px;
padding-top: 1px;
padding-left: 19px;
}
#activeBrowseButton {
background: url(images/button_active.jpg) no-repeat left top;
display: none;
}
</style>

Результат выглядит приблизительно так:

Обведенное красным — blocker, файл-инпут показан с половинной прозрачностью.


Теперь при выборе файла (событие onchange) мы должны вырезать название файла из value нашего файл-инпута, определить тип файла по расширению и сместить фон, чтобы показывалась нужная иконка. Обратите внимание, что в разных операционных системах путь к файлу может быть через прямой слэш (в Mac OS, например), или через обратный (Виндуз), поэтому чтобы вырезание названия файла работало везде необходимо два регулярных выражения.

function HandleChanges() {
file = fileInput.value;
reWin = /.*(.*)/;
var fileTitle = file.replace(reWin, "$1"); //выдираем название файла для windows
reUnix = /.*/(.*)/;
fileTitle = fileTitle.replace(reUnix, "$1"); //выдираем название файла для unix-систем
fileName.innerHTML = fileTitle;

var RegExExt =/.*.(.*)/;
var ext = fileTitle.replace(RegExExt, "$1");//и его расширение

var pos;
if (ext) {
switch (ext.toLowerCase()) {
case 'doc': pos = '0'; break;
case 'bmp': pos = '16'; break;
case 'jpg': pos = '32'; break;
case 'jpeg': pos = '32'; break;
case 'png': pos = '48'; break;
case 'gif': pos = '64'; break;
case 'psd': pos = '80'; break;
case 'mp3': pos = '96'; break;
case 'wav': pos = '96'; break;
case 'ogg': pos = '96'; break;
case 'avi': pos = '112'; break;
case 'wmv': pos = '112'; break;
case 'flv': pos = '112'; break;
case 'pdf': pos = '128'; break;
case 'exe': pos = '144'; break;
case 'txt': pos = '160'; break;
default: pos = '176'; break;
};
fileName.style.display = 'block';
fileName.style.background = 'url(images/icons.png) no-repeat 0 -'+pos+'px';
};

};
function MakeActive() {
activeButton.style.display = 'block';
};
function UnMakeActive() {
activeButton.style.display = 'none';
};

Последние две функции MakeActive и UnMakeActive добавляют hover эффект к нашей кнопочке.

Отлично, но осталось несколько проблем. Первая: в разметке большое количество дивов, которые портят семантику.
Вторая: при отключенном js пользователь не увидит, какой файл у него выбран. Давайте в случае отключенного js будем выводить обычный файл-инпут, а всю необходимую нам разметку загружать при загрузке страницы. Приступим:

На странице у нас останется только два элемента:

<div id="wrapper">
<input id="File1" type="file"/>
</div>

Если у пользователя будет отключен JavaScript, он увидит обычный файл-инпут. В противном случае произойдет следующее:

 window.onload = WindowOnLoad;
var fileInput = document.getElementById('File1');
var fileName = document.createElement('div');
fileName.style.display = 'none';
fileName.style.background = 'url(images/icons.png)';
var activeButton = document.createElement('div');
var bb = document.createElement('div');
var bl = document.createElement('div');
function WindowOnLoad() {
var wrap = document.getElementById('wrapper');
fileName.setAttribute('id','FileName');
activeButton.setAttribute('id','activeBrowseButton');
fileInput.value = '';
fileInput.onchange = HandleChanges;
fileInput.onmouseover = MakeActive;
fileInput.onmouseout = UnMakeActive;
fileInput.className = 'customFile';
bl.className = 'blocker';
bb.className = 'fakeButton';
activeButton.className = 'fakeButton';
wrap.appendChild(bb);
wrap.appendChild(bl);

wrap.appendChild(activeButton);

wrap.appendChild(fileName);
};

Теперь посмотрим как это выглядит.

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

Если пожелают читатели, то в добавок к скрипту мы сделаем специализированный контрол для asp.net программеров, позволяющий легко добавлять такие стилизованные инпуты на страницу.

Memori.rumister-wong.combobrdobr.rumoemesto.rudel.icio.uszakladki.yandex.rugoogle.com/bookmarks/

RSS Комментарии

Отличная работа! Спасибо!
cmepthuk, 2 августа 2008, 21:46
Решение интересное и часто нужное. Но вот изначально скрывать поле ввода я бы не стал. Всё-таки для win- и *nix-пользователей такой вариант непривычен и сбивает с толку.
Mikhail, 2 августа 2008, 23:51
Очень интересно, спасибо за статью!
adw0rd, 3 августа 2008, 1:19
Отличная статья, спасибо
sergy, 3 августа 2008, 11:15
хм... интересно, надо будет применить на практике :)
Impala, 4 августа 2008, 9:28
Замечательная, полезная и приятно оформленная статья. Спасибо огромное!
Monte, 4 августа 2008, 13:33
Спасибо, реализовали в одном из наших проектов! :)
Сергей, 4 августа 2008, 18:13
это 5!
drw, 5 августа 2008, 12:46
отличная статья, причем давно на эту тему не встречал чего-либо столь грамотного. Постараюсь ASP.NET control сделать :)
dmitry39, 5 августа 2008, 15:42
Спасибо, думаю пригодится!
Andron, 5 августа 2008, 19:36
ага. Пожалуйста. К пятнице улучшу код и упрощу процесс интеграции на страницы.
Жека, 5 августа 2008, 21:34
изменять надо конечно все инпуты такого типа на сайте - если уж заниматься этим. название файла я бы помещала слева и обводила бы рамочкой - имитируя поле ввода. - Надо бы добавить возможость изменять текст кнопки - т.е. - не всегда файл надо находить. Может иногда его надо просто выбрать
porcelanosa, 5 августа 2008, 23:16
Эта статья и расписана для того, чтобы было понятно как это можно сделать. Расположение файл-инпута, названия файла и всего прочего можно задать вручную. А вот для того, чтобы изменять текст кнопки, надо будет наверх выводить не рисунок, а кнопку со своим текстом.
Жека, 5 августа 2008, 23:42
К сожалению, если у пользователя отключено отображение графики, то весь этот труд насмарку - пользователь вообще ничего не увидит.
Александр, 6 августа 2008, 4:45
Опять таки, можно выводить не рисунок, а кнопку. Тогда ее можно будет всегда увидеть. А еще можно сначала кнопку, а наверх наложить нужные изображения.
Жека, 6 августа 2008, 9:38
Клёво! Пригодится :)
Ренат, 6 августа 2008, 20:58
а почему в ff3 правая половина кнопки не активна?
cr_, 7 августа 2008, 14:10
Я проверял на разных машинах и разных операционных системах: в Firefox 3 (Mac, Win), Firefox 2 (Mac, Win), Opera 9.25, Opera 9.51, IE 6, IE 7, Safari (Mac, Win) неактивен только самый край кнопки, вся остальная часть активна.
Жека, 7 августа 2008, 17:59
Недавно столкнулся с аналогичной задачей, но там была еще проблема стилизовать рамку вокруг blocker, решил очень коряво, ваши статьи мне подсказали как сделать правильно. Большое спасибо за статью.
Piom, 8 августа 2008, 19:28
классно, одна из немногих нормальных статей по теме стилей.:))
Guf, 4 сентября 2008, 16:47
Большое спасибо, использую наряду с такими полезными вещами от автора как NicEdit, иконки документов и многими другими интересными решениями! Респект!
Максим, 5 сентября 2008, 16:31
наглядно и красиво
Алексей, 8 сентября 2008, 0:46
респект великолепная и очень нужная статья
Амаль, 15 сентября 2008, 12:06

Спонсоры

Wi-Fi точки в твоем городе
Profit-project.ru - заработай на своих сайтах Реклама на сайте

Блогорол