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. It’s very nice example. In my case everything is ok. But problem is i want to drag a div(3 Colspan and 2 rowspan) into another table row which is only one cell(1 column and 1 row). Now thing is this one cell will automatically span with 3 column and 2 row. Please give me a advice.
    Thanks.

  2. @ceolic – Code is upgraded and now you can define some DIV elements to be placed to some marked table cells. The demo shows how DIV element with smile image can be placed to the right lower corner of the table2. After drag initialization comes line with defined DIV id and destination class name.

    // allow dropping DIV id=”d8″ to the marked cells with class name “smile”
    REDIPS.drag.marked_exception.d8 = “smile”;

    @Sajib – I will gladly help you if you can show me the problem. Perhaps some public (test) URL …

  3. Was playing with this script, and used a css to align the tables beside each other

    ie #table1 {
    float: left;
    }

    Seems When I added this code and the tables lined up next to each other the drop and drag stopped working between tables…at least in chrome, and in IE they have a hard time finding the last table. I am wondering if there is something I can adjust in the drag.js to help.

  4. Thank you Dbunic, it works perfect.

    CaptainPlun: yea i found this too, only way seems to use relative or absolut positions.

  5. Its me again!

    Perhaps my question was mis-read or I am misinterpreting the how to use the marked_cell option.

    Say we have a list of table cells each with a pretty little thumbnail in them.

    At the top right of the page there is table cell similar to the trash, but its functionality will be a QuickList.

    I don’t want the thumbnails to be able to be dropped onto any other cell.
    Thumbnails should only be able to be dropped onto the QuickList’s table cell.

    Is this at all possible?

  6. Fantastic… I got it working with the marked_cell option just as you described.

    However there is one major limitation. I am unable to apply this script to an entire page for two simple reaons.

    – The ‘drag’ ID can only be applied to a single div.
    – Applying the ‘drag’ ID to a container div, child div’s don’t function properly. The script can’t seem to figure out which table it in and ends with:
    tables[table] is undefined. Line: 189.
    Seems the tables list is built on line 108-113.

    If anyone else was having this issue, commenting out line 110 and 113 seems to be the fix.

  7. @aalap – After package initialization, you can define drop_option property otherwise default drop_option will be presumed. In your case, initialization can look:

    // package initialization
    REDIPS.drag.init();
    // switch table content
    REDIPS.drag.drop_option = ‘switch’;

  8. drag.js lib is updated:

    1) New format for cloned id, instead of:
    obj_new.id = ‘c’ + cloned_id + ‘:’ + obj.id;

    now is:
    obj_new.id = obj.id + ‘c’ + cloned_id[obj.id];

    2) Separated id counter for each clone element

    3) Fixed id counter for cloned elements (when called myhandler_uncloned)

    4) Removed adding “d” to the cloned element

    5) Action handlers are empty by default

    6) target_cell and source_cell are exposed as public properties

    Demo contains new examples and it will be published soon …
    Stay tuned!
    ;)

  9. Wow! This is fantastic!
    Great working examples to quickly understand the workings and possibilies. Thanks for making this great script available.

  10. Hey Redips! Your script is wonderful. However, after reading through all the comments, I am extremely interested in getting this to work with my php database. I cannot seem to even get the data from the multiple parameters php to get into my database. I know it may be asking too much but is it all possible to get an example .php file that would send the data to the database. And an example .php edit that would pull the data from it?

    Another thing, I plan on making each table cell to have a drop down box as well as 2 text input boxes. That is all the data that will be in there…Would my insert method be different because I also read about just a simple form submit…but I don’t imagine that would save the cell position.

    Any help would be greatly appreciated, and I am sorry if I am implying that I want you to do all the work, but you make it sound so easy and it’s been quite a bit since I have worked with php and mysql.

    Thank you so much for your time!

  11. Well, for my first attempt at using js, I’m sure having fun….:) Beautiful script by the way, have no idea how it works on the inside but it’s just what I need.

    Been reading over all the comments, is there any way (besides setting the flags) to have one cell single content only, and have other cells multiple content. Trying to make a webpage for organizing web servers….there can only be one admin server in every column but many managed servers. I have 2 rows, one for admin, one for managed. I’d like the admin row to be single content only.

    ceolic…:) I tried that absolute setting in my css….my poor cell/DIV contents slid behind the tables. My web skills are not that advanced, but thanks for the lead…:)

  12. Hi,
    This is beautiful. I incorporated in my project and it has reduced my work drastically. However, one problem I found was that it was not working for nested tables. Any idea, why that is so? Can it be rectified easily?

  13. @aptd – Hi, in next few weeks I will try to finish demo with sending placed elements to the database and vice versa. New demo will be announced on Twitter. I think that you can have more input elements in one cell. If not, we will fix it ;)

    @CaptainPlum – In this version it’s not possible to have different dropping rules at the same time. Anyway, your request seems reasonably and I will try to update library to allow “single” cell behavior regardless on “multiple” defined drop_option. This requires modifying set_trc() private method …

    @Rajesh – Locutus asked the same question a month ago. Hopefully, with copy&paste here goes answer: Script initializes only first child nodes (tables) of DIV id=”drag”, tables in deeper hierarchy are ignored. So in your case you should comment two lines in init method. Original loop at line 110 looks:
    for (i = 0, j = 0; i < tables_nodeList.length; i++) {   // include only child tables of DIV id="drag", tables in deeper hierarchy are ignored   if (tables_nodeList[i].parentNode.id === 'drag') {     tables[j] = tables_nodeList[i];     j++;   } } ... and you should comment "if" lines like: for (i = 0, j = 0; i < tables_nodeList.length; i++) {   // include only child tables of DIV id="drag", tables in deeper hierarchy are ignored   //if (tables_nodeList[i].parentNode.id === 'drag') {     tables[j] = tables_nodeList[i];     j++;   //} } Hope the same trick will help in you case too ...

  14. Hi,

    I have the same problem than Sajib. How to get rowspans working?

    http://codesolutions.fi/demo/redips/example1/index.html

    There’s a modified version of example1. I have added some rowspans there and it stopped working. When it worked – it didn’t move the rowspan values to the other TDs, just the div?

    Detailed of what I’d like to happen:

    and moving that rowspan=”2″ td would move it to the next tr.

    Thanks!
    – Juha

  15. Hi,

    nice script.

    I was wondering, if it is possible, change the save button into a button sending the changes of the table to an email adress, basically like a html/php form, and include maybe some other input form elements?!

    Thanks in advance for your reply.

    Cheers,
    Stefan

  16. @joekki – Example1 has output from event handlers to the element with id=”message” (cell in second table). To make your example work, create element with id=”message” or simply delete “myhandler” definitions of event handlers in index.html file. In your case, window.onload can look:

    window.onload = function () {
      // initialization
      REDIPS.drag.init();
    }

    @Stefan – Sending current state of arranged elements to some email.php doesn’t sound complicated. The harder part is how to find the differences between previous and current state. Drag and Drop library doesn’t have functionality to track changes, so I will suggest to make it on the server side. PHP script (or other server script) should accept parameters from the “Save” button and compare it with the previous saved data. And finally, differences can be emailed to the recipient. This is just a concept, but it might work.

    @CaptainPlum – Drag and drop library (and examples) are updated. Now is possible to define single content table cell on the “multiple” board – just define table cell with class name “single”. In example 2 corners of main table are defined as single (as well as destination green and orange cell).

Leave a Comment