Laboratorio: filtrado de tablas
17:30 H (CET)| Temas: AI/Usabilidad · Javascript · Laboratorio
Leyendo una de las entradas de Usolab, en la que habla de colocar filtros en las tablas: Icono para el control "filtrar", no he podido resistirme a llevar a cabo tan buena idea.

En el ejemplo voy a seleccionar una tabla, y modificarla para admitir ordenación por columnas, filtrado mediante expresiones regulares y distinción de filas alternas. Además de incluir un icono en el filtro que indica el estado de filtrado.
Lo primero que debemos hacer es seleccionar la tabla en cuestión y tratarla, modificando la cabecera (th) incluyendo un enlace para poder ejecutar la acción de ordenar, un icono que indica qué columna está ordenada y en qué sentido y una caja de texto para indicar el filtrado.
La caja de texto tiene la particularidad de mostrar un icono que indica el estado del filtro, cuanto más lleno esté de "líquido", más mostrará. Para lo cual usamos una imagen como fondo de la caja de texto e iremos moviendo verticalmente para mostrar los distintos estados.
![]()
El siguiente paso es reordenar la tabla, para ello usaremos la función sort para modificar arrays, cuando se pulse en una columna se llamará al método de ordenación y redibujaremos la tabla (mediante DOM).
La parte más importante se la lleva la caja de texto, cuando pulsamos una tecla en cualquiera de las cajas, comprobaremos que filas se deben mostrar, para ello tendrán que cumplir la expresión regular que se indica en la caja de texto de cada columna.
Para redibujar la tabla, crearemos una nueva mediante DOM y luego sustituiremos la anterior por la nueva. Es curioso, por decir algo y no soltar un par de malas palabras ya que estuve bastante tiempo intentando ver por qué no funcionaba, que en IE cuando sustituyo nodos mediante replaceChild no funciona correctamente, el contenido se sustituye pero no se llega a dibujar y para solucionar este "problemilla", tengo que modificar el innerHTML añadiéndole una capa vacía (<div></div>).
Aquí os dejo el código:
// Almaceno los datos de la tabla que leo, la primera posicion indica si se debe mostrar o no, y la segunda posicion contiene los datos de la fila
var datos = new Array();
// Almaceno la cabecera de la tabla
var cabecera = new Array();
// Columna seleccionada para ordenar
var seleccionado = 0;
// Si el orden es ascendente/descendente en cada columna
var orden = new Array(true, true, true);
// El contenido de cada uno de los filtros
var filtros = new Array("", "", "");
// Indice del filtro actual
var filtrado = 0;
// Lee la tabla y guarda los datos y modifica la cabecera para incluir el orden y el filtro
function transformar() {
// Obtengo la tabla
var tabla = document.getElementById("tabla");
// Almaceno y modifico la cabecera, añadiendo el orden y el filtro
var cab = tabla.rows[0];
for (var i=0; i<cab.cells.length; i++) {
cabecera[i] = cab.cells[i].innerHTML;
cab.cells[i].innerHTML = (i != seleccionado) ?
'<a href="javascript:ordenar('+i+')">'+cab.cells[i].innerHTML+'<img src="ord-no.gif" alt="NO" /></a>' :
(orden[i]? '<a href="javascript:ordenar('+i+')">'+cab.cells[i].innerHTML+'<img src="ord-des.gif" alt="DES" /></a>' :
'<a href="javascript:ordenar('+i+')">'+cab.cells[i].innerHTML+'<img src="ord-asc.gif" alt="ASC" /></a>');
cab.cells[i].innerHTML += '<br /><input type="text" id="filtro'+i+'" class="filtro" maxlength="10" onkeyup="filtro(event, '+i+')" value="'+filtros[i]+'" autocomplete="off"/>';
}
// Guardo los datos de la tabla y marco filas alternas
for (var i=1; i<tabla.rows.length; i++) {
if (i%2 == 0) {
tabla.rows[i].className = "par";
}
var aux = new Array();
for (var j=0; j<tabla.rows[i].cells.length; j++) {
aux[j] = tabla.rows[i].cells[j].innerHTML;
}
datos[datos.length] = new Array(true, aux);
}
}
// Funcion que ordena las filas segun la columna que se haya pulsado
function organizar(a, b) {
var signo = orden[seleccionado]? 1:-1;
return (a[1][seleccionado] > b[1][seleccionado]) ? signo : -signo;
}
// Ordena y dibuja la tabla
function ordenar(i) {
var tabla = document.getElementById("tabla");
// si es la misma col se cambia el sentido
if (seleccionado == i) {
orden[seleccionado] = !orden[seleccionado];
} else {
orden[seleccionado] = true;
seleccionado = i;
}
// Se ordena la tabla y se dibuja
datos.sort(organizar);
dibujarTabla();
}
// Filtra las filas segun el contenidos de los filtros de cada columna
function filtro(evt, pos) {
for (var i=0; i<datos.length; i++) {
datos[i][0] = true;
for (var j=0; j<datos[i][1].length; j++) {
var txt = document.getElementById("filtro"+j).value;
filtros[j] = txt;
if (txt != "") {
// Puede fallar si se usa una expresion regular incompleta
try {
datos[i][0] &= eval('datos[i][1][j].match(/'+txt+'/i) != null');
} catch (error) {}
}
}
}
filtrado = pos;
// dibujo la tabla
dibujarTabla();
// Selecciono la caja de texto del filtro actual
var obj = document.getElementById("filtro"+filtrado);
obj.focus();
// Coloco el cursor al final de texto
if (obj.createTextRange) {
var r = obj.createTextRange();
r.moveStart("character", obj.value.length+1);
r.moveEnd("character", obj.value.length+1);
r.select();
} else if (obj.setSelectionRange) {
obj.setSelectionRange(obj.value.length+1, obj.value.length+1);
}
}
// Dibuja la tabla
function dibujarTabla() {
var _tabla = document.getElementById("tabla");
// Me creo una copia de la tabla
var tabla = document.createElement("TABLE");
tabla.id = "tabla";
tabla.border = "0";
tabla.setAttribute("cellspacing","0");
tabla.setAttribute("cellpadding", "2");
// Creamos los THs
var tr = document.createElement("TR");
for (var i=0; i<cabecera.length; i++) {
var th = document.createElement("TH");
th.innerHTML = ((i != seleccionado) ?
'<a href="javascript:ordenar('+i+')">'+cabecera[i]+ '<img src="ord-no.gif" alt="NO" /></a>' :
(orden[i]? '<a href="javascript:ordenar('+i+')">'+cabecera[i]+ '<img src="ord-des.gif" alt="DES" /></a>' :
'<a href="javascript:ordenar('+i+')">'+cabecera[i]+ '<img src="ord-asc.gif" alt="ASC" /></a>'));
th.innerHTML += '<br /><input type="text" id="filtro'+i+'" class="filtro" maxlength="10" onkeyup="filtro(event, '+i+')" value="'+filtros[i]+'" autocomplete="off"/>';
tr.appendChild(th);
}
tabla.appendChild(tr);
// Creamos los TRs
var cont = 0;
for (var i=0; i<datos.length; i++) {
if (datos[i][0]) {
var tr = document.createElement("TR");
tr.className = (cont%2==0)? "":"par";
cont++;
for (var j=0; j<datos[i][1].length; j++) {
var td = document.createElement("TD");
td.innerHTML = datos[i][1][j];
tr.appendChild(td);
}
tabla.appendChild(tr);
}
}
// Reemplazo la tabla actual por la nueva
var padre = _tabla.parentNode;
padre.replaceChild(tabla, _tabla);
// ¿¿PERO QUE ES ESTO?? SI NO LO HAGO NO FUNCIONA EN EL INTERNET EXPLORER
padre.innerHTML=padre.innerHTML+"<div></div>";
// Situo el fondo del filtro para saber en que estado está
for (var i=0; i<filtros.length; i++) {
var obj = document.getElementById("filtro"+i);
if (filtros[i].length > 3) {
obj.style.backgroundPosition = "88px -45px";
} else {
obj.style.backgroundPosition = "88px -"+(filtros[i].length*15)+"px";
}
}
}
Relacionados
Feedback (14) » Formulario
5. willy ~ Sábado, 19 Ago 2006 | 01:15H:
Saludos...
bueno solo decirte q es exelente lo q hicistes, eres un genio yo queria hacer lo mismo pero tenia q estudiar muchas cosas para hacer lo q tu hicistes, y bueno preguntarte si no habria problemas en pasarlo en una clase Prototype... pero primero debo de entender lo q hicistes..
hasta luego, q exelente trabajo....
7. wily ~ Martes, 29 Ago 2006 | 21:36H:
Hola soy yo denuevo willy, mira ahora estoy buscando un metodo para no sobrecargar la pagina cuando se tenga mas de 600 datos para filtrar, por q congela toda la pagina hasta q termine de procesar...
o sabras de algun metodo, agradeceria tu ayuda...
ah ya termine de colocarlo en una clase javascript voy a ponerlo en linea para q veas el codigo y me deas tu sugerencia..
saludos...
8. Luis ~ Miércoles, 30 Ago 2006 | 08:29H:
Hola willy.
Mi primer consejo sería que usaras paginación, claro, que el poner paginacion a veces no se puede hacer (por deseo del cliente, por ejemplo).
Déjame pensar algo y te cuento, pero me temo que no va a ser una solución limpia (vamos, una chapucilla), porque el tema es que la ordenación se hace mediante el método de javascript.
10. andy ~ Martes, 21 Nov 2006 | 22:30H:
hola que tal tu programa realmente es muy interesante, pero la funcion match lo único que sabes es si un cadena contiene o no la palabra q ingrese. me explico he tratado de modificar cosa de que filtre aquellas filas qeu empiezen con el contenido de mi palabra no que las contengan como tu programa lo hace. pero no he tenído exito, será que me puedes ayudar con esto por fa!!
11. Luis ~ Martes, 21 Nov 2006 | 23:35H:
Hola Andy.
Es fácil, tienes que añadirle un ^ al incio de la expresión regular, por ejemplo: /abc/ reconocerá los strings abcde y xabcde, pero si le añadimos ^ (que equivale al inicio de línea) /^abc/ solo te reconocerá abcde, ya que zabcde empieza por ^x y no por ^a.
Después de este rollo, tienes que cambiar:
eval('datos[i][1][j].match(/^'+txt+'/i) != null');
12. Kevin ~ Jueves, 08 Feb 2007 | 22:06H:
Hola, que tal muchachos, primero que Nada Felicitarlos por tan excelente herrramienta, yo tambien soy una persona que me gusta que la interfaz de los sistemas sean lo mas amigables posibles y que la experiencia del usuario se vea incrementada (shift experience) Bueno realmente lo que quisiera saber es que si han hecho un control autocomplete el cual a medida que escribas en el te aparezca, me imagino que un div, con las opciones mas coincidente y te permita seleccionar utilzando las flechas arriba y abajo, estilo las aplicaciones hechas con ajax como gmail
Se los agradezco mucho si me echan una mano con esto.
Saludos.
14. ramon ~ Sábado, 10 Feb 2007 | 18:54H:
Hola muchachos! buen trabajo el que habéis hecho. EXCELENTE!
No obstante estoy intentanto que cada fila contenga un "input" de tipo "button" para que al presionar sobre dicho boton se carguen los datos de la fila correspondiente en una caja de texto.
Haber si me podéis hechar una mano.
Saludos a todos.

