Table Drag and Drop JQuery plugin
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?
- Download Download jQuery (version 1.2 or above), then the TableDnD plugin from GitHub (current version 0.6).
- Reference both scripts in your HTML page in the normal way.
- In true jQuery style, the typical way to initialise the tabes is in the
$(document).readyfunction. Use a selector to select your table and then calltableDnD(). 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 usingonDragClass.) The CSS style to apply is specified as
a map (as used in the jQuerycss(...)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 istDnD_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).
Version History
| 0.2 | 2008-02-20 | First public release |
| 0.3 | 2008-02-27 | Added onDragStart option Made the scroll amount configurable (default is 5 as before) |
| 0.4 | 2008-03-28 | Fixed the scrollAmount so that if you set this to zero then it switches off this functionality Fixed the auto-scrolling in IE6 thanks to Phil Changed the NoDrop attribute to the class “nodrop” (so any row with this class won’t allow dropping) Changed the NoDrag attribute to the class “nodrag” (so any row with this class can’t be dragged) Added support for multiple TBODYs–though it’s still not perfect Added onAllowDrop to allow the developer to customise this behaviour Added a serialize() method to return the order of the rows in a form suitable for POSTing back to the server |
| 0.5 | 2008-07-11 | Now supports having a column as a drag handle (specify a class for the dragHandle option when configuring).
Improved the serialize method to use a default (but also settable in the options) regular expression for generating the serialized string. The default is /[^\-]*$/ which will remove everything before a final hyphen, so item-s1 becomes s1. Added $(‘…’).tableDnDUpdate() to cause the table to update its rows so the drag and drop functionality works if, for example, you’ve added a row. Added $(‘…’).tableDnDSerialize() which allows you to serialize a table from any javascript code. Removed remaining $ and replaced with jQuery so that it should work with Prototype and Scriptaculous |
Tags: Drag & Drop, Javascript, jQuery, Web
Subscribe to news from Isocra



Hy I Have found bug in the table drag and drop plugin.
i fill the div’s Inner Html with xajax function after words in xajax i called addScript() function to add dynamic script.table is sortable and dragable in any ie,mozilla and opera browser.
but this cant work in the safari 3+. & i found the following error or alert message
Type Error: Value undefined(result of expression o.find) is not object.
Please help me asap.
First off, thanks so much for the plugin. It is really useful and easy to use.
I have a problem when I have a table inside an iframe. When I drag a row and take it to the top, the page will not scroll (even after explicitly setting scrollAmount to a positive value). Scrolling works on the same table if it is not inside an iframe. Has anyone faced this problem? Has anyone figured out a solution for this?
Thanks in advance.
[...] Table Drag and Drop JQuery plugin [...]
[...] http://www.isocra.com/2008/02/table-drag-and-drop-jquery-plugin/ [...]
Russian translate here: http://www.ilyaplot.ru/2010/03/dinamicheskaya-sortirovka-tablic-ajax-jquery-2/
I described how to connect the script on your blog. Thank you for the precious plugin!
Two things I’ve hacked since using in a live environment.
One, need to have the row take on the drag class upon click, not just once it begins drag.
jQuery(rows[i]).mousedown(function(ev) { ... // want the same drag color on mouse down $(this).addClass('myDragClass'); ... })Two, there is a bug with auto scroll that needs addressed. If you are at the top of the window (in config.scrollAmount buffer zone) and dragging down, don’t apply autoscroll up. If you are at the bottom of the window (in config.scrollAmount buffer zone) and dragging up, don’t apply autoscroll down. Must change the placement of the autoscroll code to take “movingDown” into account.
// TODO worry about what happens when there are multiple TBODIES if (movingDown && jQuery.tableDnD.dragObject != currentRow) { var windowHeight = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; // If we are dragging up from bottom of window, // don't apply any scrolling please. if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) window.scrollBy(0, config.scrollAmount); jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { // If we are dragging down from top of window, // don't apply any scrolling please. if (mousePos.y-yOffset < config.scrollAmount) window.scrollBy(0, -config.scrollAmount); jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); }[...] Whit this really cool plugin you can reorder and filter items with a nice shuffling animation. The demo is a must see! Table Drag and Drop JQuery plugin [...]
Hi all,
I have read this post and every single reply a dozen times. I’m trying to reorder an SQL table via PHP. I’ve read some responses that say “just do an ajax call” or “put the values in a hidden field”.
My problem is I’m extremely new with javascript. I don’t know how to do those things. I’m trying to do something like the 3rd example but it seems incomplete. I checked the “PHP Source” link but PHP is not my issue, it’s getting the data TO PHP that I don’t know how to execute. PLEASE, PLEASE, PLEASE help me!
Hi @TomM,
You’re right, there’s a vital line missing from the code display for example 3. Sorry, in all this time I hadn’t noticed. It’s only one line, and just uses the standard jQuery Ajax
loadcall. Here it is, highlighted:$('#table-3').tableDnD({ onDrop: function(table, row) { alert("Result of $.tableDnD.serialise() is "+$.tableDnD.serialize()); $('#AjaxResult').load("/articles/ajaxTest.php?"+$.tableDnD.serialize()); } });Basically, you specify a part of the current page that you want to replace with the results of the Ajax call (in this case the element whose id is “AjaxResult”), and then you call
load(url). To attach the information for PHP or whatever, you call$.tableDnD.serialise()and it produces the parameters and values in the format described above.Hope this helps?
Hey Denis,
THANK YOU! That was exactly what I needed to know and I very much appreciate such a speedy response. The plugin is so great, but yeah, my knowledge of JS/JQuery is still sort of in its infancy, so I was just missing what the call to push it to PHP was. Now that I have the data pushing to something I know how to use I’ve got it working just how I need it to. A thousand thank yous!
hello sir, can you please tell how i canapply inline edit and drag and drop together in jquery. My inline edit was working, now i have added drag and drop which has stopped edit to work. please guide through! thanks to all experts over here!!!!!!!!
Hi everyone.
I need to know how can I get the new order of the rows so I can insert the new rows in MySQL.
Thanks.
Thanks so much. I know I’ll be using this all the time now. I can’t beleive I never knew about this and it’s so simple!
Really weird thing going on here.. basically on dragstart i block off certain rows as nodrop.. then on drop i get all the rows without no drop class..
everything is fine, until i add the piece of code with the ajax. The variable it passes is the orginal order not the new order.. however without the ajax code the variable is correct??
onDrop: function(table, row) { var debugStr = ""; var rows = table.tBodies[0].rows; // console.log(row.id); for (var i=0; i<rows.length; i++) { if($(rows[i]).hasClass("nodrop") == 0) {//console.log(rows[i].id); debugStr += rows[i].id+",";}} console.log(debugStr); debugStr1 = new Array(); debugStr1 = debugStr.split(",").clean(""); console.log(debugStr1); $("##table-1 tr").removeClass('nodrop inactive'); /* $.ajax({ url: "index.cfm?action=#arguments.action#&shuffle=true&ajax=true&id="+debugStr1, success: function(data) { $('##promo_rtp').html(data); } });/**/ }HI, great job with your plugin…may i make a question? There is a way to kill the jquery object, cause i will to activate and deactivate the drag&drop on my table.
Thanks in advance.
I looked here a while ago for a solution that would let you “group” rows using ONE table and ONE tbody. I didn’t see anything (and I’m too lazy to search this massive thread now), so I wrote my own and figured I’d share with the community.
Demo: http://www.michaelsoutherton.com/sandbox/tablerowdrag/
Initialization: http://www.michaelsoutherton.com/sandbox/tablerowdrag/jquery.tablerowdrag_groups.setup.js
It requires a pretty specific setup to your table, so it probably won’t fit everyone’s needs. And I’m a jQuery amateur at best, so I’m sure there’s a million better ways to write this. Hope it helps though.
Hi Gigi,
I just did this, so I’ll show you how I accomplished it. Someone can probably come along and refactor my code to be even more efficient as well. This particular bit of code reorders my product categories:
First, call the ajax insert to a div (here called #ajaxresult) just below my table –
onDrop: function(table, row) {
$(‘#ajaxresult’).load(“reorder.php?”+$.tableDnD.serialize());
}
Then, the php script to reorder. This will show a “reorder” and “cancel” button below my table when a row is dropped. If submitted, it will reorder the rows in the SQL database -
<?php // If reorder is submitted
if (isset($_POST['update'])) {
$size = $_POST['how_many'];
for ($k=1; $k<=$size; $k++) {
$old = $_POST[$k];
$query = "SELECT cat_id FROM categories WHERE position='$old'";
$result = mysql_query($query, $connection);
confirm_query($result);
$catids[$k] = mysql_fetch_row($result);
}
$i = 1;
while ($i
<?php // Runs on reorder drag
$result = $_REQUEST["cat_table"];
$count = count($result);
$how_many = $count – 1;
for ($j=1; $j<=$how_many; $j++) {
$old = $result[$j];
$new_value = $j;
echo "”;
}
echo “”;
?>
Hope that helps!
Hmmm…sorry, I don’t know how to get that nice little code box in my comments. The script posted is incomplete. If someone can let me know how to post code correctly, I’ll repost. Don’t want to blow up the comments attempting it!
[...] Table Drag and Drop JQuery plugin (TableDnD) [...]
I got onDragClass to work, but I cannot seem to change the class or style of the dropped row. I want it to stay changed after dropping and ever use of onDropStyle that I could think of does not work. Can you provide an example for that?
I managed to get the dropped box to hold a new style by doing this.
onDrop:function(table,row) {
$(“#”+row.id).addClass(“movedRow”);
}
I was surprised that I could not (or did not know how) to use the value of row to change the row’s class and I tried various combinations of $(row.id). Only this morning it dawned on me that perhaps I needed to add “#”+. It still seems to me this is a bit of a round-about way to get it done. Is there something more straight forward? If the element is contained in row, shouldn’t I be able to manipulate it more directly?
Steven, thanks for your example. It did exactly what I needed it to do. It’s great that this thread is over two years old and still being contributed to.
[...] Table Drag and Drop 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. [...]
Steven – Because the row parameter is already an element, you can pass this straight into $:
onDrop: function(table,row) {
$(row).addClass(“movedRow”);
}
hi,
I have a table that I use the drag and drop for ordering. I have a button that allows the user to add a new row… however, the new row does not work with the drag and drop… any ideas?
Love this script.
Just curious version 1.0 will ever make it out. Anyone test this script on IE8?
[...] Table Drag and Drop 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. [...]
Hi,
Thanks for the script, I have a question,
I am looping over TR and creating a table with items I added this drag and drop feature, my question when I drag a TR first TD in TR should Stay still and rest should move? any suggestion.
Thanks Again for this script.
hi,
i want to change the table row td color after drag and drop the row. i modify the onDrop function and add a class. It seems that the row color will change after i click the row even i have not change its position. I only want to change the row color after dragging to other position. can anyone tell me how i can do that?
onDrop:function(table,row) {
$(“#”+row.id).addClass(“myDropClass”);
},
wuu nice plugin,thanks
Hello all.
First of all, this is a great plugin. It works great for me. Please forgive me if I am asking a totally strange/dumb question. For my table, I implemented the dragStart and onDrop events. Whenever I drag and drop a row, the sequence of events is something like dragStart -> onDrop -> dragStart. Can someone please tell me on how to stop dragStart being called the 2nd time? Thanks a lot.
It’s very useful plugin, thanks a lot!
HI
loving the script!
I have a table each row has an input box for the page order, does anybody know how to update the input boxes with the new row position?
I have a table on the left. And some categories on the right. The table rows show the files present in each category when a category is selected. A file can be dragged and drop into another category. I have a problem. Whenever I drag a file and drop it into a category, it doesnt go into that category instead it goes into the category directly above it. Anybody has any suggestions on how to solve this problem?
Hello, I really appreciate your script, especially the way you wrote the code. It is really easy to modify.
Here a tiny enhancement of my own:
Problem setting: I have multiple tBodies and want to move between them.
The tBodies can happen to have no element and maybe several headings that should not be movable.
Now the problem is, that if you declare the headings as nondrag & nondrop you cannot fill an empty tbody.
You could also declare the lowest heading “nondrag”able only, but then, the code allows you to put elements between the headings.
Hope it got more or less clear, what the problem was.. here comes the solution: (most parts taken from http://gist.github.com/14807)
var nodrag = jQuery(currentRow).hasClass("nodrag"); if ( (movingDown || nodrag ) && jQuery.tableDnD.dragObject != currentRow) { currentRow.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow && !nodrag) { currentRow.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); }It does basically the following: If you want to move the row from the top between a nondrop and a nondrag row, the drop will be made below the nondrag.
eg
..
heading
<————————————————- before, it was possible to drop here.
subheading
.. content>
good night
What about having the table horizontally? I am trying to do something with this plugin where I can rotate the html table any which way, and still have drag and drop functionality. Is this possible?
[...] if they are able to change the order of invoice lines quickly. In my application I used jqGrid with TableDND extension. Here’s how I got it [...]
Hi Denis
Thanks for the great work on this tool; it’s become very helpful, allowing me to build a really nice, intuitive UI component for our CMS.
Whilst I was doing this, I stumbled across a problem with nested draggable tables where content was being generated dynamically and found a solution.
According to the notes above, tableDnDUpdate() should be called after a draggable table has been altered; in my tests, this doesn’t always work and can lead to Javascript errors in line 275 of the code.
After a lot of faffing about, I found a solution: instead of using tableDnDUpdate(), I use tableDnD() after I’ve altered the table in question. This then seems to sort out problems with this function.
Cheers!
Wow.. nice plugin its hleps me a lot..
How can I get the row# or rowID that the row was dragged to? I saw an example by Nathan but couldn’t get it to work.
If you need sorted ids passed by ajax call after a button click when you finish sorting you can use following example
$("#table-cat").tableDnD({ onDrop: function(table, row) { var order = $.tableDnD.serialize('id'); $('.groovybutton').click(function() { $('#result').load("ajax_handler.php?" + order); $('#sort').hide(); }); $('#sort').show('slow'); } });Put a button in a DIV block but keep hidden when page load
‘);
Could it be that the implementation is not working anymore? Whatever I try I just can’t get the (stock) example up and running. Any help?
I have implemented this great plugin into osCommerce admin area to sort categories
To download:
http://addons.oscommerce.com/info/7422
http://www.hepsiparis.com/pub/duzgun.com/Admin_Categories_Drag-n_Drop.v1.rar
Screenshot:
http://www.hepsiparis.com/pub/duzgun.com/screenshot.jpg
Hi All,
I’m also working with oscommerce, i’ve already implemented the catergorie sort order on my own.
Now i also want to implement it for the products. They are in the same table as the categories.
Is it posible to ’split’ the function on for example the class ?
dragHandle: ‘.dragme’ is for the categories.
I thought about copying this code and changing the classname for the products.
But that doesn’t work…
Anyone? Thanks in advance
@Barclay
maybe this helps
[code] $("#myTable").tableDnD({ onDrop: function(table, row) { droppedRow=$(row).attr('id'); prevRow=$('#'+rowDropped).prev().attr('id'); nextRow=$('#'+rowDropped).next().attr('id'); }); [/code]there’s a mistake in my prev posting. replace rowDropped with droppedRow
How do you past query variable to php?
[...] 11. Table Drag and Drop JQuery plugin [...]