Elementos ordenables con "sortable" de jQueryUI

  • Por
Cómo hacer elementos cuyos items se puedan ordenar, por medio del interactor sortable de jQueryUI.

Dentro de jQueryUI hay muchos componentes que son realmente sencillos de poner en marcha. Es el caso de los elementos sortable, que no son widgets precisamente, sino que están clasificados dentro de los interactions. En el Manual de jQueryUI ya hemos visto en marcha varios plugins para interacción, como los droppable o draggable.

En concreto el comportamiento que vamos a explorar ahora se llama sortable y lo traduciríamos por ordenable. Sirve para generar interacción con el cliente, de modo que pueda ordenar los items de, por ejemplo, una lista. Es muy sencillo de implementar, como muchos otros plugins de jQueryUI.

Veamos un ejemplo de lista HTML que luego convertiremos en un sortable.

<ul id="ordenable"> 
   <li>Elemento ordenable</li> 
   <li>Otro que se ordena</li> 
   <li>...</li> 
</ul>

Para darle la funcionalidad de ordenación a los elementos de esta lista, podemos invocar a la interacción con una única línea de código.

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

Ahora esos elementos de la lista se pueden ordenar perfectamente, arrastrando y soltando en otra posición. Podemos ver el ejemplo en marcha.

Claro que la lista se ve muy parecida a una lista normal y nadie se va a dar cuenta que esos elementos se pueden ordenar con una acción de arrastrar y soltar, pero para eso podemos asignar algunas propiedades especiales a los elementos ordenable y algunos estilos CSS. Todo esto lo veremos a continuación.

Colocar iconos de jQueryUI a los elementos de la lista

Comenzamos viendo cómo podemos colocar unos iconos en los elementos de la lista, con unas flechitas que indiquen que se pueden ordenar. Como vemos, dentro del HTML de cada LI, colocamos un SPAN con una clase determinada que nos permite que se convierta en un icono.

<ul id="ordenable">
<li><span class="ui-icon ui-icon-arrowthick-2-n-s"></span> Elemento ordenable</li>
<li><span class="ui-icon ui-icon-arrowthick-2-n-s"></span> Otro que se ordena</li>
<li><span class="ui-icon ui-icon-arrowthick-2-n-s"></span> ...</li>
</ul>
Nota: La clase ui-icon y ui-icon-arrowthick-2-n-s son dos clases pertenecientes a jQueryUI que formarían parte del framework CSS que viene incluido con estas librerías y que se explicó en el artículo Librería CSS de jQueryUI.

Configurar propiedades del elemento ordenable

También podemos hacer un pequeño cambio para que, al arrastrar un elemento de la lista para ordenarlo, se cree un espacio en blanco entre los elementos que se posicionaría si lo soltásemos en ese momento.

$("#ordenable").sortable({
   placeholder: "ui-state-highlight" 
});

El valor que asignemos a placeholder es el correspondiente a la clase CSS que queremos que se aplique para estilizar el lugar donde se soltaría el elemento. En este caso utilizamos "ui-state-highlight", que es una de las clases del framework CSS de jQueryUI.

Además, en nuestro CSS podríamos aplicar estilos a esta clase, o cualquier otra que tengamos a bien utilizar.

.ui-state-highlight { height: 1.5em; line-height: 1.2em; }
Nota: Otra utilidad muy interesante que nos permite jQueryUI y que está disponible en cualquier download de las librerías, por formar parte del core, es invocar el método disableSelection() sobre un elemento ordenable. Este método, no documentado en el momento de escribir este artículo, sirve para evitar que el texto de un elemento se pueda seleccionar.
$("#ordenable").disableSelection();
Es muy útil debido a que a menudo, al intentar ordenar un elemento, podríamos acabar seleccionando el texto del elemento en vez de arrastrarlo a otra posición.

Podemos ver una lista un poco más elaborada, con los estilos y configuraciones vistos anteriormente en una página aparte.

Existen multitud de propiedades en los elementos sortable que nos sirven para hacer diversas cosas, es cuestión de informarse de ellos en la documentación de jQueryUI. Por ejemplo, si queremos que algunos de los elementos de la lista no se puedan ordenar, podemos utilizar la propiedad cancel, asignando el selector de los elementos no ordenables.

<ul id="ordenable"> 
   <li>Elemento ordenable</li> 
   <li class="no">Otro que se ordena</li> 
   <li>otro elemento</li> 
</ul>

$("#ordenable").sortable({ 
   cancel: ".no" 
});

Con esto conseguimos que los elementos de la lista que tienen la clase "no" estén inhabilitados para modificar su posición.

Enviar el nuevo orden por Ajax a otra página del servidor

Ahora la pregunta que muchos de vosotros os podéis hacer ¿Cómo puedo procesar el orden actual, para almacenarlo en una base de datos o algo parecido?.

Afortunadamente, jQueryUI tiene métodos para hacer lo que queramos una vez cambiado el orden de los elementos. Existe el evento "update" que sirve justamente para realizar cualquier tipo de acción cuando se altera el orden de los elementos que hemos convertido en sortable.

Ahora vamos a ver un ejemplo para enviar por Ajax al servidor el nuevo orden de los elementos. Ese orden lo enviaremos en una cadena por GET y ya en la programación del servidor podréis hacer lo que se os antoje, como almacenarlo en una base de datos, enviarlo por email o lo que sea.

Veamos el siguiente código:

$("#ordenable").sortable({ 
   placeholder: "ui-state-highlight", 
   update: function(){ 
      var ordenElementos = $(this).sortable("toArray").toString(); 
      $.get("cambia_orden.php",{nuevo_orden: ordenElementos},function (respuesta){ 
         alert(respuesta); 
      }); 
   } 
});

Con el evento update definimos las acciones a ejecutar al alterarse el orden. En este ejemplo generamos un texto a partir de la lista, y luego ejecutamos un $.get() para enviarlo por Ajax a una página PHP. Cuando se reciban los datos que devuelve esa página PHP se mostrarán en una caja de alerta.

Esto se puede ver en marcha en una página aparte. Os sugerimos activar el Firebug para ver por consola la forma que tiene la solicitud Ajax que estamos ejecutando con $.get().

Conclusión

Hemos visto hasta qué punto es potente el sistema de ordenar elementos que implementa jQueryUI. Nosotros tenemos que definir muchos asuntos, como los estilos, comportamientos especiales, etc. pero el sistema es capaz de realizar la mayoría de las cosas que nos podamos imaginar.

Como has podido ver, no solo se trata de hacer un sistema que se pueda manejar desde el lado del cliente para poder ordenar los elementos, sino también una interfaz que pueda comunicar con el servidor para enviar a cualquier lugar las actualizaciones del orden y efectuar cualquier tipo de operación.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

tomy_molina

03/5/2012
Interesante
Interesante Miguel, esto luego lo convinas con HTML5 local Storage (o cookies) y puedes hacer aplicaciones muy buenas.

ernestosoto

04/5/2012
Todo resuelto
Gracias Miguel!
Voy a implementar tus recomendaciones de este artículo para un sistema de ordenación que estaba creando. Me estaba haciendo la picha un lío y no me estaba saliendo todo bien. Así es mucho más fácil!

Alberto

01/4/2017
el ejemplo con toArray NO FUNCIONA!
Me has ayudado un montón con el ejemplo, pero desde el punto de vista de "no te fies ni de los que saben mas que tu". ¿Has probado el ejemplo que escribes con "toArray"?

Para que funcione los <li> tienen que tener id, si no el array que devuelve es vacío.

(evidentemente la solución la tuve que buscar y apareció 3h despues de darle vueltas y vueltas ....)

Juan Antonio

19/10/2017
Alternativa a la alerta?
Gracias Miguel Angel, ha sido fantástica la claridad de tu artículo.

En vez de saltar alerta, he intentado recoger la variable con el nuevo orden en un formulario a través de un imput oculto. Aquí linea de código, mas o menos:

<FORM name="enviar" action="enviar.php" method="post">
<input type="hidden" name="ORDEN">
<input name="sent" type="submit" value="Envio">
</FORM>
<script>
document.Sombra.ordenF.ORDEN = ordenElementos;
</script>

Pero no me recoge el valor que se cargaba en ordenElementos

Muchas gracias

Juan Antonio

19/10/2017
Enlace con comentario anterior
Perdón el script estaba mal, este es el correo, aun así no recoge el orden ...
<script>
document.Sombra.ORDEN.value = ordenElementos;
</script>