Drag and Drop table content with JavaScript

Content of HTML table cells can be dragged to another cell or another table. It isn’t difficult to define onMouseMove handler and change top / left element styles to move the object. In case with tables, you will have to determine somehow target cell. Attaching onMouseOver handler on TD elements will not work, because browser doesn’t fire events to the elements below the dragged object.

Anyway, after taking care of the current scroll position and calculating TD positions, REDIPS.drag should work in recent major browsers like Google Chrome, Firefox, Safari, Internet Explorer, Opera and mobile devices as well. Click on image below, will open live demo where you can drag green, blue or orange bordered DIV elements, change properties (radio button and check-boxes) and click on “Save” button.

Download latest version redips2.tar.gz


REDIPS.drag example01

In this example “Save” button will scan table content, create query string and send to PHP page. Demo shows how to collect content and accept parameters on the server side. More about accepting parameters you can read at Reading multiple parameters in PHP. “Clone” elements (orange in this demo) will be duplicated first because of “redips-clone” keyword contained in class name. If you drop object on cell named “Trash”, object will be deleted from the table (with or without confirmation). Library has built in autoscroll and option to forbid landing to non empty cells or cells named with class “redips-mark”. Table can contain rowspan / colspan TDs and different background color for every cell.

Here are minimal steps to enable content dragging in table:

  • put <script type=”text/javascript” src=”redips-drag-min.js”></script> to the head section
  • initialize REDIPS.drag library: <body onload=”REDIPS.drag.init()”>
  • place table(s) inside <div id=”redips-drag”> to enable content dragging
  • place <div class=”redips-drag”>Hello World</div> to the table cell

Other features of REDIPS.drag library:

  • methods and data structure are defined in namespace (easier integration with other JS frameworks)
  • all JavaScript code is checked with ESLint
  • REDIPS.drag documentation generated with JsDoc Toolkit
  • drag and drop table rows
  • movable DIV element can contain other HTML code (images, forms, tables …)
  • forbidding or allowing TDs marked with class name “redips-mark”
  • option to define exceptions and allow dropping certain DIV elements to the marked cell
  • option to define single content cell on the table declared with “multiple” drop option
  • cloning
    • for unlimited cloning add “redips-clone” class name to the DIV object
      <div class=”redips-drag redips-clone”>Hello World</div>
    • to limit cloning and transform last object to the ordinary movable object add ‘climit1_X’ class name
      <div class=”redips-drag redips-clone climit1_4″>Hello World</div>
    • to limit cloning and transform last object to immovable object add ‘climit2_X’ class name
      <div class=”redips-drag redips-clone climit2_4″>Hello World</div>
    • where X is integer and defines number of cloned elements (in previous examples, each climit will allow only 4 cloned elements)
  • unlimited nested tables support
  • dropping objects only to empty cells
  • switch cell content
  • switching cell content continuously
  • overwrite TD content with dropped element
  • shift table content
  • table cell with “redips-trash” class name becomes trashcan
  • enabled handlers to place custom code on events: changed, clicked, cloned, clonedDropped, clonedEnd1, clonedEnd2, dblClicked, deleted, dropped, droppedBefore, finish, moved, notCloned, notMoved, shiftOverflow, relocateBefore, relocateAfter, relocateEnd, rowChanged, rowClicked, rowCloned, rowDeleted, rowDropped, rowDroppedBefore, rowDroppedSource, rowMoved, rowNotCloned, rowNotMoved, rowUndeleted, switched and undeleted
  • deleting cloned DIV if the cloned DIV is dragged outside of any table
  • enabling / disabling dragging
  • animation (move element/row to the destination cell/row)
  • added support for touch devices (touchstart, touchmove, touchend)

How REDIPS.drag works?

Script will search for DIV elements (with class name “redips-drag”) inside tables closed in <div id=”redips-drag”> and attach onMouseDown event handler. When user clicks with left mouse button on DIV element, onMouseMove and onMouseUp handlers will be attached to the document level.

While dragging DIV element, script changes its “left” and “top” styles. This is function of the onMouseMove handler. When user releases left mouse button, onMouseUp event handler will unlink onMouseMove and onMouseUp event handlers. This way, browser will listen and process mousemove events only when DIV element is dragged.

As I mentioned, onMouseDown is defined on the elements you want to drag. Elements beneath the dragged object will not be able to catch onMouseOver event. Why? Because you are dragging object and that object only can catch the onMouseOver event.

So, to detect destination table cells, script calculates all cell coordinates (with scroll page offset) and store them to the array. Array is searched inside onMouseMove handler and after left mouse button is released, DIV will drop to the current (highlighted) table cell.

In redips2.tar.gz package you will find many examples including example of how to save/recall table using PHP and MySQL. Package also contains and redips-drag-min.js – a compressed version of REDIPS.drag library (compressed with Google Closure Compiler).

Happy dragging and dropping!

1,196 thoughts on “Drag and Drop table content with JavaScript”

  1. @Adam – Yes, this could be possible:

    1) Every draggable DIV should get ID (needed to distinguish DIV elements).

    2) Define array which contains forbidden combination (which table cell data can’t go to some table cell). For example, DIV elements could have IDs like id=”drag1″, id=”drag2″, id=”drag3″ … and array could look like arr[1] = [3, 2] (DIV with id=”drag1″ can’t go to the row=3, col=2), arr[2] = [1, 4] (DIV with id=”drag2″ can’t go to the row=1, col=4)

    3) In JS function set_tcr (set current table, row and cell) at the bottom, right after testing “if cell is marked as forbidden”, you can place code to compare dragged object and landing table cell. Comparison should use obj.ID (obj is global reference to the dragged element), current row and cell like in line “if cell is marked as forbidden” and previously mentioned array.

  2. @momo – You will have to somehow save the order of table cells to the server (probably to the database), but first, order should be created. Please note a new button “Click” and you will see how to prepare / create content order of the first table. Instead of displaying, you can modify JS function to save parameters to the server.

    @arjan – DIV elements (green / blue bordered) are placed in table cells and can contain any kind of HTML content as well as links and images.

    @James – Actually, you can. If you place more then one element to the same cell, try to click on the upper element. It should go to the cell bottom. This way, you have option to re-order elements within the same table cell.

  3. This is a clever idea. I stumbled on it while looking for a solution to the problem that Danny Goodman’s older JS doesn’t work properly in IE8 (at least not for me). This page also has problems in IE8, but works fine in IE7 and FireFox. Any ideas what is wrong in IE8?

  4. Sorry, forget my last message. The problem was the “vertical-align:baseline” in the general CSS of tbody, tr and td. Incredible but true. regards

  5. Nice job!!!
    Question from a newbie… Would it be possible to let the element “stay” where it is located and drag and drop a copy of it instead? This in order to (some kind of) “duplicate” the same element to more than one cell.
    Thank you in advance for your reply.

  6. @Dave – I didn’t install IE8 yet, but when IE8 comes to my hands, I will try to fix script.

    @jp – Now it’s possible ;) … Please try to drag “Clone” element from the third table. Details can be found in clone_obj() JavaScript function. So, if div element have “clone” in his class list, than it will be cloned instead of dragged.

  7. I’ve noticed you’ve just added a clone part! – at exactly the same time as I did a modification of your code to do the same, with a slight variation however: You may want to have the concept of a source and sink. I have special cells that will infinitely reproduce their contents, and also dispose of them should they be dropped into there (any). I’m using this in the context of : Imagine a whiteboard with little magnets, you may want to have many magnets saying “Out of Office” for example, but when finished with them, you want to just throw them away.

    Anyway, I really like what you have done here! Thanks!

  8. This looks great ! dbunic is this code free for use on commercial and non-commercial web sites ? I would like to use this code. Do let us all know under what license it is being released. Thank you for your work and time.

  9. Are there any examples of this script in use? It looks like a pretty cool script but I’m having trouble thinking of what I might actually use it for in a website design.

  10. Hi again, this code is great. If you paste this code:
    var target = getEventSource(e);
    if (target.tagName == ‘INPUT’ || target.tagName == ‘SELECT’) return true;

    at the top of handler_onmousedown function, you will add in dragable div buttons, textboxes, selects and other html elements and change their values.

    Sorry for my English.

  11. I forgot paste this function:

    function getEventSource(evt) {
    if (window.event) {
    evt = window.event; // For IE
    return evt.srcElement;
    } else {
    return evt.target; // For Firefox
    }
    }

  12. @paulrho – You are right, if I have infinity source of objects, it will be nice to have and Trash cell (object dropped to the Trash should be deleted). Thank you for the idea!

    @Marc – I didn’t think about any licence for my code, but after I found explanation that releasing code without a license only does one thing – it declares a copyright without explaining how others can use this code – I chose BSD license. So answer to your question is: yes, this code is free for use on commercial and non-commercial web sites.

    @vbdietz – Well, my intention was to show how to drag and drop div elements inside table. You have option to clone div object, to declare table cell as trash can, to forbid landing to some table cell and so on. If you need a such functionality, I hope this post will help. Anyway, I have only this demo and more examples will come soon.

    @Damian – Thank you for improving drag and drop script.

  13. I tried to setup the dragable input elements as Damian’s description. However, I have the following error from time to time, any ideas?

    ‘table[…].rows[…].cells’ is null or not an object

  14. Nice. Does anyone have some practical examples to show how this can be used? Thanks.

  15. @Newbies – Now is possible to drag and drop form elements also. Please try the last version, it should work without any error.

  16. I am looking to add a clause where if the user is asked upon dropping the object into the new table cell if they want to move that object, and if not the object stays in its original cell, however I am finding this difficult to code, here is what I have got so far:

    var answer = confirm (“Do you want to move ” + div_text + ” ?”)
    if (answer)
    alert (“Move Successful”)
    else
    alert (“No move was made”)

Leave a Comment