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

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

Привет. Сегодня я хочу вам рассказать о том, как можно изменить внешний вид файлового инпута, как стилизовать файл-инпут под свой дизайн, как стилизовать <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 программеров, позволяющий легко добавлять такие стилизованные инпуты на страницу.

Рекламное место, которое может стать вашим

Понравилась статья?

Тогда подпишись на обновления через RSS или воспользуйся
другими способами подписки.

Читать в Яндекс.Ленте Добавить в Google Добавить в Netvibes
  •  

Комментарии — 50

RSS
  • Аватарка
    2 августа 2008 в 21:46 ]]>cmepthuk]]>
    Отличная работа! Спасибо!
  • Аватарка
    2 августа 2008 в 23:51 Mikhail
    Решение интересное и часто нужное. Но вот изначально скрывать поле ввода я бы не стал. Всё-таки для win- и *nix-пользователей такой вариант непривычен и сбивает с толку.
  • Аватарка
    3 августа 2008 в 1:19 adw0rd
    Очень интересно, спасибо за статью!
  • Аватарка
    3 августа 2008 в 11:15 sergy
    Отличная статья, спасибо
  • Аватарка
    4 августа 2008 в 9:28 Impala
    хм... интересно, надо будет применить на практике :)
  • Аватарка
    4 августа 2008 в 13:33 Monte
    Замечательная, полезная и приятно оформленная статья. Спасибо огромное!
  • Аватарка
    4 августа 2008 в 18:13 ]]>Сергей]]>
    Спасибо, реализовали в одном из наших проектов! :)
  • Аватарка
    5 августа 2008 в 12:46 drw
    это 5!
  • Аватарка
    5 августа 2008 в 15:42 ]]>dmitry39]]>
    отличная статья, причем давно на эту тему не встречал чего-либо столь грамотного. Постараюсь ASP.NET control сделать :)
  • Аватарка
    5 августа 2008 в 19:36 Andron
    Спасибо, думаю пригодится!
  • Аватарка
    5 августа 2008 в 21:34 ]]>Жека]]>
    ага. Пожалуйста. К пятнице улучшу код и упрощу процесс интеграции на страницы.
  • Аватарка
    5 августа 2008 в 23:16 ]]>porcelanosa]]>
    изменять надо конечно все инпуты такого типа на сайте - если уж заниматься этим. название файла я бы помещала слева и обводила бы рамочкой - имитируя поле ввода. - Надо бы добавить возможость изменять текст кнопки - т.е. - не всегда файл надо находить. Может иногда его надо просто выбрать
  • Аватарка
    5 августа 2008 в 23:42 ]]>Жека]]>
    Эта статья и расписана для того, чтобы было понятно как это можно сделать. Расположение файл-инпута, названия файла и всего прочего можно задать вручную. А вот для того, чтобы изменять текст кнопки, надо будет наверх выводить не рисунок, а кнопку со своим текстом.
  • Аватарка
    6 августа 2008 в 4:45 Александр
    К сожалению, если у пользователя отключено отображение графики, то весь этот труд насмарку - пользователь вообще ничего не увидит.
  • Аватарка
    6 августа 2008 в 9:38 ]]>Жека]]>
    Опять таки, можно выводить не рисунок, а кнопку. Тогда ее можно будет всегда увидеть. А еще можно сначала кнопку, а наверх наложить нужные изображения.
  • Аватарка
    6 августа 2008 в 20:58 Ренат
    Клёво! Пригодится :)
  • Аватарка
    7 августа 2008 в 14:10 cr_
    а почему в ff3 правая половина кнопки не активна?
  • Аватарка
    7 августа 2008 в 17:59 ]]>Жека]]>
    Я проверял на разных машинах и разных операционных системах: в Firefox 3 (Mac, Win), Firefox 2 (Mac, Win), Opera 9.25, Opera 9.51, IE 6, IE 7, Safari (Mac, Win) неактивен только самый край кнопки, вся остальная часть активна.
  • Аватарка
    8 августа 2008 в 19:28 ]]>Piom]]>
    Недавно столкнулся с аналогичной задачей, но там была еще проблема стилизовать рамку вокруг blocker, решил очень коряво, ваши статьи мне подсказали как сделать правильно. Большое спасибо за статью.
  • Аватарка
    4 сентября 2008 в 16:47 ]]>Guf]]>
    классно, одна из немногих нормальных статей по теме стилей.:))
  • Аватарка
    5 сентября 2008 в 16:31 Максим
    Большое спасибо, использую наряду с такими полезными вещами от автора как NicEdit, иконки документов и многими другими интересными решениями! Респект!
  • Аватарка
    8 сентября 2008 в 0:46 ]]>Алексей]]>
    наглядно и красиво
  • Аватарка
    15 сентября 2008 в 12:06 ]]>Амаль]]>
    респект великолепная и очень нужная статья
  • Аватарка
    30 декабря 2008 в 19:54 Jabe
    отличная статья ..завтра же попробую реализовать в проекте:)..а то стандартное поле загрузки вообще не нравится
  • Аватарка
    12 января 2009 в 21:53 Миша Татхагата
    Женя, а почему бы не отрыть народу страшную тайну: существует, по крайней мере, пара плагинов к JQuery, которые позволяют сделать все это действо немного проще. Надеюсь, ты не заподозришь меня в том, что я не слишком хорошо знаком с объектной моделью документа. Но, как человек продающий за возобновляемые деньги невосполнимое время я все чащи и чаще думаю о том, что стодвенадцатьтысячтристатридцатьчетвертое изобретение велосипеда, возможно, не является самой удачной идеей в наше нелегкое время )))
  • Аватарка
    13 января 2009 в 15:53 ]]>Жека]]>
    Ну вот. Взял и все рассказал :(
  • Аватарка
    13 января 2009 в 16:59 Миша Татхагата
    Браво, Женя, адекватно реагируешь на комплименты )))
  • Аватарка
    26 февраля 2009 в 13:50 Сергей DS
    Если пожелают читатели, то в добавок к скрипту мы сделаем специализированный контрол для asp.net программеров, позволяющий легко добавлять такие стилизованные инпуты на страницу.
    Если такая возможность есть, было бы СУПЕР!
  • Аватарка
    30 марта 2009 в 21:11 ]]>Евгений]]>
    Ок. До пятницы сделаю.
  • Аватарка
    15 мая 2009 в 12:39 Сергей
    Друзья, у меня он не работает, все конечно красиво но файлы на сервер не загружаются
  • Аватарка
    15 мая 2009 в 14:33 ]]>Евгений]]>
    Они и не должны загружаться на сервер. Я рассматривал здесь только клиентскую часть.
  • Аватарка
    23 августа 2009 в 0:52 ]]>Stefana]]>
    Stefana http://freelance4all.ru/ Мне очень полезна эта статья оказалась. Давно задавался целью сделать что-либо подобное. Ты давно всем этим увлекаешься??
  • Аватарка
    24 августа 2009 в 17:02 ]]>Evgenij]]>
    Стилизацей файл-инпутов? Да, давольно давно. В детстве меня больше склоняло, как и всех нас, молодых и незрелых, к стилизации чекбоксов.
    Но потом я одумался и стал увлекаться вышиванием крестиком и стилизацией файл-инпутов.
  • Аватарка
    1 октября 2009 в 10:53 ]]>Константин]]>
    Спасибо! Просто супер реализация. Можно Ваш пример использовать у себя на сайте?
  • Аватарка
    1 октября 2009 в 16:53 ]]>Евгений]]>
    Конечно можно!
  • Аватарка
    25 октября 2009 в 21:06 ]]>Adward]]>
    Спасибо =)
  • Аватарка
    14 января в 17:20 LamerStudio
    А как можно сделать 2 кнопки выбора на одной странице? Например, нужно указать, что нужно выгрузить фотографию и резюме. Как со строны css - понятно, две разные кнопки, в разных состояних, для них разные стили. Но, видимо что-то надо менять js. И курсор, наверное, надо менять со стандартного.
  • Аватарка
    14 января в 18:23 ]]>Евгений]]>
    ЛамерСтудио, после этой статьи вышло еще несколько про стилзацию файл-инпутов.
    Последняя: обзор файловых загрузчиков.
    Рекомендую AjaxUpload.

    А насчет курсора, то его стиль невозможно изменить в FF.
  • Аватарка
    14 января в 19:22 LamerStudio
    Спасибо, Женя! Но мне понравился именно этот вариант. Все устраивает, но два на странице не становятся, а надо два именно таких на одной странице.
  • Аватарка
    14 января в 19:34 LamerStudio
    cursor: pointer исправляет ситуацию с курсором, но не до конца, мешает опять же js? который делает псевдо :hover.
  • Аватарка
    19 января в 8:24 LamerStudio
    Евгений, никак не решим эту проблему?
  • Аватарка
    9 февраля в 20:31 dot
    заметил небольшой косяк: если сначала выбрать какой-нибудь файл, а при следующей попытке выбора нажать кнопку Отмена, то в поле с именем файла сохранится иконка соответствующая типу предыдущего выбранного файла.
    решение: добавить к условию выбора иконки if(ext){...} условие else fileName.style.display = 'none';
  • Аватарка
    28 февраля в 16:39 B@rmaley.e>
    > Замечу, что высоту кнопок мы можем увеличить, а вот ширину — нет.
    А я утверждаю, что ширину тоже можно изменить.
  • Аватарка
    28 февраля в 16:57 ]]>Евгений]]>
    Бармалей, браво :)
    Я такого не видел, и сам не додумался.
  • Аватарка
    30 марта в 21:23 ]]>Саша]]>
    а как можно запустить функцию на javascript сразу после того как будет выбран файла?
  • Аватарка
    14 апреля в 9:43 ]]>Денисов Вячеслав]]>
    var RegExExt =/.*.(.*)/; //здесь ошибка
    var RegExExt =/.*.(.*)/; //так правильней (косая перед точкой)
  • Аватарка
    14 апреля в 9:49 ]]>Денисов Вячеслав]]>
    >>а как можно запустить функцию на javascript сразу после того как будет выбран файла?



    function select_ext() {
    здесь код функции
    }


    p.s. к предыдущему моему комментарию - перед второй точкой нужно ставить обратный слэш, чтобы функция работала
  • Аватарка
    14 апреля в 9:52 ]]>Денисов Вячеслав]]>
    господи. администрация, разрешите в комментариях html-теги (htmlspecialchars() которые) а то бардак получается.

    to Саша:
    input type="file" onchange="функция()"
  • Аватарка
    28 апреля в 13:50 Евген
    Неее, статейка весьма сомнительной недобности. Все это, конечно, фишки, но никакой реальной выгоды не имеют.
  • Аватарка
    24 мая в 18:26 ]]>LoveIs]]>
    пришлось сначала повозиться, но потом все заработало так как надо, спасибо :)
  • Эл. почта (используется для Граватарки)
  • Домашняя страница
  • Имя в Твиттере
  • Разрешенные теги Текст сообщения (надо бы заполнить это поле)
  • как выглядит какой тег
    жирный текст <b>жирный текст</b>
    курсивный тект <i>курсивный тект</i>
    зачеркнутый текст <s>зачеркнутый текст</s>
    подчеркнутый текст <u>подчеркнутый текст</u>
    ссылка <a href="адрес">ссылка</a>
    function foo() { ... }
    <pre><code>function foo() { ... } </code></pre>