Sortable con jQuery

De ChuWiki

jQuery nos permite hacer fácilmente listas cuyos items se pueden ordenar arrastrándolos con el ratón. Incluso llevarnos items de unas listas a otras. Vamos a ver un pequeño ejemplo de cómo hacerlo.


Hacer las listas en html[editar]

Lo primero, bien como consulta a la base de datos y con algo como php o jsp, podemos construir el html con las listas que nos interesan. Vamos con un ejemplo sencillo como este

<!doctype html>
<html>
  <head>
     <meta charset="utf-8">
     <title>Demo</title>
  </head>

  <body>
     <script src="jquery-1.7.1.js"></script>
     <script src="jquery-ui-1.8.16.custom.min.js"></script>
     <script>
        $(document).ready(function() {
           $("#lista1").sortable({
              update : function(event, ui) {
                 $('#resultado').append(ui.item.parent().attr('id')+"<br/>");
                 $('#resultado').append(ui.item.parent().sortable('toArray')+"<br/>");
              }});
        });   
     </script>
     <ul id="lista1" >
        <li id="11">uno</li>
        <li id="12">dos</li>
        <li id="13">tres</li>
        <li id="14">cuatro</li>
     </ul>
     <div id="resultado"></div>
  </body>
</html>

En la parte html, sólo un <ul> con id "lista" y dentro una serie de elementos <li> con un id cada uno y el texto correspondiente. Para ordenar podemos poner cualquier tipo de elemento que queramos anidado a cualquier otro, luego es cuestión de configurar jQuery.

En la parte de javascript, hemos incluido el fichero base de jQuery-1.7.1.js y la librería de interfaz de usuario jquery-ui-1.8.16.custom.min.js necesaria para hacer el arrastrado de items y la lista ordenable.

Luego, en $(document).ready() indicamos a qué función debe llamar jQuery cuando la página esté completamente cargada. En esta función daremos a la lista el caracter de ordenable. Nos bastaría con un

 $("#lista1").sortable();

para hacer que la lista fuese ordenable arrastrando con el ratón, pero pretendemos también enterarnos de cuando se cambia un item de orden para hacer una serie de cosas. En este ejemplo sólo sacaremos en el <div id="resultado"> alguna información sobre el cambio realizado. Para enterarnos de cuando cambia algo, añadimos como parámetro a sortable una propiedad "update" en la que ponemos la función a la que queremos que nos llamen. Esa función recogerá dos parámetros

  • event : indicando el tipo de evento de ratón, como coordenadas x,y de donde sale el item, coordenadas x,y donde llega, botones pulsados en el ratón, etc.
  • ui : Este indica algunos datos útiles sobre el cambio, como el item que ha cambiado, de qué lista viene, etc.

En nuestra función vamos a mostar en pantalla el nombre de la lista (más que nada para ver cómo se puede saber el nombre de la lista a partir del evento) y a sacar el nuevo orden de los elementos.

ui.item contiene el elemento de la lista (el <li>) que ha cambiado. Llamando a su función parent(), obtenemos la lista (el <ul>) en el que está ahora ubicado. Con attr('id') obtenemos el id del <ul> que contiene ahora al item

 /* Saca en el <div id="resultado"> el id del <ul> que contiene ahora al item */
 $('#resultado').append(ui.item.parent().attr('id')+"<br/>");


Guardando el orden en el servidor[editar]

La llamada a sortable("toArray") nos devuelve un array de String con los id de los <li> en la lista, ordenados en el mismo orden que está en la lista. Sacamos esto también en el <div id="resultado">

 $('#resultado').append(ui.item.parent().sortable('toArray')+"<br/>");

Esto último puede ser útil no para sacar esta lista en pantalla otra vez, sino por ejemplo, para pasarlo por AJAX al servidor y que este se encargue de almacenar en base de datos (u otro sitio) el nuevo orden de los items. Por ejemplo, si en el servidor hacemos un pequeño php como este

<?php if ($_GET['orden']) {
	foreach ($_GET['orden'] as $id) {
		echo $id.',';
	}
} else  {
	echo 'sin orden'; 
}?>

y el código javascript de update lo modificamos para que tenga esto

           $("#lista1").sortable({
              update : function(event, ui) {
		 var request = $.ajax('un.php', { data : { orden : ui.item.parent().sortable('toArray') }});
		 request.done(function(mensaje) {
			 $('#resultado').append(mensaje+"<br/>");
		 });
              }});

Vemos que cada vez que se ordena la lista, se hace una llamada ajax al servidor, al fichero un,php, pasándole por GET un atributo "orden" que contiene el array de id de items de la lista ordenados. Si miramos el código de un.php, vemos que en $_GET['orden'] tenemos disponible ese array. Ahí simplemente lo convertimos a texto y lo devolvemos, pero lo suyo sería almacenar ese orden en base de datos.


Múltiples listas[editar]

jQuery nos permite hacer simultáneamente varias listas ordenables y nos permite incluso pasar elementos de una a otra. El siguiente código permite hacerlo con tres listas

<!doctype html>
<html>
  <head>
     <meta charset="utf-8">
     <title>Demo</title>
     <style type="text/css">
        ul {
           min-height : 10px;
        }
     </style>
  </head>

  <body>
     <script src="jquery-1.7.1.js"></script>
     <script src="jquery-ui-1.8.16.custom.min.js"></script>
     <script>
        $(document).ready(function() {
           $("#lista1").sortable({
              connectWith : ['#lista2','#lista3'],
              update : function(event, ui) {
                 if (ui.sender){
                    $('#resultado').append(ui.sender.attr('id')+"->");
                    } else {
                    $('#resultado').append(this.id+"->");
                 }
                 $('#resultado').append(ui.item.parent().attr('id')+"<br/>");
              }});
           $("#lista2").sortable({connectWith : ['#lista1','#lista3']});
           $("#lista3").sortable({connectWith : ['#lista1','#lista2']});
        });   
     </script>
     <ul id="lista1" >
        <li id="11">uno</li>
        <li id="12">dos</li>
        <li id="13">tres</li>
        <li id="14">cuatro</li>
     </ul>
     <ul id="lista2" >
        <li id="21">a</li>
        <li id="22">b</li>
        <li id="23">c</li>
        <li id="24">d</li>
     </ul>
     <ul id="lista3" >
        <li id="31">alfa</li>
        <li id="32">beta</li>
        <li id="33">gamma</li>
        <li id="34">delta</li>
     </ul>
     <div id="resultado"></div>
  </body>
</html>

Tenemos tres listas ul lista1, lista2 y lista3 con varios items cada una. En jQuery las hemos hecho todas ellas sortable, pero pasamos como parámetro a sortable lo siguiente:

 $("#lista1").sortable({connectWith : ['#lista2','#lista3'], ...

Este connectWith permite a esta lista "soltar" elementos en las otras dos listas que también deben ser sortable. Podemos, por supuesto, conectar con las que queramos o con ninguna otra. A nuestro gusto. En el callback update hemos puesto un trozo de código que pretende identificar de qué lista viene el elemento y en qué lista termina.

              update : function(event, ui) {
                 if (ui.sender){
                    $('#resultado').append(ui.sender.attr('id')+"->");
                    } else {
                    $('#resultado').append(this.id+"->");
                 }
                 $('#resultado').append(ui.item.parent().attr('id')+"<br/>");

ui.sender es la lista de origen, pero estará sin definir si el item no viene de otra lista, por ello conviene comprobarlo. Si no viene de otra lista, es que procede de esta misma, por ello se usa this.id. En caso de que ui.sender exista, ui.sender.attr('id') nos dará el id de la lista de origen.

ui.item.parent().attr('id') nos dará el id de la lista en la que ahora está ubicado el item que acabamos de soltar.

Por supuesto, deberíamos poner algo parecido a lo que hicimos con persistencia en el caso anterior si queremos mantener el orden en alguna base de datos y que no se pierda en cuanto cerremos el navegador. También deberíamos añadir el callback update en las tres listas.

Un pequeño detalle. Si una vez tenemos el ejemplo lanzado vamos moviendo items de una lista a otra, es posible que en algún momento dejemos una de las listas vacía. Entonces no podemos volver a meter items en esa lista vacía debido a que su tamaño es cero y no podemos acertar con el ratón en ella para soltar el item. Para evitarlo, en el head del html hemos puesto algo de estilo css, dando un tamaño mínimo a la altura de los ul. De esta forma, aunque se quede vacío, tendrá un tamaño de 10 pixels de alto y así podemos arrastrar mejor los items a ese hueco para soltarlos.


Enlaces[editar]