Dar estilo a un botón input file

en css jquery publicado el 10 de febrero de 2009

Dar estilo a un botón input file para seleccionar un archivo no es tan sencillo como puede ser hacerlo con cualquier otro tipo de botón. La razón es que los navegadores no lo permiten, así que cualquier intento de cambiar la fuente, el color, etc. no funcionará.

Para solucionarlo existe una técnica conocida desde hace tiempo que consiste en reemplazar el control por una imagen. Está documentada en styling an input type="file" o en styling file inputs with css and the dom. Yo prefiero la segunda opción. Shaun Inman escribió un javascript que lo que hace es desplazar el control por debajo de la imagen para que en todo momento el botón del control esté donde se encuentra el puntero del ratón. Al hacer clic lo que realmente está pasando es que se está apretando el botón del control y provoca que se abra la ventana de selección de archivo.

He encontrado un par de cosas que funcionan mal para Internet Explorer así que las he corregido, de paso he añadido una funcionalidad que creo que es importante, y finalmente lo he convertido en un plugin para jQuery. La funcionalidad extra es añadir un campo que muestra el nombre del fichero seleccionado, ya que al no mostrar el campo de texto del control se pierde esta información.

Demo Descargar

HTML

Para estilizar un control de selección de archivo lo único que hay que hacer es asignarle una clase o un id para posteriormente poder seleccionarlo. Por ejemplo:

<input type="file" class="file" name="file" />

En la cabecera del fichero HTML hay que indicar dónde se encuentra la librería jQuery, el plugin y la hoja de estilos. Por ejemplo:

<link href="jquery.si.css" rel="stylesheet" type="text/css" />
<script src="http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js" type="text/javascript"></script>
<script src="jquery.si.js" type="text/javascript"></script>

CSS

El plugin jQuery se encarga de transformar el código

<input type="file" class="file" name="file" />

por

<div class="si">
  <label id="cabinet0" class="cabinet">
    <input type="file" name="file" class="file" />
  </label>
  <div class="uploadButton"></div>
  <div></div>
  <label class="selectedFile"/>
</div>

La hoja de estilos:

div.si label.cabinet {
  width: 156px;
  height: 34px;
  display: block;
  overflow: hidden;
  position: relative;
  z-index: 3;
  float: left;
}
div.si label.cabinet input {
  position: relative;
  left: -140px;
  top: 0;
  height: 100%;
  width: auto !important;
  z-index: 2;
  opacity: 0;
  -moz-opacity: 0;
  filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);
}
div.si div.uploadButton {
  position: relative;
  float: left;
}
div.si div.uploadButton div {
  width: 156px;
  height: 34px;
  background: url(examinar.png) 0 0 no-repeat;
  left: -156px;
  position: absolute;
  z-index: 1;
}
div.si label.selectedFile {
  margin-left: 5px;
  line-height: 34px;
}

Los estilos aplicados son similares a los de Shaun. Básicamente las diferencias son para corregir los pequeños problemas que he encontrado:

  • para evitar un bug conocido de Internet Explorer con el overflow: hidden hay que añadir position: relative. Esto provocaba que una vez pasado el puntero del ratón por encima del botón, en cualquier lugar de la ventana que se hiciese clic, se abriera el seleccionador de archivos.
  • en vez de utilizar una imagen de fondo para el label.cabinet, utilizo una capa que posiciono relativamente encima del label. La razón es porque cuando recibía el foco en Internet Explorer la imagen se desplazaba un poco.
  • desplazo el control de -140px hacia la izquierda para evitar que en Internet Explorer se vea el cursor del campo de texto del control parpadear

Para la demo utilizo la imagen examinar.png de tamaño 156px por 34px. Las dos primeras líneas de la definición de label.cabinet y las cuatro primeras de div.uploadButton div se deberán modificar para adaptar el plugin a otra imagen. Para centrar verticalmente el texto con el nombre del fichero seleccionado, le aplico el mismo line-height que el height de la imagen.

Javascript

Una vez incluidas las librerías en la cabecera del fichero HTML, para que el plugin actúe hay que hacer la siguiente llamada:

 $(document).ready(function() {
  $("input.file").si();
}); 

Esto significa que todos los input de tipo file con la clase file se estilizarán. Para más detalles, ver cómo funcionan los selectores de jQuery.

Compatibilidad

Funciona con cualquier navegador que soporta opacidad. Esto significa que para versiones anteriores a la 5.5 de Internet Explorer o anteriores a la 9 de Opera el plugin no actuará y se mostrará el control por defecto.

Si javascript no está activado, se mostrará el control por defecto.


14 comentarios

  • iven

    22 de diciembre de 2010

    Creo que tiene un problema importante y es q la funcionalidad de elimitar la url absoluta del archivo que se esta subiendo, lleva a tener que replantear el upload en el sistema.

  • camus

    19 de abril de 2011

    basura

  • andres

    26 de junio de 2011

    Camus cual es tu blog quiero leer tus aportes al desarrollo web, no aplicare esta tecnica pero respeto tu trabajo

  • camus

    20 de julio de 2011

    basura, pense q darias una mejor opcion a esto o mas estilos. me hicistes perder el time.

  • Ismael Correa

    11 de agosto de 2011

    Me gustó mucho!! Muchas gracias por el aporte!! Sigue así!! :D :D Lo voy a aplicar en mi sitio ajajajj

  • almendro

    29 de diciembre de 2011

    Muy buen aporte, lo voy a probar. Incluso veré si puedo modificar eso de la URL absoluta como una opción parametrizable. No hagas caso a los comentarios no constructivos de camus, al parecer es de los que prefieren todo en bandeja de plata en vez de aprender y crear. Saludos

  • Ladymerche

    20 de enero de 2012

    Muy buena explicación, lo mejor es que te da ideas y ordenar los pasos desde la aplicación del css hasta la llamada al jQuery. Me parece muy buen aporte, lo explicas con claridad. Digo lo mismo q almendro, no hagas caso de comentarios de sátrapas del copy-paste, ;D

  • test

    17 de marzo de 2012

    Mejor lo dejo como esta :) gracias!

  • test

    26 de marzo de 2012

    buen aporte, pero no funciona en ie8

  • groswell

    14 de mayo de 2012

    En ie8 se debe hacer doble click en el botón. Investigando porqué.

  • groswell

    15 de mayo de 2012

    Pequeña chapuza que corrige el hecho de tener que hacer doble click en el botón para que se abra el examinar. Debemos añadir este código en la función $(document).ready(function(). Así $(document).ready(function() { if (navigator.appVersion.indexOf('MSIE') > -1){ $("input.file").mouseup(function(){ $("input.file").click();}); } } El Código es JQuery.

  • groswell

    15 de mayo de 2012

    Comprobación de versión del navegador en jquery. if(jQuery.browser.msie == true && jQuery.browser.version=="8.0") o if($.browser.msie == true && $.browser.version=="8.0") Ojo que jQuery.browser lo quieren mover en futuras versiones de sitio. con la 1.7.2 funciona.

  • groswell

    17 de mayo de 2012

    Después de pensar un poco. Porqué tanta tonteria pones un input type=file con estilo display none donde queiras de tu form. Luego haces un botón que onclick haga un click del input type=file y listo. Luego si quieres muestras el valor del fichero al lado del botón. más facil y menos rollos. que si con este navegador no va y el otro si. A parte la solucion anterior peta al hacer un submit en explorer 8. así que mejor no usarla.

  • maria

    22 de julio de 2012

    ola muy buena post

¿Pensando en contratar mis servicios?

Lee esto

Algunos apuntes que te pueden resultar útiles:

  • Soy programador web y aunque me encantaría diseñar, no entra dentro de mis competencias. Para poder desarrollar una página web necesito el diseño gráfico final. Si no conoces a ningún diseñador te puedo recomendar alguno con el que haya colaborado y su trabajo me haya parecido bueno.
  • No soy especialista SEO, aunque uso reglas básicas y sentido común a la hora de estructurar el contenido lo que se traduce en una mejora en el posicionamiento.
  • Si es posible envíame toda la información que creas que necesite para presentarte un presupuesto. "Programar una web como esta" no es suficiente, no podré evaluar el coste porque no sabré que funcionalidades tiene. Necesito que me las especifiques.
  • Si el proyecto es de gran envergadura, seguramente que te pida cuál es tu presupuesto. La razón es que si se aleja mucho de lo que a primera vista considero puede costar, te contestaré enseguida que no es un trabajo que pueda hacer.
  • Si existe una fecha de entrega del proyecto, comunícamela. En función de mi disponibilidad te contestaré si es un trabajo que pueda hacer o no.

Para contactarme puedes usar el formulario de contacto.