Стилизируем элемент input type="file"

HTML, Элементы формы

Статья является переводом “Styling an input type=”file.”

Из всех полей формы поле выбора и загрузки файла практически не поддается стилизации при помощи CSS. Internet Explorer и Mozilla имеют некоторые возможности для стилизации, но остальные браузеры не предоставляют таких возможностей вообще. Кнопка “Обзор” не доступна средствами CSS.

Проблема

Для своего веб-сайта мне нужны были поля, которые имели такой вид:

Дизайнер хотел такие же стили и для поля загрузки файла, и ко всему еще и кнопку выбора в виде картинки. Когда я применил стили, которые использовал для обычных полей к полю выбора файла, они не сработали. Возникли проблемы с разным представлением одного и того же поля в разных браузерах, к тому же кнопка “Обзор” вообще оказалась недоступной для стилизации.

Вот как это выглядит в различных браузерах:

Как сделать веб сайт. Стилизируем элемент input type=

Посмотрите, как этот же элемент отображает Safari (OSX), совсем другой подход. Разработчики Safari выбрали такой метод, чтоб избежать ситуаций внедрения эксплоитов, отменив при этом ручной ввод. Результатом этого стало то, что пользователь после выбора уже не может отменить выбор.

Решение

К счастью, выход есть, его нашел Майкл МакГрейди (Michael McGrady). Все методы, которые будут рассмотрены далее, принадлежат ему, я только добавил несколько замечаний, тестов и перенес все в JavaScript.

Без использования всяких методик поле выбора файла имеет вид:

Используя технику МакГрейди, я добился такого отображения этого же поля:

Это смотрится намного лучше, не так ли?

Технология МакГрейди проста и элегантна:

  • 1. Берем обычный элемент <input type="file"> и вкладываем его в родительский элемент, у которого устанавливаем свойство position: relative.
  • 2. В этот же родительский элемент включаем поле <input> (фальшивое поле, которое будет имитировать поле с путем к файлу) и картинку – кнопку выбора (Select), к которым применяем стили. Позиционируем эти элементы абсолютно (position: absolute), так, чтоб они заняли те же места в родительском элементе, как и <input type="file">.
  • 3. Устанавливаем для <input type="file"> свойство z-index: 2, этот элемент будет лежать поверх фальшивого поля и картинки.
  • 4. Свойство opacity для элемента <input type="file"> устанавливаем равным 0. Теперь он становится прозрачным, при этом поле и картинка станут хорошо видны, и мы можем кликнуть по кнопке “Обзор” элемента <input type="file">. Если эта кнопка находится над картинкой, пользователь может кликнуть по ней и при этом вызовет окно выбора файла. (Помните, не используйте visibility: hidden, потому как элемент станет невидимым и кликнуть по нему будет невозможно).
  • 5. При выборе пользователем файла, наше фальшивое поле должно отображать путь к выбранному файлу, как в элементе <input type="file">. В этом нам поможет JavaScript, с помощью которого мы скопируем путь из поля выбора файла в наше фальшивое, стилизированоие поле.

Технология использует JavaScript. Я решил перенести все в JavaScript по причинам, которые объясню позже.

HTML/CSS структура

Используем следующий HTML/CSS подход:

CSS

div.fileinputs {
	position: relative;
}
div.fakefile {
	position: absolute;
	top: 0px;
	left: 0px;
	z-index: 1;
}
input.file {
	position: relative;
	text-align: right;
	-moz-opacity:0 ;
	filter:alpha(opacity: 0);
	opacity: 0;
	z-index: 2;
}

HTML

<div class="fileinputs">
	<input type="file" class="file" />
	<div class="fakefile">
		<input />
		<img src="search.gif" />
	</div>
</div>

Родительский элемент <div class="fileinputs"> имеет свойство position: relative, соответственно мы можем создать блок внутри него, который будет позиционирован абсолютно.

Блок fakefile содержит фальшивое поле и кнопку для выбора файла. Он размещается абсолютно и имеет свойство z-index: 1, то есть лежит ниже настоящего поля выбора файла.

Настоящее поле выбора также имеет свойство position: relative, чтоб к нему можно было применить z-index. В конечном результате, это поле должно находиться выше фальшивых полей и быть прозрачным.

Также заметьте, свойство text-align: right - Mozilla не поддерживает ширины для поля выбора, и мы должны быть уверены, что кнопка “Обзор” находится возле правого края. Кнопка выбора в виде картинки также находится возле правого края, и она должна лежать точно под кнопкой “Обзор…” поля <div class="fileinputs">.

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

Почему JavaScript?

Я решил все перенести в JavaScript, так как нам его в любом случае нужно использовать для копирования пути к файлу. К тому же, такой подход делает код чистым и читабельным. И, наконец, старые браузеры не справятся со стилями CSS, в худшем случае будет видно два поля для выбора: одно настоящее, второе – фальшивое. Для этого в JavaScript мы проверяем, может ли движок браузера создать новый элемент или нет.

Вот что получаем при использовании простого подхода:

Browse

Вы можете выбрать файл, но не увидите в поле его пути (JavaScript не используется).

Ниже показано как это выглядит в некоторых старых браузерах:

Netscape 4
Как сделать веб сайт. Стилизируем элемент input type=

Как видите, пользователь увидит только кнопку, возможно, он даже не сможет по ней кликнуть.

Explorer 4

Как сделать веб сайт. Стилизируем элемент input type=

Netscape 3

Пользователи этого браузера увидят два поля, второе не будет работать:
Как сделать веб сайт. Стилизируем элемент input type=

Решение - JavaScript

Решение всех этих проблем простое – генерировать фальшивые поля при помощи JavaScript. В худшем случае – скрипт не будет работать и пользователь увидит обычное поле для выбора, что намного лучше, чем рассмотренные выше примеры в различных браузерах.

Итак, наш код сократится до следущего:

<div class="fileinputs">
	<input type="file" class="file">
</div>

Оставшиеся элементы мы добавим при помощи JavaScript.

Скрипт

Вот код нашего скрипта:

var W3CDOM = (document.createElement && document.getElementsByTagName);

function initFileUploads() {
	if (!W3CDOM) return;
	var fakeFileUpload = document.createElement('div');
	fakeFileUpload.className = 'fakefile';
	fakeFileUpload.appendChild(document.createElement('input'));
	var image = document.createElement('img');
	image.src='pix/button_select.gif';
	fakeFileUpload.appendChild(image);
	var x = document.getElementsByTagName('input');
	for (var i=0;i<x.length;i++) {
		if (x[i].type != 'file') continue;
		if (x[i].parentNode.className != 'fileinputs') continue;
		x[i].className = 'file hidden';
		var clone = fakeFileUpload.cloneNode(true);
		x[i].parentNode.appendChild(clone);
		x[i].relatedElement = clone.getElementsByTagName('input')[0];
		x[i].onchange = x[i].onmouseout = function () {
			this.relatedElement.value = this.value;
		}
	}
}

Объяснения:

1. Если браузер не поддерживает W3C DOM, не делать ничего – пользователи увидят обычное поле для выбора файла.

var W3CDOM = (document.createElement && document.getElementsByTagName);

function initFileUploads() {
	if (!W3CDOM) return;

2. Cоздаем фальшивое поле для ввода и кнопку:

var fakeFileUpload = document.createElement('div');
fakeFileUpload.className = 'fakefile';
fakeFileUpload.appendChild(document.createElement('input'));
var image = document.createElement('img');
image.src='pix/button_select.gif';
fakeFileUpload.appendChild(image);

3. Просматриваем весь документ на поля и игнорируем все, кроме поля, тип которого file.

var x = document.getElementsByTagName('input');
	for (var i=0;i<x.length;i++) {
		if (x[i].type != 'file') continue;

4. Проверяем, имеет ли найденное поле родительский элемент fileinputs:

if (x[i].parentNode.className != 'fileinputs') continue;

5. Итак, мы нашли то, что искали, теперь нам нужно его настроить. Во-первых, добавляем новый класс “hidden”, в котором описано прозрачность и позиционирование для поля:

x[i].className = 'file hidden';

6. Делаем копию созданного фальшивого элемента и включаем его в родительский блок:

var clone = fakeFileUpload.cloneNode(true);
x[i].parentNode.appendChild(clone);

7. Теперь мы успешно создали то, к чему стремились.

Но это еще не все. Мы должны отобразить путь к файлу, который мы выбрали. Для этого, создадим новое свойство для элемента <input type="file">, которое будет относиться к нашему фальшивому полю:

x[i].relatedElement = clone.getElementsByTagName('input')[0];

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

Проблема здесь заключается в том, какое событие использовать. Очевидно, при изменении значения поля выбора файла (onchange) нужно вносить и изменения в фальшивое поле. Но Mozilla 1.6 такого события не поддерживает. Поэтому я использую еще и событие onmouseout, вызов которого происходит когда файл выбран.

x[i].onchange = x[i].onmouseout = function () {
   this.relatedElement.value = this.value;
}

Проблемы и дополнения

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

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

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

Разрешить выделять еще можно:

x[i].onselect = function () {
	this.relatedElement.select();
}

Но, к сожалению, безопасность в коде JavaScript не разрешит нам изменять значения поля выбора <input type="file">, потому мы не можем позволить пользователю изменять значения в фальшивом поле.

Возможным решением этой проблемы будет добавление еще одной кнопки “Очистить,” которая будет удалять поле выбора и строить его заново. Это может показаться очень нагроможденным, но, возможно, это единственный способ.

 

© 2008 Как сделать веб сайт
Entries RSS Comments RSS