I’ve been using JQuery for a while now and really agree with its tag line that it’s the “The Write Less, Do More, JavaScript Library”. We’ve also got this code for dragging and dropping table rows that has proved very popular, so it seemed natural to combine the two and wrap up the table drag and drop as a JQuery plugin.

Why have another plugin?

Dragging and dropping rows within a table can’t be handled by general purpose drag and drop utilities for a number of reasons, not least because you need to move the whole row, not just the cell that receives the mouse events. Re-parenting the row also requires specific code. Sadly also, effects like fadeIn and fadeOut don’t work well with table rows on all browsers, so we have to go for simpler effects.

What does it do?

This TableDnD plugin allows the user to reorder rows within a table, for example if they represent an ordered list (tasks by priority for example). Individual rows can be marked as non-draggable and/or non-droppable (so other rows can’t be dropped onto them). Rows can have as many cells as necessary and the cells can contain form elements.

How do I use it?

  1. Download Download jQuery (version 1.2 or above), then the TableDnD plugin (current version 0.7).
  2. Reference both scripts in your HTML page in the normal way.
  3. In true jQuery style, the typical way to initialise the tabes is in the $(document).ready function. Use a selector to select your table and then call tableDnD(). You can optionally specify a set of properties (described below).
1 One some text
2 Two some text
3 Three some text
4 Four some text
5 Five some text
6 Six some text

The HTML for the table is very straight forward (no Javascript, pure HTML):

<table id="table-1" cellspacing="0" cellpadding="2">
    <tr id="1"><td>1</td><td>One</td><td>some text</td></tr>
    <tr id="2"><td>2</td><td>Two</td><td>some text</td></tr>
    <tr id="3"><td>3</td><td>Three</td><td>some text</td></tr>
    <tr id="4"><td>4</td><td>Four</td><td>some text</td></tr>
    <tr id="5"><td>5</td><td>Five</td><td>some text</td></tr>
    <tr id="6"><td>6</td><td>Six</td><td>some text</td></tr>
</table>

To add in the “draggability” all we need to do is add a line to the $(document).ready(...) function
as follows:

<script type="text/javascript">
$(document).ready(function() {
    // Initialise the table
    $("#table-1").tableDnD();
});
</script>

In the example above we’re not setting any parameters at all so we get the default settings. There are a number of parameters you can set in order to control the look and feel of the table and also to add custom behaviour on drag or on drop. The parameters are specified as a map in the usual way and are described below:

onDragStyle
This is the style that is assigned to the row during drag. There are limitations to the styles that can be associated with a row (such as you can’t assign a border—well you can, but it won’t be displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as a map (as used in the jQuery css(...) function).
onDropStyle
This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations to what you can do. Also this replaces the original style, so again consider using onDragClass which is simply added and then removed on drop.
onDragClass
This class is added for the duration of the drag and then removed when the row is dropped. It is more flexible than using onDragStyle since it can be inherited by the row cells and other content. The default is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your stylesheet.
onDrop
Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table and the row that was dropped. You can work out the new order of the rows by using
table.tBodies[0].rows.
onDragStart
Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the table and the row which the user has started to drag.
scrollAmount
This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, FF3 beta)

This second table has has an onDrop function applied as well as an onDragClass. The javascript to set this up is as follows:

$(document).ready(function() {

	// Initialise the first table (as before)
	$("#table-1").tableDnD();

	// Make a nice striped effect on the table
	$("#table-2 tr:even').addClass('alt')");

	// Initialise the second table specifying a dragClass and an onDrop function that will display an alert
	$("#table-2").tableDnD({
	    onDragClass: "myDragClass",
	    onDrop: function(table, row) {
            var rows = table.tBodies[0].rows;
            var debugStr = "Row dropped was "+row.id+". New order: ";
            for (var i=0; i<rows.length; i++) {
                debugStr += rows[i].id+" ";
            }
	        $(#debugArea).html(debugStr);
	    },
		onDragStart: function(table, row) {
			$(#debugArea).html("Started dragging row "+row.id);
		}
	});
});
1 One
2 Two
3 Three
4 Four
5 Five
6 Six
7 Seven
8 Eight
9 Nine
10 Ten
11 Eleven
12 Twelve
13 Thirteen
14 Fourteen

What to do afterwards?

Generally once the user has dropped a row, you need to inform the server of the new order. To do this, we’ve added a method called serialise(). It takes no parameters but knows the current table from the context. The method returns a string of the form tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3...
You can then use this as part of an Ajax load.

This third table demonstrates calling the serialise function inside onDrop (as shown below). It also demonstrates the “nodrop” class on row 3 and “nodrag” class on row 5, so you can’t pick up row 5 and
you can’t drop any row on row 3 (but you can drag it).

    $('#table-3').tableDnD({
        onDrop: function(table, row) {
            alert($.tableDnD.serialize());
        }
    });

Ajax result

Drag and drop in this table to test out serialise and using JQuery.load()

1 One
2 Two
3 Three (Can’t drop on this row)
4 Four
5 Five (Can’t drag this row)
6 Six

This table has multiple TBODYs. The functionality isn’t quite working properly. You can only drag the rows inside their own TBODY, you can’t drag them outside it. Now this might or might not be what you want, but unfortunately if you then drop a row outside its TBODY you get a Javascript error because inserting after a sibling doesn’t work. This will be fixed in the next version. The header rows all have the classes “nodrop” and “nodrag” so that they can’t be dragged or dropped on.

H1 H2 H3
4.1 One
4.2 Two
4.3 Three
4.4 Four
4.5 Five
4.6 Six
H1 H2 H3
5.1 One
5.2 Two
5.3 Three
5.4 Four
5.5 Five
5.6 Six
H1 H2 H3
6.1 One
6.2 Two
6.3 Three
6.4 Four
6.5 Five
6.6 Six

The following table demonstrates the use of the default regular expression. The rows have IDs of the form table5-row-1, table5-row-2, etc., but the regular expression is /[^-]*$/ (this is the same as used in the NestedSortable plugin for consistency). This removes everything before and including the last hyphen, so the serialised string just has 1, 2, 3 etc. You can replace the regular expression by setting the serializeRegexp option, you can also just set it to null to stop this behaviour.

    $('#table-5').tableDnD({
        onDrop: function(table, row) {
            alert($('#table-5').tableDnDSerialize());
        },
        dragHandle: ".dragHandle"
    });
1 One some text
2 Two some text
3 Three some text
4 Four some text
5 Five some text
6 Six some text

In fact you will notice that I have also set the dragHandle on this table. This has two effects: firstly only the cell with the drag handle class is draggable and secondly it doesn’t automatically add the cursor: move style to the row (or the drag handle cell), so you are responsible for setting up the style as you see fit.

Here I’ve actually added an extra effect which adds a background image to the first cell in the row whenever you enter it using the jQuery hover function as follows:

    $("#table-5 tr").hover(function() {
          $(this.cells[0]).addClass('showDragHandle');
    }, function() {
          $(this.cells[0]).removeClass('showDragHandle');
    });

This provides a better visualisation of what you can do to the row and where you need to go to drag it (I hope).

Tagged with:
 

928 Responses to Table Drag and Drop JQuery plugin

  1. T.J. says:

    Once a table row is dropped, how can I update an input field in every table row with the new corresponding sort order?

    Example:

  2. kaidot says:

    HI, thanks for the great plugin. Works just fine.
    However, I am at them moment struggling (and I am new to jquery – still learning by doing)when it comes to replacing the tbody with a new tbody. Draging/dropping does not work anymore.

    With other elements I changed the jquery to the on(). But I don’t know how to change this with TableDnD.

    Currently I use $(“#table”).tableDnd(){ …. (according to the samples) inside of the $(document).ready.

    I would hope that someone could help me out with this, please.

  3. David M says:

    Get the following errors at line 108:

    Chrome 33.0.1750.154 m
    Uncaught TypeError: Object # has no method ‘hasClass’

    FF 27.0.1
    TypeError: $(…).hasClass is not a function

    IE 11.0.9600.16428
    SCRIPT438: Object doesn’t support property or method ‘hasClass’

    I am redefining the default jquery variable with:
    var $j = jQuery.noConflict();

    So I replaced the $ with $j on lines 108 and 261. Plugin does not work with the listed browsers. Do not even get the “move” cursor when positioning over the rows.

  4. David M says:

    Should also include that I am using jquery 1.8.3.

  5. Wajih says:

    hi i have table in which i want move 3 rows combine either single row how would be possible.

  6. matthias says:

    just had a quick view at your code (btw – thanks for the plugin)
    is there a reason we you create jquery objects from the same source multiple times (e.g. in the makeDraggable function -> $(this), wouldn´t it be much cleaner better in terms of performance to create one $this = $(this) and use it instead ?

    kind regards,
    matthias

  7. Tony says:

    I’m using tablednd with jquery mobile.

    I’m having an issue that I can’t figure out.

    Here is some basic set up information…
    I have two pages that each use tablednd. page A and page B

    Due to the way jquery mobile uses ajax to change page content
    I load a call to restart tablednd within the section and it does fire any jquery.

    The issue is that if page A is the first page loaded.. in other words it is loaded without ajax.. it works fine until page B is loaded. Page B works fine but when returning to page A nothing fires. However I can go back to page B and everything works for page B.

    This works in reverse if loading page B first.

    So the page that loads first will not work after loading another page and returning. But any page loaded later (by ajax) seems to work even when leaving and returning.

  8. Patrik Iden says:

    Hello, i’m trying to make this work with this script, any ideas?
    Thank you.

    Web links – List ()

    << Back to Directory

    Add new link +

    Title:
    Drag & Drop Link:
     

    <?
    $gbfile='links.txt';
    $separator= '|';
    //====================================
    //This function will add one line to
    //the end of file
    //====================================
    function add($str){
    global $gbfile;
    $tmp = trim($str);
    $fp=fopen($gbfile,'a+');
    flock($fp, LOCK_EX);
    fwrite($fp, $tmp. "\n");
    flock($fp, LOCK_UN);
    fclose($fp);
    }

    //====================================
    //Function below gets specified number
    //of lines and returns an array
    //====================================
    function get($start, $end){
    global $gbfile;
    $records=array();
    $filename="links.txt";
    $fp=fopen($gbfile,'r');
    flock($fp, LOCK_SH);
    $i=1;
    $tmp=TRUE;
    while($i<$start && !feof($fp)) {
    $tmp=fgets($fp);
    $i++;
    }
    while($i<=$end && !feof($fp)) {
    $tmp=trim(fgets($fp));
    if ($tmp) { array_push($records, $tmp); }
    $i++;
    }

    flock($fp, LOCK_UN);
    fclose($fp);
    return($records);
    }
    //Listing part

    $start=$_GET['start'];
    $end=$_GET['end'];

    if (!$end || $start<=0) { $start=1; }
    if (!$end) { $end=$linkspage; }
    if ($end

    <a href="” target=”">

    $(“a[href^='http']“).each(function() {
    $(this).css({
    background: “url(http://g.etfv.co/” + this.href + “) left center no-repeat”,
    “-webkit-background-size”: “20px”,
    “-moz-background-size”: “20px”,
    “-o-background-size”: “20px”,
    “background-size”: “20px”,
    “padding-left”: “20px”,
    “margin-left”: “10px”
    });
    });

    $show) {
    $start-=$show;
    $end-=$show;
    $start–;
    $end–;
    echo “<< Previous“;
    if (count($records)!=0) {
    $start+=$show*2;
    $end+=$show*2;
    $start=$start+2;
    $end=$end+2;
    echo “Next >>“;
    $start–;
    $end–;
    }
    else {
    echo “No more records found!”;
    }
    }
    else {
    $start+=$show;
    $end+=$show;
    $start++;
    $end++;
    Next >>“;
    $start–;
    $end–;
    }
    ?>

  9. Patrik Iden says:

    Web links - List ()

    << Back to Directory

    Add new link +

    Title:
    Drag & Drop Link:
     

    <?
    $gbfile='links.txt';
    $separator= '|';
    //====================================
    //This function will add one line to
    //the end of file
    //====================================
    function add($str){
    global $gbfile;
    $tmp = trim($str);
    $fp=fopen($gbfile,'a+');
    flock($fp, LOCK_EX);
    fwrite($fp, $tmp. "\n");
    flock($fp, LOCK_UN);
    fclose($fp);
    }

    //====================================
    //Function below gets specified number
    //of lines and returns an array
    //====================================
    function get($start, $end){
    global $gbfile;
    $records=array();
    $filename="links.txt";
    $fp=fopen($gbfile,'r');
    flock($fp, LOCK_SH);
    $i=1;
    $tmp=TRUE;
    while($i<$start && !feof($fp)) {
    $tmp=fgets($fp);
    $i++;
    }
    while($i<=$end && !feof($fp)) {
    $tmp=trim(fgets($fp));
    if ($tmp) { array_push($records, $tmp); }
    $i++;
    }

    flock($fp, LOCK_UN);
    fclose($fp);
    return($records);
    }
    //Listing part

    $start=$_GET['start'];
    $end=$_GET['end'];

    if (!$end || $start<=0) { $start=1; }
    if (!$end) { $end=$linkspage; }
    if ($end

    <a href="" target="">

    $("a[href^='http']").each(function() {
    $(this).css({
    background: "url(http://g.etfv.co/" + this.href + ") left center no-repeat",
    "-webkit-background-size": "20px",
    "-moz-background-size": "20px",
    "-o-background-size": "20px",
    "background-size": "20px",
    "padding-left": "20px",
    "margin-left": "10px"
    });
    });

    $show) {
    $start-=$show;
    $end-=$show;
    $start--;
    $end--;
    echo "<< Previous";
    if (count($records)!=0) {
    $start+=$show*2;
    $end+=$show*2;
    $start=$start+2;
    $end=$end+2;
    echo "Next >>";
    $start--;
    $end--;
    }
    else {
    echo "No more records found!";
    }
    }
    else {
    $start+=$show;
    $end+=$show;
    $start++;
    $end++;
    "Next >>";
    $start--;
    $end--;
    }
    ?>

  10. Brent says:

    The biggest problem I am having is when I have a header and some fixed rows at top. If I drag a draggable row up to the header area (I know right, like who would do that), I get an ErrorNotFound. ANy help would be appreciated :-)

  11. Stan says:

    Hi

    I use the plugin with the myDragClass, but I can’t change the background color of the row ..

    background-color:#999; does not work..

    thanks

  12. prasanna says:

    I need to disable tableDND functionality on some conditions. Is there any properties to disable the feature. Plase help.

    thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>