jQuery - Multiple draggable elements

Tuesday, 13 January 2009


This tutorial outlines how to drag multiple elements with jQuery's draggable.

Update: This post has been updated, please go here to view the updated article.

Currently it is possible to drag individual elements, but I wanted to try and get jQuery to drag multiple elements after selection. I also wanted the handle to be any of the selected elements for convenience.

I did some searching and couldn't find another implementation of this so if you have any ideas on how this could be improved just let me know. To select multiple elements for dragging just ctrl-click and they will highlight green. To un-highlight just ctrl-click again. Elements that have not been highlighted can still be dragged individually.

Other improvements that could be implemented:
* I had a file manager in mind when I implemented the ctrl-click, so it would be nice to have click anywhere besides a highlighted element removes highlighting from all elements

* Single click on any item removes highlighting from other elements and places highlighting on that element

* Shift-click will highlight multiple items according to their index as per file manager


You can view a working demo here.

This relies on jQuery draggable to work.

Our html file for this implementation has a bunch of square divs that can be dragged around together, as such:

<div class='blue draggable'></div>
     <div class='blue draggable'></div>
     <div class='blue draggable'></div>


Here is the css that I've used:

.blue {
     height:40px;
     width:40px;
     border:1px solid blue;
     background-color:aliceBlue;
     display:block;
}

.grouped {
     border:1px solid greenYellow;
     background-color:honeyDew;
}

Our blue class just defines our blue square to be dragged, this can be changed to your taste.

The grouped will be used later as our highlight to indicate which elements are currently grouped together.

The draggable class is used to indicate which elements are draggable using jQuerys draggable. I didn't want the styling to be on all elements that have class draggable in case we want to use that for other elements later on, so I created the blue class to keep the styling separate from the action.

Now let's look at the javascript. As per usual with jQuery we start off with our document ready function.

$(document).ready(function() {
     $(".blue").click(function(event) {
          if (event.ctrlKey) {
               $(this).toggleClass('grouped');
          }
     });

And then comes our click function for our 'blue' elements. It essentially checks to see if our element has been ctrl-clicked and then toggles the grouped css class so we can see which elements will be dragged together.

Next comes our dragging function. We want to specify details inside our call to draggable as such:

$(".draggable").draggable({
     start: function(event, ui) {
          posTopArray = [];
          posLeftArray = [];
          if ($(this).hasClass("grouped")) {  // Loop through each element and store beginning start and left positions
               $(".grouped").each(function(i) {
                    thiscsstop = $(this).css('top');
                    if (thiscsstop == 'auto') thiscsstop = 0; // For IE

                    thiscssleft = $(this).css('left');
                    if (thiscssleft == 'auto') thiscssleft = 0; // For IE

                    posTopArray[i] = parseInt(thiscsstop);
                    posLeftArray[i] = parseInt(thiscssleft);
               });
          }

          begintop = $(this).offset().top; // Dragged element top position
          beginleft = $(this).offset().left; // Dragged element left position
     },
     drag: function(event, ui) {
          var topdiff = $(this).offset().top - begintop;  // Current distance dragged element has traveled vertically
          var leftdiff = $(this).offset().left - beginleft; // Current distance dragged element has traveled horizontally

          if ($(this).hasClass("grouped")) {
               $(".grouped").each(function(i) {
                    $(this).css('top', posTopArray[i] + topdiff); // Move element veritically - current css top + distance dragged element has travelled vertically
                    $(this).css('left', posLeftArray[i] + leftdiff); // Move element horizontally - current css left + distance dragged element has travelled horizontally
               });
          }
     }
});

Now lets run through what is there...

We start off by calling draggable on all the elements that have the ".draggable" css class.

Then we have two special variables inside our draggable call start: and drag:.

Start is called at the time we start dragging our element and drag is called as the element is being dragged.

Inside the start function we loop through all the elements that have the class grouped. We then store the starting top and left (vertical and horizontal) positions of each element in the posTopArray and posLeftArray variables. We also store the starting top and left position of our parent element being dragged in begintop and beginleft vars.

As the parent element is dragged we calculate the distance it has moved between begintop and beginleft and where it currently is and we store this difference in the topdiff and leftdiff vars.

We then loop through each of the grouped elements and move them the same amount of distance, which is the starting position of the element stored in postTopArray and posLeftArray and add on the values of topdiff and leftdiff.

This has been tested in Firefox and IE 6/7.
blog comments powered by Disqus