Drag and Drop with AJAX Example

Published on Feb 14, 2009 by Jamie Munro

At my work it's quite clear to me that a lot of people have difficulty with both AJAX and drag and drop functionality.  In this article, I thought I would provide a realistic and simplistic example of how to accomplish both AJAX and drag and drop together.

By the end of this article you will be able to create an extremely slick content management system that works really smoothly.



The first thing we need to do is create, what I like to call, a "List page".  This page will list our data, in the example below it will list articles and allow you to edit or delete them.

The following code will create our basic table, I'm using static data for this example, in a real CMS system you would have to query a database and loop through and display the results dynamically. Sorry for the formatting, I'm still trying to work out the kinks.
<div id="content">
  <table class="listing" width="550" border="0" cellspacing="0" cellpadding="0">
  <tr>
   <th>Title</th>
   <th>Category</th>
   <th>Created</th>
   <th>Modified</th>
   <th class="actions" colspan="2">Actions</th>
  </tr>
  <tr class="dropTarget">
   <td>
    <input type="Hidden" name="articleid" value="1">
    <input type="Hidden" name="row" value="1">
    Article 1
   </td>
   <td>Category 1</td>
   <td>February 12, 2009</td>
   <td>February 14, 2009</td>
   <td class="actions" width="50"><a href="#">Edit</a></td>
   <td class="actions" width="50"><a href="#" onclick="return confirm('Are you sure you wish to delete this article?')">Delete</a></td>
  </tr>
  <tr class="dropTarget alt">
   <td>
    <input type="Hidden" name="articleid" value="2">
    <input type="Hidden" name="row" value="2">
    Article 2
   </td>
   <td>Category 2</td>
   <td>February 12, 2009</td>
   <td>February 14, 2009</td>
   <td class="actions" width="50"><a href="#">Edit</a></td>
   <td class="actions" width="50"><a href="#" onclick="return confirm('Are you sure you wish to delete this article?')">Delete</a></td>
  </tr>
  <tr class="dropTarget">
   <td>
    <input type="Hidden" name="articleid" value="3">
    <input type="Hidden" name="row" value="3">
    Article 3
   </td>
   <td>Category 3</td>
   <td>February 12, 2009</td>
   <td>February 14, 2009</td>
   <td class="actions" width="50"><a href="#">Edit</a></td>
   <td class="actions" width="50"><a href="#" onclick="return confirm('Are you sure you wish to delete this article?')">Delete</a></td>
  </tr>
  <tr class="dropTarget alt">
   <td>
    <input type="Hidden" name="articleid" value="4">
    <input type="Hidden" name="row" value="4">
    Article 4
   </td>
   <td>Category 4</td>
   <td>February 12, 2009</td>
   <td>February 14, 2009</td>
   <td class="actions" width="50"><a href="#">Edit</a></td>
   <td class="actions" width="50"><a href="#" onclick="return confirm('Are you sure you wish to delete this article?')">Delete</a></td>
  </tr>
  </table>
 </div>

 The above code, with a bit of styling, will result in the following display:

table

The two things to understand about our basic table is the <table> tag has a class called "listing" assigned to it and each <tr> (excluding the header) has a class "dropTarget" assigned to it.  We will use this in our Javascript code to set up our draggable and droppable elements.

The next step in the process is to begin creating our Javascript code.  I will list several snippets below.  I have provided a link at the end of this article to download the full source code.
// set up draggable elements
  $(".listing > tbody > tr.dropTarget").draggable({
   appendTo:"body",
   cursor:"pointer",
   cursorAt:{top:20,left:100},
   helper:function(){
    //this is the drag box that you see when you drag
    return $('<div class="dragBox"><p>Drag to change the<br/> ordering of this article</p></div>');
   }
  });

To accomplish our drag and drop we are using the JQuery libraries, if you are unfamiliar with JQuery you may wish to take a few minutes to read about it.  The above code is telling JQuery to turn all <tr>'s with the class "dropTarget" into draggable elements.  This simply means it allows us to now drag our table rows around the page.  The other options specify how we want some things to look.
return $('<div class="dragBox"><p>Drag to change the<br/> ordering of this article</p></div>');

The above line will create a little box with the message inside it when we start dragging our table row.

Now that we have our draggable elements set up, the next step is to create droppable elements.  A droppable elements is an element that waits for a draggable element to be "dropped" in it. 
//set up the droppable list elements
  $(".listing > tbody > tr.dropTarget").droppable({
   accept: ".listing > tr.dropTarget",
   hoverClass: 'droppable-hover',
   tolerance: 'pointer',
   drop: function(ev, ui) {
    alert('dropped');
   }
  });

The above code initializes the same table rows that are draggable elements to also be droppable elements.  We have the ability to set a few settings as well.  In this example, we tell JQuery to only allow a specific element, set a CSS class when an element is hovered over, and finally we create a function to process the drop.  In this simple example it simply does a Javascript alert.

At this stage our example is fully functional and we are able to drag and drop our table rows.  The steps are what really makes it slick.  We are going to update our drop function to do two new things.  The first thing it will do is dynamically move the table row that we dragged to it's new location and shift all of the other rows up or down.  After it has moved the row, we will call our PHP page with the article id and new position.  This PHP page will then perform the necessary SQL queries to adjust the order in the database.
var dropEl = this;
    var dragEl = $(ui.draggable);
  
    // get article id
    var articleId = dragEl.find("input:hidden").get(0).value;
    
    // get order
    // if they are different, we need to find out if it is above or below
    var dropOrder = 0;
    // lastObj will contain the element of where we need to insertBefore...
    var lastObj;
    var rowCount = 0;
    var isBefore;
    dropOrder = $(dropEl).find("input:hidden").get(1).value;
    
    // set the row count that we will be dropping it in
    rowCount = $(dropEl).find("input:hidden").get(1).value;

The following code does a couple basic things.  It initializes several variables, gets the article id of the element dragged, gets the position we dropped it in, etc...
// set lastObj
    lastObj = $(dropEl);
    
    if (dragEl.find("input:hidden").get(1).value > rowCount)
     isBefore = true;
    else
     isBefore = false;
  
    // insert before lastObj
    if (isBefore)
     dragEl.insertBefore(lastObj);
    else
     dragEl.insertAfter(lastObj);

The next set of code set's lastObj to the object we dropped in.  This will be used for us to either insert our dragged row before or after it.  We then determine whether we should insert before or after.  Now that we determine where it should go, we run the appropriate command.  At this point we have now successfully moved the row we dragged.

The second last thing to accomplish is we must reset the row count for all of our items so next time when we drag we know the appropriate position we dropped our table row at.  The following code accomplishes this:
// loop through all draggables and update their counts
    $(".listing > tbody > tr.dropTarget").each(
     function(intIndex) {
      if (intIndex % 2 == 0) {
       $(this).removeClass("alt");
      } else {
       $(this).addClass("alt");
     }
     if (dragEl.find("input:hidden").get(0).value != $(this).find("input:hidden").get(0).value) {
      // adjust the rowcount
      if (isBefore) {
       // move everything up by 1
       if ($(this).find("input:hidden").get(1).value >= dropOrder)
        $(this).find("input:hidden").get(1).value++;
      } else {
       // move everything down by 1
       if ($(this).find("input:hidden").get(1).value <= dropOrder)
        $(this).find("input:hidden").get(1).value--;
      }
     } else {
      $(this).find("input:hidden").get(1).value = dropOrder;
     }
    });

As you can see we also are resetting the "alt" class on every other row.  If we don't do this our alternate rows will no longer be correct.

Finally, the only thing left to do is call our PHP page with AJAX.  The PHP page will take two parameters, the article id and the new position.
// set the url of our php page that will accept an articleid and position to update it
    var url = "pageThatDoesArticleUpdate.php?articleid=" + articleId + "&position=" + dropOrder;
    $.get(url);

I'll leave you to code the PHP page, but I'll give you the basics.  You should execute three SQL queries.  The first query will get the articles original position.  The second query will set the new order for the specified article id.  The third query should update all of the other articles up or down by 1.

I hope you enjoyed this article, to download the full source code (minus the PHP page), click here.

Tags: Javascript | AJAX | drag and drop | JavaScript | jQuery

Related Posts

blog comments powered by Disqus