Нестандартные поля выбора файла
Задача
Кроссбраузерно оформить поле выбора файла (<input type="file" />).
По умолчанию поля выбора файла в разных браузерах выглядят по разному:
|
в Firefox 2.0 |
|
в IE6 |
|
в Opera 9.01 |
|
в Chrome и Safari |
А по дизайну, к примеру, нужно сделать поле оформить так:
Решение
Для решения задачи делаем следующее:
- делаем поле input type="file" полностью прозрачным, чтобы скрыть оформление по умолчанию, при этом оставив работоспособным
- подкладываем блок с фоновой картинкой оформления поля
- т.к. поле с выбором файла у нас полностью прозрачное, чтобы пользователь увидел, чтоб файл успешно выбран, добавляем обычной текстовое поле поверх оформления и при выборе нового файла, с помощью javascript название присваиваем значению текстового поля
HTML конструкция:
<div class="type_file">
<input type="file" size="28" class="inputFile" onchange='document.getElementById("fileName").value=this.value' />
<div class="fonTypeFile"></div>
<input type="text" class="inputFileVal" readonly="readonly" id="fileName" />
</div>
Зачем в поле type="file" атрибут size — «Ширина поля выбора файлов в Firefox».
.type_file { /* блок-родитель, внутри которого будут позиционироваться остальные элементы для реализации стильного поля выбора файлов */
position: relative;
height: 26px;
}
.inputFile { /* поле type="file" */
position: absolute; /* абсолютное позиционирование, чтобы можно было совместить поле и блок с оформлением */
top: 0;
left: 0;
z-index: 2; /* z-слой должен быть больше, чем у блока с оформлением */
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0); /* делаем поле абсолютно прозрачным */
-moz-opacity: 0;
-khtml-opacity: 0;
opacity: 0;
width: 267px; /* задаем ширину для всех браузеров. Для firefox подбираем значение параметра size в поле */
}
.fonTypeFile { /* блок с оформлением */
width: 267px; /* размеры картинки для оформления */
height: 26px;
background: url(images/inputFile.png); /* картинка оформления поля */
position: absolute;
top: 0;
left: 0;
z-index: 1; /* z-слой меньше, чем у поля выбора файла */
}
.inputFileVal { /* поле, в котором будет показан результат выбора файла */
position: absolute;
top: 3px;
left: 5px;
z-index: 2;
width: 175px;
background: none;
border: none;
}
Результат. Проверено в:
Недостатки
- CSS-код не валиден (можно обойти, если воспользоваться условными комментариями для IE6 и закрыть глаза на некоторые старые браузеры, убрав -moz-opacity и -khtml-opacity)
- требуется javascript
- размер кнопки выбора файла по дизайну должен примерно совпадать с размера кнопки у обычного <input type="file", т.к. нет возможности изменять ее размер
update: 23.11.09
Кнопка выбора файла любого размера
Как уже отмечено, вышеприведенный способ накладывает ограничения на размер кнопки. Но что же делать, если хочется сделать огромную кнопку выбора файлов? Раз мы не можем изменять размеры поля, воспользуемся помощью javascript. Первое, что приходит на ум — при клике на элементе, передавать фокус или эмулировать клик на полу выбора файла. К сожалению данный подход не работает, по крайней мере кроссбраузерно.
Другой способ — подвигать кнопку "Обзор" под курсор мыши. Тогда при клике будет показано окно выбора файла.
<div class="button"><input type="file" value="" /></div>
.button {
width: 80px; /* размеры кноки */
height: 42px;
background: url(path-to/upload-photo.png);
overflow: hidden; /* поможет избежать выхода поля за границы кнопки */
position: relative; /* относительно этого блока будем позиционировать поле */
}
.button input {
width: 80px; /* размеры хоть и не решают всех проблем, но их указание облегчает задачу */
height: 42px;
position: absolute; /* для более простого позиционирования поля */
top: 0; /* начальные координаты */
left: 0;
opacity: 0; /* само поле делаем прозрачным, чтобы показать фон кнопки */
cursor: pointer;
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0); /* прозрачность для IE */
}
Скрипт, который при на ведении на кнопку (фон) будет перемещать поле таким образом, чтобы под курсором оказывалась кнопка "Обзор" (настоящая кнопка). Алгоритм его следующий:
- определяем координаты курсора (e.pageX/Y если данное свойство не поддерживается e.clientX/Y)
- определяем позицию поля input type="file" на странице (в цикле с помощью offsetParent и offsetLeft/O Top)
- если есть различие между позициями курсора и кнопки, перемещаем поле таким образом, чтобы кнопка обзор попала под курсор
Необходимая разница подбирается экспериментальным путем. Аналогично и координаты, на которые нужно сместить поле.
Скрипт реализован с использованием jQuery, но основные функции сделаны на голом javascript, поэтому переделать под любой другой фреймворк не составит особого труда.
jQuery(".button").hover(
function(e)
{
if (!e) e = window.event;
if (e.pageX){
x = e.pageX;
y = e.pageY;
} else if (e.clientX){
x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
y = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
}
var posLeft = 0,
posTop = 0;
var obj = this.getElementsByTagName("input")[0];
while (obj.offsetParent)
{
posLeft += obj.offsetLeft;
posTop += obj.offsetTop;
obj = obj.offsetParent;
}
var offsetX = x-posLeft,
offsetY = y-posTop;
if(offsetX<10) jQuery(".button > input").css("left","-12px");
if(offsetY<10) jQuery(".button > input").css("top","-12px");
if(offsetY>10) jQuery(".button > input").css("top","12px");
return;
},
function()
{
jQuery(".button > input").css({left:"0", top: "0"});
});
Следует учесть, что пользователь наведя курсор на кнопку, может не сразу сделать клик, а "погулять" курсором по кнопке. А это может привести к тому, что курсор окажется не над кнопкой "Обзор". Поэтому следует отслеживать перемещения курсора и по самой кнопке:
jQuery(".button").mousemove(
function(e) {
if (!e) e = window.event;
if (e.pageX){
x = e.pageX;
y = e.pageY;
} else if (e.clientX){
x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
y = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
}
var posLeft = 0,
posTop = 0;
var obj = this.getElementsByTagName("input")[0];
while (obj.offsetParent)
{
posLeft += obj.offsetLeft;
posTop += obj.offsetTop;
obj = obj.offsetParent;
}
var offsetX = x-posLeft,
offsetY = y-posTop,
currentX = jQuery(".button > input").css("left"),
currentY = parseInt(jQuery(".button > input").css("top"));
if(offsetX<10 && currentX=="0px") jQuery(".button > input").css("left","-12px");
if(offsetX<=50 && currentX=="-12px") jQuery(".button > input").css("left","0px");
if(offsetY<10 && currentY>=0) jQuery(".button > input").css("top","-12px");
if(offsetY>=35 && currentY<0) jQuery(".button > input").css("top","12px");
return;
});
Все значения (10, 50, 35, -12) подбирались экспериментально, чтобы было максимально кроссбраузерно и с минимальным шансами не отработать как нужно. Посмотреть результат.Проверено в:
Заметки
Кроссбраузерный нужный вид указателя мыши на кнопке пока не получилось добиться.