Drag and Drop table content with JavaScript

Content of HTML table cells can be dragged to another table 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 destination table cell. Attaching onMouseOver handler on table cells will not work, because browser doesn't fire events to the elements beneath the dragged object. :(

Anyway, after taking care of the current scroll position and calculating table cells positions, here is example that should work in FF3, IE7 / 8 and Google Chrome. Please try to drag green, blue or orange bordered objects, change properties (radio button and check-boxes) and click on "Save" button.
Version 2.3.1
Download redips2.tar.gz
You can not drop here
Drag
and
drop
content
with
JavaScript
Table2
and
Drag
drop
table
with
JavaScript

Table3
Clone
(1) Clone
(2) Clone
Trash
Save content of the first table (it will only show accepted parameters)
Enable dropping to already taken table cells
Disable dropping to already taken table cells
Switch content
Overwrite content
Remove cloned object if dragged outside of any table
Confirm before delete object
Enable dragging

You can try another example built on this Drag and Drop library.

"Save" button will scan tables, create query string and send to the PHP page. Demo shows how to collect table content and accept parameters on the server side. More about accepting parameters you can read in my post Reading multiple parameters in PHP. Orange object "Clone" will be duplicated first because of "clone" keyword in his class name. If you drop object on cell named "Trash", object will be deleted from the table (with or without confirmation). Script has built in autoscroll and option to forbid landing to non empty cells or cells named with class "forbid". Table can contain rowspan / colspan cells and different background color for every cell.

Here are minimal steps to enable content dragging in table:

  • put <script type="text/javascript" src="drag.js"></script> to the head section
  • start drag.js initialization: <body onload="REDIPS.drag.init()">
  • place table(s) inside <div id="drag"> to enable content dragging
  • add .drag{position: relative;} class to CSS file
  • place <div class="drag">Hello World</div> to the table cell

Other features of drag.js:

  • functions and data structure are defined in namespace (easier integration with other JS frameworks)
  • JSLint: No problems found in drag.js (tough one, huh)
  • movable div element can contain form elements and links
  • forbidding or allowing table cells marked with class name "mark" (depends on "marked_cell" parameter)
  • option to define exceptions and allow certain DIV elements to the marked table cell
  • option to define single content cell on the table declared with "multiple" drop option
  • cloning
    • for unlimited cloning add "clone" class name to the div object
      <div class="drag clone">Hello World</div>
    • to limit cloning and transform last object to the ordinary movable object add 'climit1_X' class name
      <div class="drag clone climit1_4">Hello World</div>
    • to limit cloning and transform last object to immovable object add 'climit2_X' class name
      <div class="drag 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)
  • allowed nested tables
  • dropping objects only to empty cells
  • switching content of table cells
  • overwrite table cell content with dropped element
  • table cell with "trash" class name becomes trashcan
  • enabled handlers to place custom code on events: clicked, moved, not moved, dropped, switched, cloned, cloned end1, cloned end2, deleted and undeleted
  • deleting cloned div if the cloned div is dragged outside of any table
  • enabling / disabling dragging

How drag.js works?

Script will search for div elements (with class name "drag") inside tables closed in <div id="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 "left" and "top" styles of the object. 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 mouse move events only when user holds left mouse button on div element.

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 table cell coordinates (with scroll page offset) and store them to arrays. Arrays are searched inside onMouseMove handler and after left mouse button is released, object will drop to the current (highlighted) table cell.

In redips2.tar.gz (34KB) package you will find several examples including example how to save/recall table using PHP and MySQL. Instead of using well commented drag.js (in production) from the package, I will suggest to compress it first. Compression will cut out comments and unnecessary space. Finally, drag-min.js will function as the original but with 50% smaller size.

Happy dragging and dropping!


Related posts

Bookmark and Share

328 Responses to “Drag and Drop table content with JavaScript”

  1. Sajib says:

    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. dbunic says:

    @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. CaptainPlum says:

    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. ceolic says:

    Thank you Dbunic, it works perfect.

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

  5. Kevin says:

    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. Kevin says:

    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. CaptainPlum says:

    Thanks for the reply ceolic...I will look into that....:)

  8. aalap says:

    Hi,

    I want to switch content between cells.
    can you please tell me way to do this ?

  9. dbunic says:

    @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';

  10. dbunic says:

    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!
    ;)

  11. aalap says:

    hey thank you so much man.
    you cant realize my happiness.
    You made my work really easy.
    thanksssss.

  12. Luc says:

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

  13. aptd says:

    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!

  14. CaptainPlum says:

    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...:)

  15. [...] Demo  Posted in ajax | Tags: drag and drop, javascript, table content « Simple AJAX [...]

  16. Rajesh says:

    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?

  17. dbunic says:

    @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 ...

  18. joekki says:

    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

  19. Sefan says:

    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

  20. dbunic says:

    @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 Reply