Drag & Drop (arrastrar y soltar) simple en un DIV con JavaScript sin librerí­as

Hoy voy a dejarles un código bastante sencillo y didáctico para hacer que un DIV pueda ser arrastrado por toda el área de nuestra página y colocado donde el usuario desee. Como es costumbre el código no usará ninguna librería pesada; y no es que esté en contra del uso de librerías o frameworks, sino es que me parece sumamente importante ENTENDER que pasa por debajo de esas bonitas llamadas a clases/funciones y ser capaces de modificar el código para nuestro beneficio.

Lo que voy a explicar es el tipo de Drag & Drop más sencillo que hay en el cual solo se trata de "arrastrar y soltar" y no en el que las capas se reordenan automáticamente (este lo dejamos para el siguiente POST). En otras palabras lo que vamos a conseguir es algo como esto: http://www.formatoweb.com.ar/blog/files/dragdrop.php y el auto-ordenable que dejamos para más adelante sería este: http://www.formatoweb.com.ar/blog/files/dragdrop2.php (observen que pasa al arrastrar una capa por encima de la otra).


A continuación les dejo el código y pasamos a la explicación de cada función por separado:



JavaScript:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">


  2. <html>

  3. <head>

  4. <script type="text/javascript">

  5. function carga()


  6. {

  7.     posicion=0;

  8.    


  9.     // IE

  10.     if(navigator.userAgent.indexOf("MSIE")>=0) navegador=0;


  11.     // Otros

  12.     else navegador=1;

  13. }


  14. function evitaEventos(event)


  15. {

  16.     // Funcion que evita que se ejecuten eventos adicionales

  17.     if(navegador==0)


  18.     {

  19.         window.event.cancelBubble=true;


  20.         window.event.returnValue=false;

  21.     }


  22.     if(navegador==1) event.preventDefault();

  23. }


  24. function comienzoMovimiento(event, id)


  25. {

  26.     elMovimiento=document.getElementById(id);

  27.    


  28.      // Obtengo la posicion del cursor

  29.     if(navegador==0)

  30.      {

  31.         cursorComienzoX=window.event.clientX+document.documentElement.scrollLeft+document.body.scrollLeft;


  32.         cursorComienzoY=window.event.clientY+document.documentElement.scrollTop+document.body.scrollTop;



  33.         document.attachEvent("onmousemove", enMovimiento);


  34.         document.attachEvent("onmouseup", finMovimiento);

  35.     }


  36.     if(navegador==1)

  37.     {   


  38.         cursorComienzoX=event.clientX+window.scrollX;

  39.         cursorComienzoY=event.clientY+window.scrollY;


  40.        

  41.         document.addEventListener("mousemove", enMovimiento, true);


  42.         document.addEventListener("mouseup", finMovimiento, true);


  43.     }

  44.    

  45.     elComienzoX=parseInt(elMovimiento.style.left);


  46.     elComienzoY=parseInt(elMovimiento.style.top);

  47.     // Actualizo el posicion del elemento


  48.     elMovimiento.style.zIndex=++posicion;

  49.    


  50.     evitaEventos(event);

  51. }



  52. function enMovimiento(event)

  53. { 

  54.     var xActual, yActual;


  55.     if(navegador==0)

  56.     {   


  57.         xActual=window.event.clientX+document.documentElement.scrollLeft+document.body.scrollLeft;


  58.         yActual=window.event.clientY+document.documentElement.scrollTop+document.body.scrollTop;


  59.     } 

  60.     if(navegador==1)


  61.     {

  62.         xActual=event.clientX+window.scrollX;


  63.         yActual=event.clientY+window.scrollY;

  64.     }


  65.    

  66.     elMovimiento.style.left=(elComienzoX+xActual-cursorComienzoX)+"px";


  67.     elMovimiento.style.top=(elComienzoY+yActual-cursorComienzoY)+"px";


  68.     evitaEventos(event);

  69. }


  70. function finMovimiento(event)

  71. {


  72.     if(navegador==0)

  73.     {   

  74.         document.detachEvent("onmousemove", enMovimiento);

  75.         document.detachEvent("onmouseup", finMovimiento);


  76.     }

  77.     if(navegador==1)

  78.     {

  79.         document.removeEventListener("mousemove", enMovimiento, true);


  80.         document.removeEventListener("mouseup", finMovimiento, true);


  81.     }

  82. }


  83. window.onload=carga;


  84. </script>

  85. </head>

  86. <body>



  87. <div id="div1" style="top:100px; left:100px; position:absolute; background-color:#FF0000; color:#000000;"

  88. onmousedown="comienzoMovimiento(event, this.id);" onmouseover="this.style.cursor='move'">

  89. <span style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;">


  90. Prueba.................<br>

  91. Prueba.................<br>

  92. Prueba.................<br>


  93. Prueba.................<br>

  94. Prueba.................<br>

  95. </span>

  96. </div>


  97. <div id="div2" style="top:200px; left:300px; position:absolute; background-color:#FFFF00; color:#000000;"

  98. onmousedown="comienzoMovimiento(event, this.id);" onmouseover="this.style.cursor='move'">


  99. <span style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;">

  100. Prueba.................<br>

  101. Prueba.................<br>


  102. Prueba.................<br>

  103. Prueba.................<br>

  104. Prueba.................<br>

  105. </span>


  106. </div>


  107. </body>

  108. </html>



EDITADO: les dejo una versión del código que utiliza JavaScript no intrusivo para asignar los scrolls y que además utiliza el modelo de registro de eventos tradicional para simplificar bastante el código, aunque quizá no sea tan didáctico: descargar ejemplo.
Como ven, el script se compone únicamente de 5 funciones las cuales paso a explicar a continuación:
  • carga(): se ejecuta al cargar la página (onload) y se encarga de detectar el navegador del usuario para el posterior correcto manejo del objeto event. La variable navegador quedará en 0 si el usuario tiene IE o en 1 para cualquier otro. También aquí se setea la variable posicion en 0; esta variable se incrementa cada vez que se hace click en uno de los divs y se utiliza para el seguimiento del atributo z-index de los mismos. Esta variable es la responsable de que el div que se esté arrastrando en ese momento sea siempre el que se muestre por encima de los demás.
  • evitaEventos(): simplemente evita que otros eventos adicionales sean ejecutados en la propagación (más info en inglés aquí http://www.quirksmode.org/js/events_order.html)
  • comienzaMovimiento(): esta función es llamada al momento que el usuario clickea una de las capas que pueden ser arrastradas. Esta función se encargará de obtener en las variables cursorComienzoX y cursorComienzoY (hay una forma distinta de obtenerlas según el navegador) que representan la posición del cursor al momento del click; también obtendrá en las variables elComienzoX y elComienzoY que representan la posición del elemento con respecto al borde superior e izquierdo de la página, osea el top y left de CSS. Por último "le dirá" al objeto document que cada vez que el usuario mueva el mouse un pixel, la función enMovimiento debe ser llamada (para poder recalcular la posición de la capa) y que cuando el usuario suelte el mouse la función finMovimiento deberá ejecutarse. Esto se hace añadiendo manejadores de eventos al objeto document para que cuando una acción X sea realizada por el usuario (en este caso mover el mouse con la tecla presionada) se llame a una función Y (en este caso enMovimiento). Más información sobre eventos aquí: http://www.quirksmode.org/js/events_tradmod.html. Esta función también colocará la capa por encima de todas al modificarle su z-index valiendose de la variable posicion declarada en carga().
  • enMovimiento(): función llamada cuando el usuario mueve el mouse manteniendo el botón presionado sobre una capa perteneciente al Drag. La funcionalidad de este trozo de código es obtener en xActual e yActual la posición actual del puntero del mouse y, como ya teniamos guardada la posición del mouse al comenzar el movimiento y la posición de la capa con respecto a los márgenes, simplemente obtenemos el trayecto en pixeles que ha recorrido el mouse restando la posición actual con la posición inicial (xActual-cursorComienzoX) y a esto le sumamos la posición de la capa respecto de los márgenes (elComienzoX). Este resultado, que suena enredado pero es sencillo de comprender leyendo con detenimeinto, es donde debemos colocar finalmente la capa, modificandole sus atributos left y top con esta sentencia: elMovimiento.style.top=(elComienzoY+yActual-cursorComienzoY)+"px"; Recordemos que esta función se llama cada vez que el usuario mueve el mouse habiendo hecho click sobre la capa; y se seguirá ejecutando hasta que el botón izquierdo sea soltado.
  • finMovimiento(): función ejecutada cuando el usuario suelta el botón izuqierdo después de haber realizado el movimiento de la capa. Su funcionalidad será únicamente "avisarle" al objeto document que ya no debe llamar a la función enMovimiento cuando el usuario mueva el mouse; esto, al igual que en las funciones anteriores se realiza diferencialmente dependiendo que navegador posea el usuario... temita de incompatibilidades, los colegas del rubro sabrán entender...
Bueno, eso es todo. Como habrán notado en el código utilizo lo que los que saben llaman "modelo de registro de eventos avanzado" (el addEventListener y sus secuaces) aunque se podría haber utilizado el modelo tradicional (elemento.onmousemove=funcion; por ejemplo) y quizá simplificar un poco las cosas... pero bueno, cuestión de gustos.
Espero resulte útil. Para más adelante queda pendiente la explicación para Drag & Drop auto-ordenable de los 2 tipos que existen. Cualquier consulta comenten nomás!

Comentarios