Доброго времени суток, мой дорогой друг. В сети, да и на Хабре, есть множество статей на тему создания своего input type=«file», но все они отличаются большим количеством костылей и большим количеством кода, что, как мне кажется, не есть хорошо. Ибо, как бы это не было парадоксально, меньше — лучше.

Рабочий пример того, что получится:
Сам принцип кастомизированного input file особых отличий не имеет: убираем с экрана input, и всё возлагаем на плечи label, которому мы добавим свои стили. Главное отличие — малое кол-во кода.
Начнём
Мы имеем input и label:
Теперь уберём с экрана input, добавив классу my следующие стили:
А также стилизуем сам label, добавив ему свои стили:
И теперь самое интересное: javascript. Собственно вот он:
Он работает следующим образом: Когда пользователь жмёт на input с классом .my, то js начинает отслеживать его изменение. Дальше в дело вступает if (если). Так вот если у нас этот (this) input не пустой (то есть файл был какой-то выбран), то стоящий по соседству выше элемент (это у нас label) получит текст «Выбрано файлов» + кол-во файлов, которое выбрал пользователь. Ну а если пользователь ничего не выбрал, то label просто получит текст «Выберите файлы».
Всё!
// Дополнительная информация
Может возникнуть проблема с вёрсткой, какая была и у меня. А проблема может заключаться с этим пресловутым .prev(). По факту, есть вероятность того, что невозможно расположить label и input file рядом друг с другом, и текст «Выбрано файлов» будет применяется не к label, а к левому элементу.
Эту проблему можно решить вот так:
Поместите label и input в один div, и дайте этому div'у класс, к примеру «box-form»
И замените в js
на
Вуаля! Теперь, стоявшие далеко друг от друга, label и input способны взаимодействовать друг с другом.
Лучше, конечно, избегать таких случаев, но никто не защищён от фреймворков, где input'ы пишешь не ты, а их генерирует сам фреймворк…
Спасибо за внимание.

Рабочий пример того, что получится:
Сам принцип кастомизированного input file особых отличий не имеет: убираем с экрана input, и всё возлагаем на плечи label, которому мы добавим свои стили. Главное отличие — малое кол-во кода.
Начнём
Мы имеем input и label:
<label for="myfile" class="label">Выберите файлы</label> <input type="file" class="my" id="myfile" name="myfile" multiple>
Теперь уберём с экрана input, добавив классу my следующие стили:
.my { width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1; }
А также стилизуем сам label, добавив ему свои стили:
.label { width: 180px; height: 50px; border-radius: 4px; text-align: center; cursor: pointer; display: block; font: 14px/50px Tahoma; transition: all 0.18s ease-in-out; border: 1px solid #333; color: #333; } .label:hover { color: white; background: #333; }
И теперь самое интересное: javascript. Собственно вот он:
$('.my').change(function() { if ($(this).val() != '') $(this).prev().text('Выбрано файлов: ' + $(this)[0].files.length); else $(this).prev().text('Выберите файлы'); });
Он работает следующим образом: Когда пользователь жмёт на input с классом .my, то js начинает отслеживать его изменение. Дальше в дело вступает if (если). Так вот если у нас этот (this) input не пустой (то есть файл был какой-то выбран), то стоящий по соседству выше элемент (это у нас label) получит текст «Выбрано файлов» + кол-во файлов, которое выбрал пользователь. Ну а если пользователь ничего не выбрал, то label просто получит текст «Выберите файлы».
Всё!
// Дополнительная информация
Может возникнуть проблема с вёрсткой, какая была и у меня. А проблема может заключаться с этим пресловутым .prev(). По факту, есть вероятность того, что невозможно расположить label и input file рядом друг с другом, и текст «Выбрано файлов» будет применяется не к label, а к левому элементу.
Эту проблему можно решить вот так:
Поместите label и input в один div, и дайте этому div'у класс, к примеру «box-form»
<div class="box-form"> <p>Текст какой-то</p> <div> <label for="myfile" class="label">Выберите файлы</label> </div> <div> <div></div> <input type="file" class="my" id="myfile" name="myfile" multiple> </div> </div>
И замените в js
$(this).prev()
на
$(this).closest('.box-form').children('.label')
Вуаля! Теперь, стоявшие далеко друг от друга, label и input способны взаимодействовать друг с другом.
Лучше, конечно, избегать таких случаев, но никто не защищён от фреймворков, где input'ы пишешь не ты, а их генерирует сам фреймворк…
Спасибо за внимание.
