Stylowanie input file

Z jednej strony jestem praktycznie 100% fanem nie stylowania formularzy, z drugiej strony męczące jest to, jak mało jest nam dane. Stylowanie checkboxów? A może ruszymy select czy file input? Zapomnij. Nawet nie chodzi mi tutaj o wysublimowane efekty, ale o zwykłą zmianę tła…

Oczywiście możemy zrobić nie jedno cudo za pomocą js, ale nie tędy powinna iść nasza droga.
Wszystko to jak wiadomo dla naszego dobra. I tak sobie myślę – szkoda, że nikt nie wpadł na pomysł certyfikatów dla wyglądu. Masz certyfikat autentyczności? Możesz zmieniać wygląd.

Kolejna walka z wiatrakami i stylowaniem formularzy sprawiła, że chciałem sobie wyrwać włosy. Przycisków typu file za nic nie ruszymy za pomocą samego CSS. Nawet gdybyśmy je nakłuwali widłami, po prostu nie da rady (chyba, że robimy stronę tylko dla Chrome). Pozostaje więc js. Nie tyle czysty, co w postaci naszego ulubionego jQuery.

Zaglądam więc do netu w poszukiwaniu odpowiedniego pluginu. „jQuery file style” wyrzuca mi kilkadziesiąt stron z tym samym dodatkiem. Nudy. Używałem tego plugina i wcale taki fajny nie jest. Wywoływany z głupimi parametrami określającymi wymiary (po co?), w dodatku sam kod tego plugina do pięknych nie należy (niestety leży zarówno od strony kodu jquery jak i logiki kodu).

Pozostało napisanie własnego pluginu, co wcale nie jest takie trudne.

Cała sztuczka polega na:
– ukrywamy input file (opacity:0)
– ustawiamy go na pierwszy plan (z-index:2)
– pod input file wstawiamy button i input textowy, które będą symulowały nasz ukryty input file
– po kliknięciu na input file, musimy ustawić jego wartość dla inputa textowego, który wstawiliśmy pod niego

file-style

JS

$(function() {
     //dla każdego input:file
     $('input:file').each(function(){
          var $this = $(this);

          //pobieramy tekst dla buttona
          var labeltext = ($this.attr('title')!='' && $this.attr('title')!=undefined)?$(this).attr('title'):'Wybierz plik';

          //całość okryjemy divem, żeby wszystko stanowiło całość
          var $fileContainer = $('<div class="file-container"></div>');

          //input tekstowy
          var $text_file = $('<input class="file-input-text" type="text" value="..." />');

          //button "przeglądaj" do którego wstawiamy text labelki pobrany powyżej
          var $uploadbutton = $('<input class="file-input-browse" type="button" value="'+labeltext+'" />');

          //input file okrywamy divem
          $this.wrap($fileContainer);

          //do diva wstawiamy input tekstowy i button
          $this.parent('.file-container').append($text_file).append($uploadbutton);

          //dla naszego ukrytego input file wywołujemy akcję, która wstawi jego wartość do inputa tekstowego
          $this.bind('change focus click', function() {
               //czemu nie sibling? jeżeli użyjemy pluginu do stylowania formularz takiego jak jqtransform,
               //wtedy nasz input text zostanie zmieniony w sito, dlatego musimy go wyszukiwać w parencie.
               $this.parent().find('.file-input-text').val($this.val().replace("C:\\fakepath\\", ""));
          });
     });
})

CSS

.file-container {
     width:280px; 
     height:30px; 
     display:inline-block; 
     position:relative; 
     overflow:hidden;
}

/* input file dostaje z-index:2, oraz pozycjonowanie absolutne */
.file-container input[type="file"] {
     font-size:100px; 
     position:absolute; 
     left:0; 
     right:0; 
     top:0; 
     bottom:0; 
     opacity:0; 
     z-index:2; height:30px; 
     display:block; 
     cursor:pointer;
}

/* a input text i button dostają mniejszy z-index dzięki czemu znajdują się pod "niewidzialnym" input file */
.file-container input[type="text"] {
     float:left; 
     width:180px; 
     padding:5px; 
     height:18px; 
     background:#efefef; 
     border:1px solid #aaa; 
     z-index:0; 
     position:relative; font:11px Tahoma; 
     color:#777;
}
.file-container input[type="button"] {
     float:right; 
     width:70px; 
     height:30px; 
     background:red; 
     text-align:center; 
     font:11px Arial; 
     color:#fff; 
     line-height:30px; overflow:hidden; 
     display:block; 
     border:0; 
     cursor:pointer; 
     z-index:0; 
     position:relative;
}

Czemu nie wyliczam wymiarów takiego inputa automatycznie? Da się to zrobić. Jest to nawet bardzo proste (wystarczy pobrać wymiary input:file za pomocą outerWidth(true) a potem je odpowiednio użyć w naszym skrypcie). Niestety wyliczenia takie nie sprawdzają się gdy nasz input:file jest ukryty, ponieważ wymiary ukrytego elementu to 0 (tak już jest i kropka). Osobiście wolę skorzystać ze sztywnej, aczkolwiek pewnej ręcznej metody :) Swoją drogą zauważcie ciekawą technikę rozciągania elementu – input file pozycjonuję do każdej ze stron.

Ale jeżeli chcesz podjąć rzucone rękawice i zrobić automat, to masz pracę domową.

Pokaż demo

Komentarze

  • Kasia

    Dlaczego to nie działa pod IE8 trzeba na uzik kliknąć ze 2 razy??

  • Pingback: Stylowanie formularzy - doman.art.pl()

  • kwiecia

    Dzięki za ten kawałek kodu, działa lepiej niż wszystkie inne dedykowane pluginy, które się wywalają na różnych rzeczach.

    Jedna uwaga – w demie jest „$this.bind(‚click'”…, przez co input nie uzupełnia się w odpowiednim momencie tylko pozostaje pusty aż do następnego kliknięcia w niego, no ale wystarczyło zmienić zdarzenie na „change” żeby działało jak trzeba.

  • kartofelek007

    Już niedługo stworzę nowy wpis z wersją v02 dla pluginu zmieniającego wygląd. Przez ten czas trochę się to pozmieniało.

  • Adrian

    mam pytanie jak w przypadku input file multiplie (wielokrotnego wyboru) można wyświetlić wszystkie nazwy plików w polu input „file-input-text”. Niestety wyświetla się wówczas pierwszy plik z zaznaczonych plików. A chcę wszystkie pliki.