Pull to refresh

Заменяем стандартный элемент input file

Reading time 5 min
Views 14K
Недавно, занимаясь кастомизацией (да простят меня руссоведы) я бы даже назвал это веб-моддингом (ещё раз извините, уважаемые руссоведы) стандартных элементов формы, а в частности элемента file, я наткнулся на большую неприятность: он оказался не профпригоден для настройки. Суть вот в чём: от элемента file нам особенно нужно его диалоговое окно "Upload files", добыть который программными средствами в браузерах Mozilla и Opera невозможно, т.е. команда document.getElementById('SaveForm').click(); ничего не вернёт. Разработчики уверяют что это небезопасно, что спорно. А вот IE меня порадовал, он беспрепятственно отправляет нажатие мыши в элемент file, скорее всего по недосмотру разработчиков, оставивших «опаснейшую дыру» в Вашей безопасности.

Как я только не изворачивался ничего не получалось. Прогуглив три часа, наткнулся на первое свиду идеальное решение: поместить file в div, сделать div прозрачным (opacity: 0%), обращаю ваше внимание именно на то что сделать его нужно прозрачным, но не невидимым (visibility: hidden), условно говоря opacity: 0% прячет элемент от глаз, а visibility:hidden ещё и от мыши. Но если поиграть с размерами шрифтов, метод начинает терять свою привлекательность, появляется возможность промахнуться мимо невидимой кнопки.
Спустя час, я наткнулся на ещё один пример yamzbrowser (позволяет осуществлять загрузку файлов на сервер из swf-объекта вставленного в html-страницу). Он повторяет первый метод, но! с одной лишь разницей: он подставляет div под курсор мыши при перемещении. Кстати, обнаружить это мне удалось именно благодаря Опере (обычно я пользуюсь Лисой, и это большая случайность что я открыл пример именно Оперой), так как автор забыл установить прозрачность ещё и для неё.

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

Допущения


  1. Размер элемента подменяющего file могут значительно отличаться от размеров самого file'а
  2. Элемент file явлеятся частью формы
  3. Только параноик отключает JavaScript в браузере
  4. Посетитель относится к тем скромным 99% пользователей использующих Firefox, Opera или IE
  5. Решение должно работать по одинаковому алгоритму во всех браузерах, т.е. вариант использования разных средств для IE — document.getElementById('SaveForm').click();, а для FF и Opera — подстановка, неприемлем
  6. Решение может дублироваться на странице

Алгоритм


При наведении мыши на подменяющую кнопку слой с элементом подставляется под курсор, так что бы курсор был в середине слоя, т.о. мы будем точно знать что пользователь не промахнётся мимо кнопки и visibility меняется на visible: слой с элементом остаётся невидимым, но доступным для мыши. При перемещении курсора двигаем слой в пределах подменяющей кнопки, по достижению края кнопки visibility получает значение hidden.

Рецепт


HTML


<form>
<div id="divFile1" class="file" onmousemove="divFileMove(this.id, 'buttonFile1',event);">
<input type="file" name="UploadFile" id="file1" size="1" onchange="result(this.id, 'divResult');" tabindex="1" />
</div>
<div id="divResult"> </div>
<image src="./images/fileUploadButton.jpg" id="buttonFile1" onmouseover="divFileShow('divFile1', event);"/>
</form>

CSS



div.file{
  position: absolute;
  top: -1000px;
  left: -1000px;
  -moz-opacity: 0;
  filter: alpha(opacity=0);
  opacity: 0%;
  visibility: hidden;
  }


div.file — используется именно класс .file а не id, ввиду допущения номер 6.
top, left: -1000px — прячет слой до того как до него доберётся javascript, так на всякий случай.
-moz-opacity, filter: alpha, opacity — задаёт прозрачность для FF, IE и Opera соответственно.
visibility: hidden — скрывает от мыши.

JavaScript


Функция divFileShow(divId,e), где divId — id слоя с элементом file, e — event. Функции mouseX и mouseY высчитывают положение мыши относительно левого верхнего угла для всех браузеров. Делает слой видимым для мыши и подставляет под курсор таким образом чтобы курсор оказался посередине слоя, так мы точно не промахнёмся.

function divFileShow(divId,e){
  var div = document.getElementById(divId);
  div.style.left = (mouseX(e) - Math.round(div.offsetWidth/2)) + "px";
  div.style.top = (mouseY(e) - Math.round(div.offsetHeight/2)) + "px";
  div.style.visibility = "visible";
  }

Функция divFileMove(divId, butId, e), где divId — id слоя, butId — id подменяющей кнопки (необходим для получения габаритов кнопки), e — event. Она заставляет слой двигаться за мышью, при досижении границы объекта butId.
function divFileMove(divId, butId, e){
  var div = document.getElementById(divId);
  var button = document.getElementById(butId);
  var move = true;
  var divX = parseInt(div.style.left) + Math.round(div.offsetWidth/2);
  var divY = parseInt(div.style.top) + Math.round(div.offsetHeight/2);
  if (divX < button.offsetLeft || divX > (button.offsetLeft + button.offsetWidth)) {
    move = false;
    }
  if (divY < button.offsetTop || divY > (button.offsetTop + button.offsetHeight)){
    move = false;
    }
  if (move){
    div.style.left = (mouseX(e) - div.offsetWidth/2) + "px";
    div.style.top = (mouseY(e) - div.offsetHeight/2) + "px";
  } else {
    div.style.visibility = "hidden";
    }
  }

Функция result(fileId, divResultId), где fileId — id элемента файл, divResultId — id слоя куда будет выведено имя выбранного файла. Функция передаёт в innerHTML слоя только имя файла(например, «all my passwords.txt»), а в атрибут title помещает полный путь, по-моему, это удобнее.

function result(fileId, divResultId){
  var divResult = document.getElementById(divResultId);
  var formFile = document.getElementById(fileId);
  divResult.innerHTML = formFile.value.replace(/^([^\\\/]*(\\|\/))*/,"");
  divResult.setAttribute("title",formFile.value);
  }

И на последок две функции (mouseX(e), mouseY(e), где е — event) определяющие местоположение курсора на странице для IE и остальных браузеров, они отличаются только двумя буквами поэтому приведу одну из них.
function mouseX (e){
  if (e.pageX) return e.pageX;
  if (e.clientX) return e.clientX + document.body.scrollLeft;
  }

Примечание


Так как слой с элементом file перекрывает кнопку, ей не передаётся событие :hover, поэтому если вам необходимо сымитировать наведение мыши Вам придётся добавить соответствующй функционал самостоятельно либо путём изменения класса кнопки, либо изменения изображения.
Рабочий пример, если кто-то хочет посодействовать правому делу разместите пример у себя для онлайн презентации, ссылку и благодарности в эту статью гарантирую.
Тестировалось в браузерах Firefox 2, Opera 9, IE 6.
Спасибо за внимание.

P.S. О багах сообщайте заранее не отходя от кассы ;)
Tags:
Hubs:
+6
Comments 32
Comments Comments 32

Articles