Los que alguna vez jugueteamos con el diseño web, muy seguramente nos encontramos con el problema de personalizar los input file. Inteentaré mostrar la forma en que logré personalizar los input file usando sólo CSS, JS, y el DOM de una manera sencilla. Que además nos permite eliminar archivos que ya estan en la lista para ser subidos al servidor.
La idea es convertir esto:
en esto:
<html>
<head>
<link href="input.css" rel="stylesheet" />
<script src="input.js"></script>
</head>
<body>
<center>
<form action="http://127.0.0.1:5000/upload" enctype="multipart/form-data" method="POST" id="frm_upload">
<div id="upload">
<input id="archivos" type="file" name="file[]" multiple="multiple" onchange="seleccionados();"/>
</div>
<span id="filename"></span>
<p/>
<input type="button" name="submit" value="Subir" onclick="subir();"/>
</form>
</center>
</body>
</html>
Para eso, vamos a usar un formulario normal, estructurado así:
un div con id "upload" dentro del cual meteremos el input file; es el div que moldearemos para que tome la forma que nuestro upload tenga.
un input file configurado para seleccionar multiples archivos. Cuando se selecciona un archivo (evento onchange) llama a la función seleccionados
un span con id "filename" que es donde mostraremos los archivos seleccionados
un input button que se encargara cuando hagamos click sobre el de enviar el formulario.
El css (input.css):
input[type=file] {
opacity: 0;
width: 100%;
height: 100%;
}
#upload {
background: url("https://cdn2.iconfinder.com/data/icons/internet/512/Upload-128.png") #111 center center no-repeat;
width: 150px;
border-radius: 5px;
height: 150px;
box-shadow: 0 0 5px #111;
}
Este sencillo CSS simplemente hace invisible el input file (opacity: 0) y le da un ancho y alto de 100% para que ocupe la totalidad del div (upload)
en el bloque #upload, damos la apariencia que queremos que tengo nuestro upload agrgando un simple background, una sombra y unas dimensiones (150x150).
Hasta ahora, nuestro upload se ve así:
Si lo clickeamos, nos abre la ventana para seleccionar los archivos, pero una vez seleccionados, NO LOS MUESTRA. Así que ahora vamos a codear el JS:
El JS (input.js): Los navegadores no nos dejan modificar los datos de los input file, pero si que nos permite juguetear un poco con los datos. Así que lo que haremos sera obtener la lista de los elementos seleccionados y la guardaremos en un array que podremos modificar (ara eliminar los elemento que no queremos subir) y a la hora de enviar el formulario al servidor, lo que haremos es simular un formulario que llenaremos con la informacion del array y lo enviamos al servidor.
La función seleccionados:
//Creamos el array que usaremos para almacenar y enviar la informacion
var ELEMENTOS = new Array();
function seleccionados() {
//Obtenemos el inputfile
var input = document.getElementById("archivos");
//Obtenemos los archivos
var archivos = input.files;
//Aca es donde mostraremos los elementos seleccionados
var elementos = document.getElementById("filename");
//Limpiamos los elementos seleccionados anteriormente
elementos.innerHTML = " ";
ELEMENTOS = [];
//Recorremos archivo por archivo
for (item=0; item< archivos.length; item++) {
//duplicamos el archivo seleccionado en el array elementos
ELEMENTOS[item] = archivos[item];
//Creamos un div donde mostramos el archivo seleccionado
crear_div("file", elementos, archivos[item].name);
}
}
La funcion crear_div: Esta funcion se encarga de crear un div, agregarle propiedades y luego incrustarlo en un elemento padre
//funcion que crea un div
function crear_div(clase, padre, informacion) {
//Escapamos la variable informacion para evitar posibles XSS
informacion = escape(informacion);
//Creamos el div
var div = document.createElement("div");
//Le agregamos al div sus propiedades
div.setAttribute("class", clase);
div.setAttribute("title", "click para eliminar");
//en caso de hacer click en un elemento, lo elimina de la lista de los archivos a subir
div.onclick = function() {
var index = -1;
//eliminamos el archivo seleccionado de la lista
for (indice=0; indice < ELEMENTOS.length; indice++) {
//Buscamos el index del elemnto seleccionado para ser eliminado
if (ELEMENTOS[indice].name == unescape(informacion)) {
index = indice;
}
}
if (index != -1) {
//removemos el elemento de la lista
ELEMENTOS.splice(index, 1);
padre.removeChild(div);
} else {
alert("No se pudo eliminar el elemento");
}
}
//Agregamos el div al padre
padre.appendChild(div);
div.innerHTML = informacion;
}
Hasta ahora, nuestro upload se ve así.
Sólo nos resta darle un estilo a los div que contienen el nombre de los archivos seleccionados (que tienen la clase file):
.file {
background: #111;
display: inline-block;
margin: 5px;
color: #FFF;
line-height: 25px;
padding: 5px;
border-radius: 5px;
box-shadow: 0 0 5px #000;
}
Y programar la función para subir los archivos al servidor:
function subir() {
//creamos un nuevo formulario y lo llenamos con la información del array con los elementos a enviar
var formulario = new FormData();
for (file=0; file < ELEMENTOS.length; file++) {
formulario.append("file[]", ELEMENTOS[file]);
}
//obtenemos el action y method del formulario original
var frm = document.getElementById("frm_upload");
var metodo = frm.method.toUpperCase();
var action = frm.action;
//enviamos el formulario
var request = new XMLHttpRequest;
request.open(metodo, action);
request.send(formulario);
}
Y nuestro uploader está listo.
Saludos!