Questo articolo è una traduzione della versione originale in inglese intitolata Styling an input type=”file” e pubblicato su questo sito con l’autorizzazione del autore.
Il credito di questa tecnica va interamente a Michael McGrady, chi l’ha inventata.
NB: Il browser deve supportare la proprietà opacity per applicare questa tecnica. Per quello, non funzionerà con Internet Explorer 5.0 sia su Windows che Mac e Opera.
Di tutti i campi di forma, il ‘file upload’ è uno dei peggiori quando viene a li dare stile css.
Windows Explorer offre poche possibilità di stile, Mozilla un po di meno e nulla per altri browsers.
Specialmente il pulsante ‘Sfoglia’ è completamente inaccessibile allo stile css.
Il problema
Prendendo un esempio concreto, per un sito è necessario un campo d’input come di seguito:
Il designer vuole lo stesso stile, più un immagine ’seleziona’ (select) da applicare a tutti i campi di tipo input file. Però, quando sono applicate le normale regole css ai campi di tipo ‘input’, i campi di tipo ‘file’, non funzionano correttamente. Ci sono grande differenze d’interpretazione tra i vari browsers e dare stile css al pulsante di default è impossibile.
Vediamo le differenze.

Non si può dire che sia un campo di input stilizzato per bene!
Ma fino a poco tempo fa, era il meglio che si poteva ottenere.
Anche da notare, la differenza fondamentale con Safari.
Il team di Safari avranno probabilmente deciso di adottare questa soluzione per disabilitare l’opzione di digitare manualmente il nome d’un file e evitare cose del genere. Un problema è che l’utente non può decidere di non caricare un file dopo averlo selezionato.
La soluzione
Per fortuna, un lettore Michael McGrady ha inventato un veramente bel trucco che ci permette (più o meno) di dare un stile css ai campi input di tipo ‘file’.
I crediti di questa soluzione presentata su questa pagina sono suoi, è stato solo aggiunto il position: relative;, qualche note e prove extra, e portato il tutto a Javascript.
Senza questa tecnica, il vostro browser reagisce come di seguito:
Soluzione - JavaScript
In tutta la sua ‘cattiveria’, la soluzione è semplice: generare il finto input e pulsante via Javascript.
Il peggio che può succedere è che il script non funziona. In tale caso, l’utente vedrai solo il vero
<input type="file">. Sicuramente meno gradevole a l’occhio ma sempre accessibile.
L’HTML è ridotto a:
<div class="fileinputs">
<input type="file" class="file">
</div>
Gli altri elementi saranno aggiunti via Javascript.
Il script
Ecco il codice scritto:
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='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;
}
}
}
Spiegazione
Se il browser non supporta il W3C DOM, non fare nulla.
var W3CDOM = (document.createElement && document.getElementsByTagName);
function initFileUploads() {
if (!W3CDOM) return;
Crea il <div class="fakefile"> e il suo contenuto. Sarà duplicato tutte le volte necessarie.
var fakeFileUpload = document.createElement('div');
fakeFileUpload.className = 'fakefile';
fakeFileUpload.appendChild(document.createElement('input'));
var image = document.createElement('img');
image.src='button_select.gif';
fakeFileUpload.appendChild(image);
Controlla tutti gli inputs nella pagina e ignora quelli che non sono <input type="file">.
var x = document.getElementsByTagName('input');
for (var i=0;i<x.length;i++) {
if (x[i].type != 'file') continue;
Un altro controllo: se il <input type="file"> non ha un elemento genitore con un nome di classe fileinputs, ignorarlo.
if (x[i].parentNode.className != 'fileinputs') continue;
Adesso abbiamo trovato un <input type="file"> che ha bisogno di essere lavorato. Primo, aggiungiamo “hidden” al suo nome di classe.
Aggiungiamo le proprietà avanzate (opacity e position) a questa classe visto che possono causare problemi con i browser più vecchi.
x[i].className = 'file hidden';
Duplica il falso campo e aggiungerlo al nodo genitore di <input type="file">.
var clone = fakeFileUpload.cloneNode(true);
x[i].parentNode.appendChild(clone);
Adesso abbiamo stilizzato corettamente il <input type="file">.
Non abbiamo ancora finito. Dobbiamo essere sicuri che l’utente vede il path al file che vuole caricare.
Primo, creiamo una nuova proprietà per il <input type="file"> che punta al finto campo d’input:
x[i].relatedElement = clone.getElementsByTagName('input')[0];
Usiamo quello per accedere facilmente il finto campo appena l’utente cambia il vero <input type="file"> (es: selezionando un file), in modo di potere copiare il suo valore nel finto campo d’input.
Un problema qui: quale evento usare? Il più logico potrebbe essere l’evento change del campo di file: se il suo valore cambia, il finto campo d’input file dovrebbe anche cambiare.
Purtroppo Mozilla 1.6 non supporta l’evento change per i campi di tipo file. Per il bene di Mozilla,
è stato scelto l’evento mouseout che entrà in azione solo dopo che l’utente ha selezionato un file. (Funziona anche per Explorer ma non per Safari).
x[i].onchange = x[i].onmouseout = function () {
this.relatedElement.value = this.value;
}
Problemi e estensione
Un problema rimane: l’utente non può scegliere di caricare un file dopo di tutto.
Supponiamo che l’utente seleziona un file, poi in un secondo pensiero decide di non caricarlo.
In un normale <input type="file">, può semplicemente rimuovere il path e il file non sarà caricato.
Nel nostro esempio invece, è molto difficile farlo.
Provatelo, è fatibile ma non intuitivo.
Allora, quello che vogliamo fare è dare la possibilità a l’utente di selezionare e modificare il contenuto del finto campo d’input file e passare i cambiamenti al vero ‘file upload’.
Autorizzare la selezione è in una certa maniera possibile. Quando l’utente seleziona qualunque parte del vero ‘upload file’, selezioniamo l’intero valore del finto ‘upload file’.
x[i].onselect = function () {
this.relatedElement.select();
}
Sfortunatamente, le sicurezze Javascript non ci permettono di cambiare il valore di un <input type="file">. Di conseguenza, non possiamo lasciare l’utente cambiare manualmente il finto ‘input’. Di là ,è stato scelto, di lasciare a perdere completamente l’evento onselect.
Una altra soluzione possibile sarebbe di aggiungere un pulsante ‘Clear’ al finto input, che aziona un script che completamente elimina il <input type="file"> e ne crea uno nuovo. E un pò confuso ma si potrebbe rimuovere completamente il file selezionato. Questa parte del script non è stato scritto e neanche è sicuro che potrebbe funzionare.
Tracciare l’evento click
Un lettore ha proposto di togliere tutto la parte complicata del CSS, completamente nascondere il campo del input file e ritracciare gli eventi ‘click’ sul finto campo di upload a quello vero. Un idea eccelente e molto più semplice di quella descritta sopra.
fakeField.onclick = function () {
realField.click()
}
Il metodo click() permette di simulare un ‘click’ su un campo del ‘form’. ‘Checkboxes’ sono azionati, pulsante ‘radio’ selezionati e cosi via. Purtroppo Mozilla e Opera non hanno aggiunto quel metodo per i campi di tipo ‘file upload’. Non si capisce il perche, aggiungerlo non sarebbe un rischio al livello sicurezza visto che il peggio che può succedere è che si apre la finestra popup per selezionare il file.
Per quella ragione, non possiamo usare questa soluzione proposta.
















{ 0 comments… add one now }