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

    @dbunic :

    Sorry, my bad. :-D Now those has been deleted. But the main problem still remains. If I make a rowspan=4, the whole td wont move - just the div inside. I assume that this script has been designed this way but is there any change to move the td instead the div inside?

    My purpose is to make similar content moving system than this: http://arshaw.com/fullcalendar/ (click on the "week" -view.)

  2. CaptainPlum says:

    Thank you so much dbunic, the single cell class works perfect. AWESOME :)

  3. dbunic says:

    @joekki - Unfortunately drag.js library is designed to move table content only. Moving TD means to modify HTML table and such functionality isn't supported yet.

    @CaptainPlum - I'm glad it works ... Cheers!

  4. Rajesh says:

    Thank you dbunic. I tried your suggestion (commenting the 2 lines to also use nested tables). So now I have this kind of structure: Parent Table has 2 columns - Column 1 contains my source table and column 2 contains my target table. When I am moving the cell from source table, it is not identifying any cell in target table, where I can move. In the case, where I do not have the parent table (no nested structure), everything works fine. Any idea why this may be so?

  5. dbunic says:

    @Rajesh - Script scans table rows of all tables inside <div id="drag">. In your case table row of nested table is overlapping with parent table and that might be confusing. Because of that reason, tables in deeper hierarchy are ignored (two commented lines). If you need parent table only to establish page layout, you can use DIVs to place tables (source and target) side by side. Placing tables with DIVs instead of parent table should work without problems. But please wait till I modify drag.js to enable using DIV layout.

  6. Neke says:

    Cool stuff! Nicely done!
    I wonder... I'm trying to build a site in which images are split on 2 columns, and by clicking on any of those images, I'd move it from one column to the other. Could you tell me a little on which functions should I look into for this to work? thanks!

  7. S.Marr says:

    Excellent script, has made my workload a bit easier. One question, I have been trying to tinker with the code so that the "REDIPS.drag.myhandler_dropped" function would pass the "query" from "save_content" to the .innerHTML. I have tried various ways of doing this, but cannot get it to work correctly. I have checked previous posts for this but no clues there. Am I missing something? or is just not possible to get it to look the same? (I suspect the former.), Kind regards. S.Marr

  8. Traigo says:

    Excellent script! I needed an event to fire after an object was moved that would access the DOM with the table and the moved object at it's new location. I didn't see a handler that would do that so I added one: REDIPS.drag.myhandler_afterMove. I set it to call after the object was appended to the table cell. Hope it would be useful to someone else and you can add to your script.
    Traigo

  9. dbunic says:

    @Rajesh - Drag and drop library is updated. Now is possible to prepare DIV layout and place tables for dragging. In redips2.tar.gz package you can find example3 School timetable where tables are placed side by side. Left table contains school subjects while right table is timetable.

    @Neke - If it is need to move image from one column to the other column, than before moving you should define image reference and reference to the target table cell. For example, if image reference is obj, and reference to the target cell is target_cell, then appendChild will relocate image:

        target_cell.appendChild(obj);

    The same reference names are used in drag.js so you can peek to the code.

    @S.Marr - save_content() function can be modified to return the "query" instead of opening popup window.

        //window.open('multiple-parameters.php?' + query, 'Mypop', 'width=350,height=160,scrollbars=yes');
        return(query);

    So, if you call modified save_content() in REDIPS.drag.myhandler_dropped, returned value from the save_content() can be used for further activities. You said about innerHTML property, but I didn't understand to whom belongs this property.

    @Traigo - That event handler already exists and its name is REDIPS.drag.myhandler_dropped. Please take a look at example 2 where myhandler_dropped is extensively used. Thank you and I'm glad you found my script useful.

  10. BInair10 says:

    Hello There,
    The drop and drag works good, I make some changes.
    Now I want to store the actual view off the table's.

    Do one of you have a suggestion?

    Regards Binair10

  11. S.Marr says:

    dbunic, thank you very much for that. It is exactly what I needed. (although I should have seen it myself). Kind regards, SMarr.

  12. Neke says:

    thanks a lot! I'll try to get it working!

  13. traigo says:

    @dbunic
    I looked through it, but for some reason, when I would set myhandler_dropped, it would fire before the div was appended as a childNode. So when my function would run, it would see no child elements for that TD. It was probably something I was doing wrong. I'm also trying to narrow down another small problem. The z-index is remaining 999 after a drop. I've noticed it in your examples as well. I'm testing around it right now to track it down. It seems to stay 999 only when you drag from one table to another. If you drag to within the same table, it changes to 10. If I find a solution, I'll post.
    Thanks, and great work!

    Ubuntu 8.10 x64 Firefox 3.0.17

  14. traigo says:

    Found it. It's on right-click. The onmousedown event handler captures right-click events. When I was right-clicking to view the firebug output, it was setting the z-index to 999 but not back to 10. Adding an event handler for oncontextmenu can just set it back to 10.

  15. Ron says:

    An example showing how to save and recall the table using PHP, for PHP neophytes like myself, would be invaluable.

  16. aptd says:

    Thank you dbunic and I look forward to php database examples!

  17. HKad says:

    Hi

    I love your code. I have a small problem. I have defined a table with 2 rows and 2 cols.

    row=0 col=0 div id=d1
    row=0 col=1 div id=d2
    row=1 col=0 div id=d3
    row=1 col=1 div id=d4

    I am using drop_option = 'switch'.

    Before swapping any cell content, if i press save button, i get query variable showing me all 4 DIV ids.

    p[]=D1_0_0_0&p[]=D2_0_0_1&p[]=D3_0_1_0&p[]=D4_0_1_1

    If i swap cell content of any cell except first cell (row=0 & col=0), I get correct query variable on clicking save button,

    p[]=D1_0_0_0&p[]=D4_0_0_1&p[]=D3_0_1_0&p[]=D2_0_1_1

    however anytime i swap content of first cell (row=0 & cell=0) i do not get the div id for that cell as part of query on save button.

    p[]=D4_0_0_0&p[]=D2_0_0_1&p[]=D3_0_1_0 <<<<<<<< D1 is missing

    Any idea whats going on?

    I am not sure if i made myself clear, but here is what you can do

  18. dbunic says:

    @Binair10 - Demo package is updated and contains example3 (PHP / MySQL) with saving / restoring from database.

    @traigo - Maybe the reason was due to calling myhandler_dropped before appendChild. I have change order in handler_onmouseup and it looks:

    // append object to the target cell
    target_cell.appendChild(obj);
    // call myhandler_dropped because clone_limit could call myhandler_clonedend1 or myhandler_clonedend2
    REDIPS.drag.myhandler_dropped(target_cell);

    Hope this will fix the problem, if not we will try again ... ;)

    @Ron, @aptd - You are right guys. Demo package now contains PHP / MySQL example3. Please try ...

    @HKad - Thank you for pointing to the problem. I will create 2x2 table and test save_content() function to see what is going on.

  19. Ron says:

    Thank you very much Darko, your example really helped!

  20. dbunic says:

    @HKad - save_content() is slightly changed to return parameters for particular table. If input parameter isn't set, save_content() will scan first table. Anyway, I made 2x2 table with 4 DIV elements and set "switch content". All four DIVs are shown in popup regardless how I switch them in FF3, IE8 and Google Chrome. There is not any difference for switching first element or other elements in table. I assume that the problem is solved with cutting out table from the combined parameter. If problem still exists, please show me and I will try to fix it.

Leave a Reply