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



I can update MYSQL DB :S
Any GooD Example … ?
sorry, i say “I CAN´T”
[...] Table Drag and Drop [...]
[...] Table Drag and Drop [...]
Just offering a heads up to anyone trying to use UUID’s as TD ID values. Make sure you set the serializeRegexp value when calling tableDnD, as per the following:
$(‘#table’).tableDnD({
serializeRegexp: /^.*$/
});
Thanks for this, it’s a pretty great plugin. I made one tiny addition which I thought you should incorporate back into the release.
I have a table where every other row is dark. While dragging the rows, I need to update the classes on the fly otherwise the table looks broken because there might be two dark rows together.
I added an “onDrag” variable to the config right under onDragStart (line 102). Then in the mousemove function on line 279, I added: if (config.onDrag){ config.onDrag(); }
Hope this helps!
Thanks this thing is really cool
I noticed load of people on here looking to drag and drop multiple rows anyone had any luck with that?
anyone working on it? I have a project where it would meet my spec perfectly if i had this ability.
otherwise anyone seen a tree with drag and drop reordering that has full html nodes not just text and an image?
Thanks again
Is there any possibility to integrate this fine plugin into TinyMCE ? This would be great for me…
Hi, nice plugin.
Just wanted to say that the function onDragStart(table, row) actually returns the table cell instead of the table row for me. The documentation above says it should return the row.
Also, you say that the plugin generates a javascript error when you drag beyond the bounds of a tbody. For me, not being able to drag outside of a tbody turned out to be a feature, very useful to constrain the range. The error message can easily be suppressed by enclosing line 273 and line 275 in try catch blocks. This won’t break the functionality at all.
Hi I am trying to drag and drop with dynamically added rows.. but does not work. The static rows can be dragged but the newly added row is not draggable. Means cannot pick the new row and move.. where as the static rows can be moved even in place of the new row.
Any suggestion.
Actually I am creating a table dynamically and wants all the rows to be draggable before saving.
Does this plugin support nested lists? Or sub-lists as someone else called it.
Regarding onAllowDrop implementation.
I need parse both parameters in the function passed by onAllowDrop
So (Pau talk about it above) I changed:
(line 269 –not line 301– @ version 0.5)
var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
by:
var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj[0], y);
In this way I can do something like:
function myAllowDrop(draggedRow, myrow) {
var mycel = draggedRow.getElementsByTagName(“td”)[0];
if (mycel.innerHTML != “ ”)
{
var mycel = myrow.getElementsByTagName(“td”)[0];
if (mycel.innerHTML != “ ”)
return true;
}
}
Thanks Denis for your work…
Hi,
I need to implement a drag and drop functionality in web. The situation is, I need to drag content from a popup window and drop it into the parent window. Is that really possible? Please let me know.
Thanks in Advance
Hi. Just wanted to ask about progress implementing dragging between tables…
[...] Table Drag and Drop [...]
Can any one help me with this plug ins
http://www.isocra.com/2008/02/table-drag-and-drop-jquery-plugin/
In this plug ins user can drag and drop only one row, my need is to drag and drop Multiple row.
Thanks
Nilesh J. Macwan
Thank you! Very good script!
The (X)HTML-standard indicates that the ID-attribute cannot start with a number which seems to be the key requirement for this plugin to work. Please tell me I’m wrong?
It doesn’t have to start with a number. I think some of the examples used above do start with a number, but others do not. For example, table5-row-4 is the format for one set of ids in the examples above.
It is nice to have some kind of key value system for you ids, so that you can relate back to the appropriate database row for updates. You just need to parse out text aspects of the id on the backend.
[...] Table Drag and Drop JQuery plugin [...]
Nino printed some work back in 08 on how to use with scrollable div. Seems (prob due to cut-n-paste issues) to be problematic. does anyone have this effort functional? New (sadly) to all this javascript/jquery so I’m weak and learning but need to use something like this
Any help appreciated – Thanks!
Hi
very nice script
can anybody help on the following senerio
i am having one html table inside the div and i am trying to drag the rows its working for me but the vertical div scroll is not working if the selected ro dragged at the end of the div
please give me any solution
thanks
irfan
Good stuff.
To drag only when using the left button, insert
if (ev.which != 1) { return false; }at the beginnig of the two
.mousedown(function(ev) { (...)handlers.the which property is provided by jQuery, so no need to worry about compat.
Also, since “dragObject” is not assigned, there is no need to modify the *move and *up events.
very good
but i want to drag and drop / and then to vote … i can do this ?
i want this for an top 20 music
you can help me pls ?
Hi guys,
Thanks for this great script !
I need to implement a sortable list (with drag n’ drop). It works fine.. but I need to use pagination to navigate through the list.
Do someone have an idea on how to implement this drag n’drop script with a pagination ?
Many thanks,
Chris
I know that people have been asking for a while how to drag table bodies, instead of just individual rows. I needed this functionality for a project, so I spent some time figuring out how to implement the solution.
It turns out that this requires relatively minor changes to the script. By changing the two lines below, you can drag table bodies just as you drag rows. If you use this solution, you will have to enclose even individual rows in tags. However, the nice part is that you can drag multiple rows as if they were a single row.
line 130: jQuery.tableDnD.dragObject = this.parentNode.parentNode;
line 285: var rows = jQuery.tableDnD.currentTable.tBodies;
[...] Table drag and drop jQuery plugin – Link. [...]
Can anyone suggest me how to drag multiple rows?
Hi Jon Can you please provide me the script you used for dragging and dropping multiple rows
Hi Rocky,
The solution I posted should work for you, if you are using the version of tablednd from this site.
You will need to enclose individual rows in tags, in addition to enclosing multiple sets of rows.
So for example:
row-one
row-two
row-three
Let me know if it is still not working, and I can paste the complete script. However, I only made the two changes above.
I think I see the problem….my code only works if you use a drag handle. I will have to make another tweak in another place if you aren’t using a draghandle. Let me know if this is what you need.
Excellent script. Was initially confused that there are two versions of the script – one for use with jQuery, and one without. This explained the “tableDnD is not a function” error message I was getting when trying to use the non-jQuery version…
Jon – Thanks for your tweaks to enable the selction of multiple rows using the Drag Handle. You mentioned that it could work without the use of the Drag Handle. Would you be so kind as to show us to make it work without the use of the drag handle?
Thanks!
Thank You for this precious litte tool. Helped a lot
Cool script. Love it.
I want to be able to use your script to nest rows under their parent rows. Is this possible?
Example:
row1
row2
row2.1
row2.2
row3
row4
row4.1
row4.2
row5
Hi Jon Can u please post the script?
Appreciate your help.
Have a Great day
Here is a link to my script.
http://www.878282v6y7k.com/dev/
I would like to be able to nest rows under their parent rows. Something like this.
[drag] Sports 2 Yes
—[drag] Golf 2 Yes
—[drag] Football 2 Yes
—[drag] Baseball 2 Yes
[drag] Tech 2 Yes
—[drag] Mobile 2 Yes
If you change line 142 to:
var rows = jQuery(“tbody”, table); // get all the rows as a wrapped set
…that should work to drag tbodies with any td. You will also have to make this change I posted earlier to line 285:
var rows = jQuery.tableDnD.currentTable.tBodies;
That should work for dragging tbodies when draghandle is not specified. I haven’t had time to test this thoroughly…so there might be bugs. I have done most of my work with the drag handle functionality enabled.
Hope that helps. Let me know if there are problems.
Any news on dragging between tables?
With two tables (left and right) I can implement something like ‘available’ and ’selected’ by dragging rows from left to right table and sorting the selected items by dragging a row up and down in the richt table
Hi
I want to drag drop rows between two tables
Can this possible.
If yes please send me th code
Thanks for nice script, but i want the swaping of rows, means if the inicially the arrangement is
r1
r2
r3
r4
then
if i drag r4 to r1 then the arrangement will be
r4
r2
r3
r1
Hey Everyone,
First of all, I’d like to give props to DenisH for the great plug-in! It really saved my a lot of time on a Form Builder project I’ve been working on recently. I am, however, disappointed to see that development on TableDnD seems to have come to a halt, but I know how it is maintaining free software — a thankless job to some degree.
Anyway, the main reason I’m posting is to share a minor modification I made to your script. After I coded this “hack” (which took only about 20 minutes, thanks to your fantastic API), I realized that it’s not that uncommon a need, and that other developers might be wishing there was a similar feature implemented.
Basically, I needed to prevent the onDragStart callback from being called just because of a quick, single click on a drag-able row. What I mean is, I didn’t want my onDragStart callback being called when the user was just CLICKING on the row, and NOT trying to actually drag the row around. In the Form Builder script I’m developing, the onDragStart callback causes the inline “Edit Question” form to be hidden, which is required if your dragging a row — but just irritating if you don’t intend to actually drag it. So basically, this little mod just makes the script a little more intuitive (IMO), and helps to determine what the users intent was (to drag, or simply click?).
To implement this, I modified the makeDraggable method in the plug-in itself. Inside the conditional that checks to see if an onDragStart callback is specified, I added a few lines. First the mouse offset position (Y cord only) is stored when the dragCell is initially clicked. Then, bind a callback to the “mousemove” event on the dragCell element. When mousemove fires, I simply get the new mouse offset Y cord, then check to see if it’s changed (increased or decreased) by at least 2 pixels (you could easily change it to however many pixels). If it has, the onDragStart callback is called, otherwise it’s not. In either case, I DO clean-up by unbinding the mousemove callback.
I hope someone may find this snippet helpful. I thought it was a useful little enhancement. Please do note, the code I’ve included ONLY works if your using a dragCell (not the entire row). However, one could easily take my code snippet and move it into the else statement a few lines below which is for those not using a dragCell. I just didn’t need that.
If anyone needs some help with this, feel free to drop me a line at snconners@gmx.co.uk, as I don’t frequent DenisH’s blog often.
————–
Grab TableDnD v0.5 with my mod here: http://pastebin.com/f31a6ee99
Or, here’s just the modified code itself, starting around line 146 of v0.5:
jQuery(this).mousedown(function(ev)
{
jQuery.tableDnD.dragObject = this.parentNode;
jQuery.tableDnD.currentTable = table;
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
/*
** Only call onDragStart if
** mouse moves > 2px (on Y)
*/
if(config.onDragStart)
{
var startOffset = jQuery.tableDnD.mouseOffset.y;
jQuery(this).bind(“mousemove”, function(ev)
{
var newOffset = jQuery.tableDnD.getMouseOffset(this, ev).y;
if(newOffset > startOffset + 2 || newOffset < startOffset – 2)
{
config.onDragStart(table, this); // Call onDragStart callback
}
});
}
return false;
});
i have just used the table DND and saved sort-list in cookies. That means the sor-list can be reloaded on table-sort initing later.
The sort-function can be inserted in build block of tableDnD main codes.
$(document).ready(function() { // Initialise the table $(".tbl").tableDnD({ onDragClass: "myDragClass", onDrop: function(table, row) { var rows = table.tBodies[0].rows; var orderStr = ""; for (var i = 0; i < rows.length; i++) { orderStr += rows[i].id + ";"; } $(".test").html(orderStr); $.cookie("order_"+ $(".plabel").text(), orderStr, { expires: 30}); }, containerDiv: $("#viewContent") }); $(".test").html($.cookie("order_"+ $(".plabel").text())); });Hallo Nino Dafonte,
your codes of “TableDnd in Scrolled Div” have a bug, that the scroll can only be scrolled down. Scroll up is not working . The codes shuld be changed like so:
var container = config.containerDiv; var yOffset = container.scrollTop(); var windowHeight = container.innerHeight(); var y = mousePos.y - jQuery.tableDnD.mouseOffset.y; var upperBorder = container.position().top + container.innerHeight(); // not error, it should also be added because mouse moves up var bottomBorder = container.position().top + container.innerHeight(); if (mousePos.y > bottomBorder) { container.scrollTop(container.scrollTop() + config.scrollAmount); } if (mousePos.y < upperBorder) { container.scrollTop(container.scrollTop() - config.scrollAmount); }It is possible now drag and drop under parent rows?
Thanks.
Great script! Do you have any plan to add drag/drop column instead of row?
I have a problem with this plugin when using dragHandle.
If you stay with the cursor with mousedown just between 2 td’s the drag image will appear on both.
It’s very annoying. :/
Anyone could help me fix it? I’ve tried alot and I don’t think this plugin will be updated anymore, right?
Thanks
Also, I can’t get tableDnDUpdate() working ;/
I was able to get the multi-tbody functionality working with a little effort:
Modify code near line 270:
if (currentRow) { // same tbody if (jQuery.tableDnD.dragObject.parentNode == currentRow.parentNode) { // make sure we've moved if (jQuery.tableDnD.dragObject != currentRow) { if (movingDown) jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); else if ( !movingDown) jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); } } // different tbody else { var tmpObject = jQuery(jQuery.tableDnD.dragObject).detach(); if (movingDown) tmpObject.insertBefore(jQuery(currentRow)); else if ( !movingDown) tmpObject.insertAfter(jQuery(currentRow)); } }Note: requires jquery 1.4! (the detach() method makes the magic happen…)
Regards,
Landon
[...] 7. Table row 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. [...]