jueves, 29 de marzo de 2012

Drag and Drop en HTML 5


Introducción

Una característica bastante utilizada en aplicaciones con una interfaz de usuario enriquezida es la de poder arrastrar los elementos. Ejemplos típicos son el de reorganizar los elementos de una web para personalizarla o mover un elemento a una determinada categoría.
Hasta ahora la forma más fácil de hacer esto era usar una librería de Javascript que nos facilitase el trabajo. La jQuery UI, que es la que más me conozco, estaba muy avanzada en este tema. Sin embargo, al ser una implementación totalmente basada en Javascript, tenía bastantes inconvenientes como:
  • Si mueves demasiado rápido los elementos, el elemento arrastrado se suele quedar atrás y se pierde en “dragging”.
  • Solo puedes mover elementos dentro de la misma web, no puedes arrastrar elementos de una ventana a otra ni aceptar elementos que no sean DOM.
  • Menos eficiencia al requerir más código.

Novedades en HTML 5

Los creadores del estándar HTML5, parece que han visto que esta característica es importante y que necesita ser mucho más exprimida para dar mucho más juego a las aplicaciones webs. A continuación os cito las principales novedades del estándard en este tema:
  • Nuevos eventos en el DOM: dragstart, drag, dragenter, dragover, dragleave, drop, dragend.
  • Nuevo atributo para los elementos DOM para hacerlo arrastrable: draggable=”true”.
  • Se permite adicionar información en el elemento arrastrable para que el contenedor pueda recibirla.
  • Posibilidad de establecer la imagen “ghost” mostrada mientras se arrastra.
  • Posibilidad de añadir efectos del estilo: copiar, mover, vínculo,  etc…
Además, esta característica va más allá de la propia web, dándonos la posiblidad de arrastrar elementos entre distintas webs e incluso aceptar elementos de otras aplicaciones como arrastrar un texto de un procesador de texto o un fichero del sistema.

Los nuevos eventos

Vamos a listar los nuevos eventos que nos trae HTML5 para drag and drop y luego veremos un ejemplo de implementación para que veáis lo fácil y rápido que se implementa.

dragstart

Comienza el arrastrado. El “target” del evento será el elemento que está siendo arrastrado.

drag

El elemento ha sido movido. El “target” del evento será el elemento arrastrado.

dragenter

Se dispara cuando un elemento que está siendo arrastrado entra en un contenedor. El “target” del evento será el elemento contenedor.

dragover

El elemento ha sido movido dentro del contenedor. El “target” del evento será el elemento contenedor. Como el comportamiento por defecto es denegar el “drop”, la función debe retornar el valor “false” o llamar a “preventDefault” para que indicar que se puede el soltar elemento.

dragleave

El elemento arrastrado ha salido del contenedor. El “target” del evento será el elemento contenedor.

drop

El elemento arrastrado has sido éxitosamente soltado en el elemento contenedor. El “target” del evento será el elemento contenedor.

dragend

Se ha dejado de arrastrar el elemento, con éxito o no. El “target” del evento será el elemento arrastrado.
Vamos a ver un ejemplo de como hacer un elemento arrastable usando jQuery.
  1. $('#mydrag')  
  2.     .attr('draggable''true')  
  3.     .bind('dragstart'function(ev) {  
  4.         var dt = ev.originalEvent.dataTransfer;  
  5.         dt.setData("Text""Información adicional");  
  6.         return true;  
  7.     })  
  8.     .bind('dragend'function(ev) {  
  9.         return false;  
  10.     });  
Y ahora la otra parte, creamos un contenedor que esté a la espera de elementos arrastrables.
  1. $('#mycontainer')  
  2.     .bind('dragenter'function(ev) {  
  3.         //Cambiamos color del contenedor para mostrar que acepta al elemento  
  4.         $(ev.target).css('background','#F00');  
  5.         return false;  
  6.     })  
  7.     .bind('dragleave'function(ev) {  
  8.        //Reestablecemos el color del contenedor  
  9.         $(ev.target).css('background','#FFF');  
  10.         return false;  
  11.     })  
  12.     .bind('dragover'function(ev) {  
  13.         //Importante como hemos dicho para que acepte al elemento,  
  14.         //por defecto cancela el drop  
  15.         return false;  
  16.     })  
  17.     .bind('drop'function(ev) {  
  18.         var dt = ev.originalEvent.dataTransfer;  
  19.         alert(dt.getData('Text'));  
  20.         return false;  
  21.     });  
  22. });  
Creo que el código no require de mucha explicación pues simplemente establece las funciones para cada evento, aunque creo que es importante reseñar que gracias al objetodataTransfer se puede compartir información entre el elemento arrastrado y el contenedor, lo veremos a fondo a continuación.

Transfiriendo información con dataTransfer

Como ya hemos dicho, una de las características más importantes de esta nueva implementación, es la posibilidad de cargar información en el elemento arrastrado para que el contenedor pueda recibirla. Pero, lo que todavía está mucho mejor, es que un contenedor pueda recibir los datos desde elementos arrastrados desde otros navegadores o aplicaciones.
Como ya he mostrado en el ejemplo anterior, esto se hace a través de los métodos setData ygetData del objeto dateTransfer expuesto en el objecto del evento (Event Object).
La información se debe de almacenar usando los tipos recomendados para un uso estándard y adecuado. A continuación unos ejemplos:
  1. var dt = ev.originalEvent.dataTransfer;  
  2. dt.setData('text/plain''Hola mundo');  
  3. dt.setData('text/html''<span>Hola mundo</span>');  
  4. dt.setData('text/uri-list''http://web.ontuts.com');  
Un ejemplo de uso de los tipos recomendados es seleccionar un texto en el navegador, arrastralo a un contenedor y esperar encontrar algo en en text/html, o arrastrar texto desde un procesador de texto y esperar encontrar algo en text/plain.

Uso de imágenes fantasma

Esta es una nueva característica que nos permite establecer lo que el usuario verá como imagen semitransparente mientras arrastra el elemento. Tenemos tres posibilidades:
  • Establecer un elemento del DOM.
  • Establecer una imagen (aunque se base en el punto anterior).
  • Establecer un elemento canvas.
Esto se hace a través del método setDragImage, incluído una vez más, en la propiedaddataTransfer. A continuación un ejemplo de los tres tipos:
  1. var dt = ev.originalEvent.dataTransfer;  
  2.  //Elemento DOM  
  3. dt.setDragImage( $('h2')[0], 0, 0);  
  4.  //Imagen (elemento DOM)  
  5. dt.setDragImage( $('#image')[0], 32, 32);   
  6.   
  7. var canvas = document.createElement("canvas");  
  8. canvas.width = canvas.height = 50;  
  9. var ctx = canvas.getContext("2d");  
  10. ctx.lineWidth = 8;  
  11. ctx.moveTo(25,0);  
  12. ctx.lineTo(50, 50);  
  13. ctx.lineTo(0, 50);  
  14. ctx.lineTo(25, 0);  
  15. ctx.stroke();  
  16. //Canvas  
  17. dt.setDragImage(canvas, 25, 25);  
El método setDragImage acepta como primer parámetro el elemento que se desea mostrar como imagen. Los dos siguientes parámetros son el “offset” que se desea dar a la imagen respecto a la posición por defecto.

Usando efectos de arrastrado / tirado

El drag and drop del nuevo estándar ha sido creado para sobre todo para soportar acciones como copiar, mover y vincular. Por ello se han añadido los efectos de tirado para mostrar la acción que se va a ejecutar. Para que te hagas una idea, son los iconcitos pequeños que aparecen cuando arrastras un icono en el S.O. (una flechita, un signo más, etc…).
Estes efectos pueden establecerse a nivel del elemento arrastrado o a nivel del contenedor. Si se vinculan al contenedor, el efecto solamente aparece cuando se producen los eventosdragenter y dragover.
Para usar esta funcionalidad se establecen las propiedades effectAllowed y dropEffectpara los elementos arrastrados y contenedores respectivamente. Como es de esperar, estas propiedades están incluídas en el objeto dataTransfer. A continuación un ejemplo de como añadir el efecto al elemento arrastrado:
  1. var dt = ev.originalEvent.dataTransfer;  
  2. dt.effectAllowed = 'copy';  
Los valores permitidos para esta propiedad son:
  • none: ninguna operación permitida
  • copy: solo copiar
  • move: solo mover
  • link: solo vínculo
  • copyMove: copiar o mover
  • copyLink: copiar o vincular
  • linkMove: vincular o mover
  • all: copiar, mover o vincular
Por otro lado, puedes especificar el efecto para el propio contenedor, para indicar la acción que se va a llevar a cabo al soltar el elemento:
  1. var dt = ev.originalEvent.dataTransfer;  
  2. dt.dropEffect = 'none';  

Conclusión

Hasta aquí el tutorial sobre el Drag and Drop en HTML 5, si has tenido la paciencia de leerlo todo, te habrás dado cuenta que tiene muchas novedades y mejoras que despilegan un nuevo abanico de posiblidades, además de mejorar el rendimiento.
Espero que os haya resultado útil la información y no dudéis en dejar un comentario para preguntar, criticar o valorar.
¡Nos vemos en el próximo!

No hay comentarios:

Publicar un comentario